Merge "Check for overflows when parsing PSSH"
diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp
index 66d6913..4217bc6 100644
--- a/camera/camera2/CaptureRequest.cpp
+++ b/camera/camera2/CaptureRequest.cpp
@@ -81,6 +81,13 @@
mSurfaceList.push_back(surface);
}
+ int isReprocess = 0;
+ if ((err = parcel->readInt32(&isReprocess)) != OK) {
+ ALOGE("%s: Failed to read reprocessing from parcel", __FUNCTION__);
+ return err;
+ }
+ mIsReprocess = (isReprocess != 0);
+
return OK;
}
@@ -118,6 +125,8 @@
parcel->writeStrongBinder(binder);
}
+ parcel->writeInt32(mIsReprocess ? 1 : 0);
+
return OK;
}
diff --git a/camera/camera2/ICameraDeviceCallbacks.cpp b/camera/camera2/ICameraDeviceCallbacks.cpp
index 4cc7b5d..f599879 100644
--- a/camera/camera2/ICameraDeviceCallbacks.cpp
+++ b/camera/camera2/ICameraDeviceCallbacks.cpp
@@ -37,6 +37,7 @@
CAMERA_IDLE,
CAPTURE_STARTED,
RESULT_RECEIVED,
+ PREPARED
};
class BpCameraDeviceCallbacks: public BpInterface<ICameraDeviceCallbacks>
@@ -80,7 +81,6 @@
data.writeNoException();
}
-
void onResultReceived(const CameraMetadata& metadata,
const CaptureResultExtras& resultExtras) {
ALOGV("onResultReceived");
@@ -93,6 +93,17 @@
remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
data.writeNoException();
}
+
+ void onPrepared(int streamId)
+ {
+ ALOGV("onPrepared");
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
+ data.writeInt32(streamId);
+ remote()->transact(PREPARED, data, &reply, IBinder::FLAG_ONEWAY);
+ data.writeNoException();
+ }
+
};
IMPLEMENT_META_INTERFACE(CameraDeviceCallbacks,
@@ -160,6 +171,15 @@
data.readExceptionCode();
return NO_ERROR;
} break;
+ case PREPARED: {
+ ALOGV("onPrepared");
+ CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
+ CaptureResultExtras result;
+ int streamId = data.readInt32();
+ onPrepared(streamId);
+ data.readExceptionCode();
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index 89c6fb7..9700258 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -42,10 +42,13 @@
END_CONFIGURE,
DELETE_STREAM,
CREATE_STREAM,
+ CREATE_INPUT_STREAM,
+ GET_INPUT_SURFACE,
CREATE_DEFAULT_REQUEST,
GET_CAMERA_INFO,
WAIT_UNTIL_IDLE,
- FLUSH
+ FLUSH,
+ PREPARE
};
namespace {
@@ -225,6 +228,50 @@
return reply.readInt32();
}
+ virtual status_t createInputStream(int width, int height, int format)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+ data.writeInt32(width);
+ data.writeInt32(height);
+ data.writeInt32(format);
+
+ remote()->transact(CREATE_INPUT_STREAM, data, &reply);
+
+ reply.readExceptionCode();
+ return reply.readInt32();
+ }
+
+ // get the buffer producer of the input stream
+ virtual status_t getInputBufferProducer(
+ sp<IGraphicBufferProducer> *producer) {
+ if (producer == NULL) {
+ return BAD_VALUE;
+ }
+
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+
+ remote()->transact(GET_INPUT_SURFACE, data, &reply);
+
+ reply.readExceptionCode();
+ status_t result = reply.readInt32() ;
+ if (result != OK) {
+ return result;
+ }
+
+ sp<IGraphicBufferProducer> bp = NULL;
+ if (reply.readInt32() != 0) {
+ String16 name = readMaybeEmptyString16(reply);
+ bp = interface_cast<IGraphicBufferProducer>(
+ reply.readStrongBinder());
+ }
+
+ *producer = bp;
+
+ return *producer == NULL ? INVALID_OPERATION : OK;
+ }
+
// Create a request object from a template.
virtual status_t createDefaultRequest(int templateId,
/*out*/
@@ -302,6 +349,20 @@
return res;
}
+ virtual status_t prepare(int streamId)
+ {
+ ALOGV("prepare");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+ data.writeInt32(streamId);
+
+ remote()->transact(PREPARE, data, &reply);
+
+ reply.readExceptionCode();
+ return reply.readInt32();
+ }
+
private:
@@ -409,7 +470,35 @@
return NO_ERROR;
} break;
+ case CREATE_INPUT_STREAM: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+ int width, height, format;
+ width = data.readInt32();
+ height = data.readInt32();
+ format = data.readInt32();
+ status_t ret = createInputStream(width, height, format);
+
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+
+ } break;
+ case GET_INPUT_SURFACE: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+
+ sp<IGraphicBufferProducer> bp;
+ status_t ret = getInputBufferProducer(&bp);
+ sp<IBinder> b(IInterface::asBinder(ret == OK ? bp : NULL));
+
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ reply->writeInt32(1);
+ reply->writeString16(String16("camera input")); // name of surface
+ reply->writeStrongBinder(b);
+
+ return NO_ERROR;
+ } break;
case CREATE_DEFAULT_REQUEST: {
CHECK_INTERFACE(ICameraDeviceUser, data, reply);
@@ -471,6 +560,14 @@
reply->writeInt32(endConfigure());
return NO_ERROR;
} break;
+ case PREPARE: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+ int streamId = data.readInt32();
+ reply->writeNoException();
+ reply->writeInt32(prepare(streamId));
+ return NO_ERROR;
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 0e3bc68..20c0094 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -17,7 +17,8 @@
$(TOP)/frameworks/native/include/media/openmax \
external/jpeg \
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -40,7 +41,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -63,7 +65,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -87,7 +90,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -110,7 +114,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -133,7 +138,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -157,7 +163,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -199,7 +206,8 @@
LOCAL_STATIC_LIBRARIES:= \
libstagefright_mediafilter
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
@@ -222,7 +230,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
index ac1a547..50913cd 100644
--- a/cmds/stagefright/SimplePlayer.cpp
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -21,6 +21,7 @@
#include "SimplePlayer.h"
#include <gui/Surface.h>
+
#include <media/AudioTrack.h>
#include <media/ICrypto.h>
#include <media/IMediaHTTPService.h>
@@ -29,7 +30,6 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/NativeWindowWrapper.h>
#include <media/stagefright/NuMediaExtractor.h>
namespace android {
@@ -73,8 +73,7 @@
surface = new Surface(bufferProducer);
}
- msg->setObject(
- "native-window", new NativeWindowWrapper(surface));
+ msg->setObject("surface", surface);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
@@ -132,10 +131,8 @@
err = INVALID_OPERATION;
} else {
sp<RefBase> obj;
- CHECK(msg->findObject("native-window", &obj));
-
- mNativeWindow = static_cast<NativeWindowWrapper *>(obj.get());
-
+ CHECK(msg->findObject("surface", &obj));
+ mSurface = static_cast<Surface *>(obj.get());
err = OK;
}
@@ -324,7 +321,7 @@
err = state->mCodec->configure(
format,
- isVideo ? mNativeWindow->getSurfaceTextureClient() : NULL,
+ isVideo ? mSurface : NULL,
NULL /* crypto */,
0 /* flags */);
@@ -411,7 +408,7 @@
mStateByTrackIndex.clear();
mCodecLooper.clear();
mExtractor.clear();
- mNativeWindow.clear();
+ mSurface.clear();
mPath.clear();
return OK;
@@ -428,12 +425,12 @@
err = state->mCodec->dequeueInputBuffer(&index);
if (err == OK) {
- ALOGV("dequeued input buffer on track %d",
+ ALOGV("dequeued input buffer on track %zu",
mStateByTrackIndex.keyAt(i));
state->mAvailInputBufferIndices.push_back(index);
} else {
- ALOGV("dequeueInputBuffer on track %d returned %d",
+ ALOGV("dequeueInputBuffer on track %zu returned %d",
mStateByTrackIndex.keyAt(i), err);
}
} while (err == OK);
@@ -448,7 +445,7 @@
&info.mFlags);
if (err == OK) {
- ALOGV("dequeued output buffer on track %d",
+ ALOGV("dequeued output buffer on track %zu",
mStateByTrackIndex.keyAt(i));
state->mAvailOutputBufferInfos.push_back(info);
@@ -459,7 +456,7 @@
err = state->mCodec->getOutputBuffers(&state->mBuffers[1]);
CHECK_EQ(err, (status_t)OK);
} else {
- ALOGV("dequeueOutputBuffer on track %d returned %d",
+ ALOGV("dequeueOutputBuffer on track %zu returned %d",
mStateByTrackIndex.keyAt(i), err);
}
} while (err == OK
@@ -502,7 +499,7 @@
0);
CHECK_EQ(err, (status_t)OK);
- ALOGV("enqueued input data on track %d", trackIndex);
+ ALOGV("enqueued input data on track %zu", trackIndex);
err = mExtractor->advance();
CHECK_EQ(err, (status_t)OK);
@@ -528,8 +525,8 @@
bool release = true;
if (lateByUs > 30000ll) {
- ALOGI("track %d buffer late by %lld us, dropping.",
- mStateByTrackIndex.keyAt(i), lateByUs);
+ ALOGI("track %zu buffer late by %lld us, dropping.",
+ mStateByTrackIndex.keyAt(i), (long long)lateByUs);
state->mCodec->releaseOutputBuffer(info->mIndex);
} else {
if (state->mAudioTrack != NULL) {
@@ -558,8 +555,8 @@
break;
}
} else {
- ALOGV("track %d buffer early by %lld us.",
- mStateByTrackIndex.keyAt(i), -lateByUs);
+ ALOGV("track %zu buffer early by %lld us.",
+ mStateByTrackIndex.keyAt(i), (long long)-lateByUs);
break;
}
}
@@ -569,7 +566,7 @@
}
status_t SimplePlayer::onOutputFormatChanged(
- size_t trackIndex, CodecState *state) {
+ size_t trackIndex __unused, CodecState *state) {
sp<AMessage> format;
status_t err = state->mCodec->getOutputFormat(&format);
@@ -640,7 +637,7 @@
if (delayUs > 2000ll) {
ALOGW("AudioTrack::write took %lld us, numFramesAvailableToWrite=%u, "
"numFramesWritten=%u",
- delayUs, numFramesAvailableToWrite, numFramesWritten);
+ (long long)delayUs, numFramesAvailableToWrite, numFramesWritten);
}
info->mOffset += nbytes;
diff --git a/cmds/stagefright/SimplePlayer.h b/cmds/stagefright/SimplePlayer.h
index 0a06059..ae9dfd2 100644
--- a/cmds/stagefright/SimplePlayer.h
+++ b/cmds/stagefright/SimplePlayer.h
@@ -23,10 +23,10 @@
struct ABuffer;
struct ALooper;
struct AudioTrack;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
struct MediaCodec;
-struct NativeWindowWrapper;
struct NuMediaExtractor;
+class Surface;
struct SimplePlayer : public AHandler {
SimplePlayer();
@@ -84,7 +84,7 @@
State mState;
AString mPath;
- sp<NativeWindowWrapper> mNativeWindow;
+ sp<Surface> mSurface;
sp<NuMediaExtractor> mExtractor;
sp<ALooper> mCodecLooper;
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 7b0de24..6e9e6ec 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -18,6 +18,8 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include <utils/String16.h>
+
#include <binder/ProcessState.h>
#include <media/mediarecorder.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -34,7 +36,7 @@
static void usage(const char* name)
{
- fprintf(stderr, "Usage: %s [-d duration] [-m] [-w] [<output-file>]\n", name);
+ fprintf(stderr, "Usage: %s [-d du.ration] [-m] [-w] [<output-file>]\n", name);
fprintf(stderr, "Encodes either a sine wave or microphone input to AMR format\n");
fprintf(stderr, " -d duration in seconds, default 5 seconds\n");
fprintf(stderr, " -m use microphone for input, default sine source\n");
@@ -85,6 +87,7 @@
// talk into the appropriate microphone for the duration
source = new AudioSource(
AUDIO_SOURCE_MIC,
+ String16(),
kSampleRate,
channels);
} else {
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index d987250..dae9bbe 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -108,7 +108,7 @@
continue;
}
- ALOGV("selecting track %d", i);
+ ALOGV("selecting track %zu", i);
err = extractor->selectTrack(i);
CHECK_EQ(err, (status_t)OK);
@@ -151,7 +151,7 @@
CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers));
CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers));
- ALOGV("got %d input and %d output buffers",
+ ALOGV("got %zu input and %zu output buffers",
state->mInBuffers.size(), state->mOutBuffers.size());
}
@@ -172,7 +172,7 @@
err = state->mCodec->dequeueInputBuffer(&index, kTimeout);
if (err == OK) {
- ALOGV("filling input buffer %d", index);
+ ALOGV("filling input buffer %zu", index);
const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
@@ -209,7 +209,7 @@
state->mCodec->dequeueInputBuffer(&index, kTimeout);
if (err == OK) {
- ALOGV("signalling input EOS on track %d", i);
+ ALOGV("signalling input EOS on track %zu", i);
err = state->mCodec->queueInputBuffer(
index,
@@ -258,8 +258,8 @@
kTimeout);
if (err == OK) {
- ALOGV("draining output buffer %d, time = %lld us",
- index, presentationTimeUs);
+ ALOGV("draining output buffer %zu, time = %lld us",
+ index, (long long)presentationTimeUs);
++state->mNumBuffersDecoded;
state->mNumBytesDecoded += size;
@@ -293,7 +293,7 @@
CHECK_EQ((status_t)OK,
state->mCodec->getOutputBuffers(&state->mOutBuffers));
- ALOGV("got %d output buffers", state->mOutBuffers.size());
+ ALOGV("got %zu output buffers", state->mOutBuffers.size());
} else if (err == INFO_FORMAT_CHANGED) {
sp<AMessage> format;
CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format));
@@ -313,17 +313,17 @@
CHECK_EQ((status_t)OK, state->mCodec->release());
if (state->mIsAudio) {
- printf("track %zu: %" PRId64 " bytes received. %.2f KB/sec\n",
+ printf("track %zu: %lld bytes received. %.2f KB/sec\n",
i,
- state->mNumBytesDecoded,
+ (long long)state->mNumBytesDecoded,
state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
} else {
- printf("track %zu: %" PRId64 " frames decoded, %.2f fps. %" PRId64
+ printf("track %zu: %lld frames decoded, %.2f fps. %lld"
" bytes received. %.2f KB/sec\n",
i,
- state->mNumBuffersDecoded,
+ (long long)state->mNumBuffersDecoded,
state->mNumBuffersDecoded * 1E6 / elapsedTimeUs,
- state->mNumBytesDecoded,
+ (long long)state->mNumBytesDecoded,
state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
}
}
@@ -418,7 +418,7 @@
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
- ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);
+ ALOGV("display is %zd x %zd\n", displayWidth, displayHeight);
control = composerClient->createSurface(
String8("A Surface"),
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index f77b38b..1183112 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -81,7 +81,7 @@
return OK;
}
- status_t handleSetParameters(const sp<AMessage> &msg) {
+ status_t handleSetParameters(const sp<AMessage> &msg __unused) {
return OK;
}
@@ -101,7 +101,7 @@
return OK;
}
- status_t handleSetParameters(const sp<AMessage> &msg) {
+ status_t handleSetParameters(const sp<AMessage> &msg __unused) {
return OK;
}
@@ -121,7 +121,7 @@
return OK;
}
- status_t handleSetParameters(const sp<AMessage> &msg) {
+ status_t handleSetParameters(const sp<AMessage> &msg __unused) {
return OK;
}
@@ -597,7 +597,7 @@
if (err == OK) {
ALOGV("draining decoded buffer %zu, time = %lld us",
- frame.index, frame.presentationTimeUs);
+ frame.index, (long long)frame.presentationTimeUs);
++(state->mNumBuffersDecoded);
diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp
index 461b56c..36fa3b5 100644
--- a/cmds/stagefright/muxer.cpp
+++ b/cmds/stagefright/muxer.cpp
@@ -53,7 +53,6 @@
using namespace android;
static int muxing(
- const android::sp<android::ALooper> &looper,
const char *path,
bool useAudio,
bool useVideo,
@@ -137,14 +136,19 @@
}
}
- ALOGV("selecting track %d", i);
+ ALOGV("selecting track %zu", i);
err = extractor->selectTrack(i);
CHECK_EQ(err, (status_t)OK);
ssize_t newTrackIndex = muxer->addTrack(format);
- CHECK_GE(newTrackIndex, 0);
- trackIndexMap.add(i, newTrackIndex);
+ if (newTrackIndex < 0) {
+ fprintf(stderr, "%s track (%zu) unsupported by muxer\n",
+ isAudio ? "audio" : "video",
+ i);
+ } else {
+ trackIndexMap.add(i, newTrackIndex);
+ }
}
int64_t muxerStartTimeUs = ALooper::GetNowUs();
@@ -163,7 +167,12 @@
ALOGV("saw input eos, err %d", err);
sawInputEOS = true;
break;
+ } else if (trackIndexMap.indexOfKey(trackIndex) < 0) {
+ // ALOGV("skipping input from unsupported track %zu", trackIndex);
+ extractor->advance();
+ continue;
} else {
+ // ALOGV("reading sample from track index %zu\n", trackIndex);
err = extractor->readSampleData(newBuffer);
CHECK_EQ(err, (status_t)OK);
@@ -308,7 +317,7 @@
sp<ALooper> looper = new ALooper;
looper->start();
- int result = muxing(looper, argv[0], useAudio, useVideo, outputFileName,
+ int result = muxing(argv[0], useAudio, useVideo, outputFileName,
enableTrim, trimStartTimeMs, trimEndTimeMs, rotationDegrees);
looper->stop();
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index fdc352e..594c933 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -32,13 +32,13 @@
using namespace android;
+static const int32_t kAudioBitRate = 12200;
+#if 0
static const int32_t kFramerate = 24; // fps
static const int32_t kIFramesIntervalSec = 1;
static const int32_t kVideoBitRate = 512 * 1024;
-static const int32_t kAudioBitRate = 12200;
static const int64_t kDurationUs = 10000000LL; // 10 seconds
-#if 0
class DummySource : public MediaSource {
public:
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 172dc36..0d64d2f 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -38,10 +38,10 @@
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/NativeWindowWrapper.h>
#include <media/stagefright/Utils.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/Surface.h>
#include "include/ESDS.h"
@@ -154,8 +154,7 @@
sp<AMessage> format = makeFormat(mSource->getFormat());
if (mSurface != NULL) {
- format->setObject(
- "native-window", new NativeWindowWrapper(mSurface));
+ format->setObject("surface", mSurface);
}
mCodec->initiateSetup(format);
@@ -328,14 +327,14 @@
CHECK(size >= 7);
CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
- uint8_t profile = ptr[1];
- uint8_t level = ptr[3];
+ uint8_t profile __unused = ptr[1];
+ uint8_t level __unused = ptr[3];
// There is decodable content out there that fails the following
// assertion, let's be lenient for now...
// CHECK((ptr[4] >> 2) == 0x3f); // reserved
- size_t lengthSize = 1 + (ptr[4] & 3);
+ size_t lengthSize __unused = 1 + (ptr[4] & 3);
// commented out check below as H264_QVGA_500_NO_AUDIO.3gp
// violates it...
@@ -491,7 +490,7 @@
if (sizeNeeded > sizeLeft) {
if (outBuffer->size() == 0) {
- ALOGE("Unable to fit even a single input buffer of size %d.",
+ ALOGE("Unable to fit even a single input buffer of size %zu.",
sizeNeeded);
}
CHECK_GT(outBuffer->size(), 0u);
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 318b56d..a9c6eda 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -965,7 +965,7 @@
OMXClient client;
status_t err = client.connect();
- for (int k = 0; k < argc; ++k) {
+ for (int k = 0; k < argc && err == OK; ++k) {
bool syncInfoPresent = true;
const char *filename = argv[k];
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 0566d14..1a40e53 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -269,7 +269,7 @@
: mEOS(false) {
}
- virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
+ virtual void notify(int msg, int ext1 __unused, int ext2 __unused, const Parcel *obj __unused) {
Mutex::Autolock autoLock(mLock);
if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) {
@@ -318,7 +318,7 @@
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
- ALOGV("display is %d x %d\n", displayWidth, displayHeight);
+ ALOGV("display is %zd x %zd\n", displayWidth, displayHeight);
sp<SurfaceControl> control =
composerClient->createSurface(
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index 3f62ed7..b90da1b 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -34,6 +34,7 @@
#include "IDrmManagerService.h"
#define INVALID_BUFFER_LENGTH -1
+#define MAX_BINDER_TRANSACTION_SIZE ((1*1024*1024)-(4096*2))
using namespace android;
@@ -933,7 +934,12 @@
//Filling DRM info
const int infoType = data.readInt32();
- const int bufferSize = data.readInt32();
+ const uint32_t bufferSize = data.readInt32();
+
+ if (bufferSize > data.dataAvail()) {
+ return BAD_VALUE;
+ }
+
char* buffer = NULL;
if (0 < bufferSize) {
buffer = (char *)data.readInplace(bufferSize);
@@ -986,6 +992,9 @@
const int size = data.readInt32();
for (int index = 0; index < size; ++index) {
+ if (!data.dataAvail()) {
+ break;
+ }
const String8 key(data.readString8());
if (key == String8("FileDescriptorKey")) {
char buffer[16];
@@ -1035,7 +1044,12 @@
const int uniqueId = data.readInt32();
//Filling DRM Rights
- const int bufferSize = data.readInt32();
+ const uint32_t bufferSize = data.readInt32();
+ if (bufferSize > data.dataAvail()) {
+ reply->writeInt32(BAD_VALUE);
+ return DRM_NO_ERROR;
+ }
+
const DrmBuffer drmBuffer((char *)data.readInplace(bufferSize), bufferSize);
const String8 mimeType(data.readString8());
@@ -1206,10 +1220,13 @@
const int convertId = data.readInt32();
//Filling input data
- const int bufferSize = data.readInt32();
+ const uint32_t bufferSize = data.readInt32();
+ if (bufferSize > data.dataAvail()) {
+ return BAD_VALUE;
+ }
DrmBuffer* inputData = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
- DrmConvertedStatus* drmConvertedStatus = convertData(uniqueId, convertId, inputData);
+ DrmConvertedStatus* drmConvertedStatus = convertData(uniqueId, convertId, inputData);
if (NULL != drmConvertedStatus) {
//Filling Drm Converted Ststus
@@ -1393,7 +1410,12 @@
const int decryptUnitId = data.readInt32();
//Filling Header info
- const int bufferSize = data.readInt32();
+ const uint32_t bufferSize = data.readInt32();
+ if (bufferSize > data.dataAvail()) {
+ reply->writeInt32(BAD_VALUE);
+ clearDecryptHandle(&handle);
+ return DRM_NO_ERROR;
+ }
DrmBuffer* headerInfo = NULL;
headerInfo = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
@@ -1417,9 +1439,17 @@
readDecryptHandleFromParcelData(&handle, data);
const int decryptUnitId = data.readInt32();
- const int decBufferSize = data.readInt32();
+ const uint32_t decBufferSize = data.readInt32();
+ const uint32_t encBufferSize = data.readInt32();
- const int encBufferSize = data.readInt32();
+ if (encBufferSize > data.dataAvail() ||
+ decBufferSize > MAX_BINDER_TRANSACTION_SIZE) {
+ reply->writeInt32(BAD_VALUE);
+ reply->writeInt32(0);
+ clearDecryptHandle(&handle);
+ return DRM_NO_ERROR;
+ }
+
DrmBuffer* encBuffer
= new DrmBuffer((char *)data.readInplace(encBufferSize), encBufferSize);
@@ -1429,8 +1459,10 @@
DrmBuffer* IV = NULL;
if (0 != data.dataAvail()) {
- const int ivBufferlength = data.readInt32();
- IV = new DrmBuffer((char *)data.readInplace(ivBufferlength), ivBufferlength);
+ const uint32_t ivBufferlength = data.readInt32();
+ if (ivBufferlength <= data.dataAvail()) {
+ IV = new DrmBuffer((char *)data.readInplace(ivBufferlength), ivBufferlength);
+ }
}
const status_t status
@@ -1477,7 +1509,11 @@
DecryptHandle handle;
readDecryptHandleFromParcelData(&handle, data);
- const int numBytes = data.readInt32();
+ const uint32_t numBytes = data.readInt32();
+ if (numBytes > MAX_BINDER_TRANSACTION_SIZE) {
+ reply->writeInt32(BAD_VALUE);
+ return DRM_NO_ERROR;
+ }
char* buffer = new char[numBytes];
const off64_t offset = data.readInt64();
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
index 9d15835..6a0b3c0 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -19,6 +19,7 @@
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 9b786c5..851ad2c 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -56,7 +56,7 @@
return true;
}
- status_t MockDrmFactory::createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin)
+ status_t MockDrmFactory::createDrmPlugin(const uint8_t /* uuid */[16], DrmPlugin **plugin)
{
*plugin = new MockDrmPlugin();
return OK;
@@ -68,8 +68,9 @@
return (!memcmp(uuid, mock_uuid, sizeof(mock_uuid)));
}
- status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
- size_t size, CryptoPlugin **plugin)
+ status_t MockCryptoFactory::createPlugin(const uint8_t /* uuid */[16],
+ const void * /* data */,
+ size_t /* size */, CryptoPlugin **plugin)
{
*plugin = new MockCryptoPlugin();
return OK;
@@ -150,7 +151,7 @@
// Properties used in mock test, set by cts test app returned from mock plugin
// byte[] mock-request -> request
// string mock-default-url -> defaultUrl
- // string mock-key-request-type -> keyRequestType
+ // string mock-keyRequestType -> keyRequestType
index = mByteArrayProperties.indexOfKey(String8("mock-request"));
if (index < 0) {
@@ -266,8 +267,8 @@
return OK;
}
- status_t MockDrmPlugin::getProvisionRequest(String8 const &certType,
- String8 const &certAuthority,
+ status_t MockDrmPlugin::getProvisionRequest(String8 const & /* certType */,
+ String8 const & /* certAuthority */,
Vector<uint8_t> &request,
String8 &defaultUrl)
{
@@ -297,8 +298,8 @@
}
status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response,
- Vector<uint8_t> &certificate,
- Vector<uint8_t> &wrappedKey)
+ Vector<uint8_t> & /* certificate */,
+ Vector<uint8_t> & /* wrappedKey */)
{
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
@@ -317,7 +318,8 @@
return OK;
}
- status_t MockDrmPlugin::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop)
+ status_t MockDrmPlugin::getSecureStop(Vector<uint8_t> const & /* ssid */,
+ Vector<uint8_t> & secureStop)
{
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::getSecureStop()");
@@ -439,6 +441,63 @@
pData ? vectorToString(*pData) : "{}");
sendEvent(eventType, extra, pSessionId, pData);
+ } else if (name == "mock-send-expiration-update") {
+ int64_t expiryTimeMS;
+ sscanf(value.string(), "%jd", &expiryTimeMS);
+
+ Vector<uint8_t> const *pSessionId = NULL;
+ ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+ if (index >= 0) {
+ pSessionId = &mByteArrayProperties[index];
+ }
+
+ ALOGD("sending expiration-update from mock drm plugin: %jd %s",
+ expiryTimeMS, pSessionId ? vectorToString(*pSessionId) : "{}");
+
+ sendExpirationUpdate(pSessionId, expiryTimeMS);
+ } else if (name == "mock-send-keys-change") {
+ Vector<uint8_t> const *pSessionId = NULL;
+ ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+ if (index >= 0) {
+ pSessionId = &mByteArrayProperties[index];
+ }
+
+ ALOGD("sending keys-change from mock drm plugin: %s",
+ pSessionId ? vectorToString(*pSessionId) : "{}");
+
+ Vector<DrmPlugin::KeyStatus> keyStatusList;
+ DrmPlugin::KeyStatus keyStatus;
+ uint8_t keyId1[] = {'k', 'e', 'y', '1'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId1, sizeof(keyId1));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_Usable;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId2[] = {'k', 'e', 'y', '2'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId2, sizeof(keyId2));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_Expired;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId3[] = {'k', 'e', 'y', '3'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId3, sizeof(keyId3));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_OutputNotAllowed;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId4[] = {'k', 'e', 'y', '4'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId4, sizeof(keyId4));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_StatusPending;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId5[] = {'k', 'e', 'y', '5'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId5, sizeof(keyId5));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_InternalError;
+ keyStatusList.add(keyStatus);
+
+ sendKeysChange(pSessionId, &keyStatusList, true);
} else {
mStringProperties.add(name, value);
}
@@ -740,7 +799,7 @@
ssize_t
MockCryptoPlugin::decrypt(bool secure, const uint8_t key[16], const uint8_t iv[16],
Mode mode, const void *srcPtr, const SubSample *subSamples,
- size_t numSubSamples, void *dstPtr, AString *errorDetailMsg)
+ size_t numSubSamples, void *dstPtr, AString * /* errorDetailMsg */)
{
ALOGD("MockCryptoPlugin::decrypt(secure=%d, key=%s, iv=%s, mode=%d, src=%p, "
"subSamples=%s, dst=%p)",
@@ -769,7 +828,7 @@
{
String8 result;
for (size_t i = 0; i < numSubSamples; i++) {
- result.appendFormat("[%zu] {clear:%zu, encrypted:%zu} ", i,
+ result.appendFormat("[%zu] {clear:%u, encrypted:%u} ", i,
subSamples[i].mNumBytesOfClearData,
subSamples[i].mNumBytesOfEncryptedData);
}
diff --git a/include/camera/camera2/CaptureRequest.h b/include/camera/camera2/CaptureRequest.h
index e56d61f..eeab217 100644
--- a/include/camera/camera2/CaptureRequest.h
+++ b/include/camera/camera2/CaptureRequest.h
@@ -30,6 +30,7 @@
CameraMetadata mMetadata;
Vector<sp<Surface> > mSurfaceList;
+ bool mIsReprocess;
/**
* Keep impl up-to-date with CaptureRequest.java in frameworks/base
diff --git a/include/camera/camera2/ICameraDeviceCallbacks.h b/include/camera/camera2/ICameraDeviceCallbacks.h
index 670480b..c57b39f 100644
--- a/include/camera/camera2/ICameraDeviceCallbacks.h
+++ b/include/camera/camera2/ICameraDeviceCallbacks.h
@@ -65,6 +65,9 @@
// One way
virtual void onResultReceived(const CameraMetadata& metadata,
const CaptureResultExtras& resultExtras) = 0;
+
+ // One way
+ virtual void onPrepared(int streamId) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index e9f1f5a..619b161 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -103,6 +103,19 @@
virtual status_t createStream(const OutputConfiguration& outputConfiguration) = 0;
+ /**
+ * Create an input stream of width, height, and format (one of
+ * HAL_PIXEL_FORMAT_*)
+ *
+ * Return stream ID if it's a non-negative value. status_t if it's a
+ * negative value.
+ */
+ virtual status_t createInputStream(int width, int height, int format) = 0;
+
+ // get the buffer producer of the input stream
+ virtual status_t getInputBufferProducer(
+ sp<IGraphicBufferProducer> *producer) = 0;
+
// Create a request object from a template.
virtual status_t createDefaultRequest(int templateId,
/*out*/
@@ -120,6 +133,11 @@
*/
virtual status_t flush(/*out*/
int64_t* lastFrameNumber = NULL) = 0;
+
+ /**
+ * Preallocate buffers for a given output stream asynchronously.
+ */
+ virtual status_t prepare(int streamId) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/AVSyncSettings.h b/include/media/AVSyncSettings.h
new file mode 100644
index 0000000..10e3bcc
--- /dev/null
+++ b/include/media/AVSyncSettings.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AV_SYNC_SETTINGS_H
+#define ANDROID_AV_SYNC_SETTINGS_H
+
+namespace android {
+
+enum AVSyncSource : unsigned {
+ // let the system decide the best sync source
+ AVSYNC_SOURCE_DEFAULT = 0,
+ // sync to the system clock
+ AVSYNC_SOURCE_SYSTEM_CLOCK = 1,
+ // sync to the audio track
+ AVSYNC_SOURCE_AUDIO = 2,
+ // sync to the display vsync
+ AVSYNC_SOURCE_VSYNC = 3,
+ AVSYNC_SOURCE_MAX,
+};
+
+enum AVSyncAudioAdjustMode : unsigned {
+ // let the system decide the best audio adjust mode
+ AVSYNC_AUDIO_ADJUST_MODE_DEFAULT = 0,
+ // adjust audio by time stretching
+ AVSYNC_AUDIO_ADJUST_MODE_STRETCH = 1,
+ // adjust audio by resampling
+ AVSYNC_AUDIO_ADJUST_MODE_RESAMPLE = 2,
+ AVSYNC_AUDIO_ADJUST_MODE_MAX,
+};
+
+// max tolerance when adjusting playback speed to desired playback speed
+#define AVSYNC_TOLERANCE_MAX 1.0f
+
+struct AVSyncSettings {
+ AVSyncSource mSource;
+ AVSyncAudioAdjustMode mAudioAdjustMode;
+ float mTolerance;
+ AVSyncSettings()
+ : mSource(AVSYNC_SOURCE_DEFAULT),
+ mAudioAdjustMode(AVSYNC_AUDIO_ADJUST_MODE_DEFAULT),
+ mTolerance(.044f) { }
+};
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_AV_SYNC_SETTINGS_H
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 583695d..61da4f2 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -201,8 +201,12 @@
*/
/* Simple Constructor.
+ *
+ * Parameters:
+ *
+ * opPackageName: The package name used for app op checks.
*/
- AudioEffect();
+ AudioEffect(const String16& opPackageName);
/* Constructor.
@@ -211,6 +215,7 @@
*
* type: type of effect created: can be null if uuid is specified. This corresponds to
* the OpenSL ES interface implemented by this effect.
+ * opPackageName: The package name used for app op checks.
* uuid: Uuid of effect created: can be null if type is specified. This uuid corresponds to
* a particular implementation of an effect type.
* priority: requested priority for effect control: the priority level corresponds to the
@@ -227,6 +232,7 @@
*/
AudioEffect(const effect_uuid_t *type,
+ const String16& opPackageName,
const effect_uuid_t *uuid = NULL,
int32_t priority = 0,
effect_callback_t cbf = NULL,
@@ -239,6 +245,7 @@
* Same as above but with type and uuid specified by character strings
*/
AudioEffect(const char *typeStr,
+ const String16& opPackageName,
const char *uuidStr = NULL,
int32_t priority = 0,
effect_callback_t cbf = NULL,
@@ -406,7 +413,9 @@
void* mUserData; // client context for callback function
effect_descriptor_t mDescriptor; // effect descriptor
int32_t mId; // system wide unique effect engine instance ID
- Mutex mLock; // Mutex for mEnabled access
+ Mutex mLock; // Mutex for mEnabled access
+
+ String16 mOpPackageName; // The package name used for app op checks.
// IEffectClient
virtual void controlStatusChanged(bool controlGranted);
diff --git a/include/media/AudioIoDescriptor.h b/include/media/AudioIoDescriptor.h
new file mode 100644
index 0000000..c94b738
--- /dev/null
+++ b/include/media/AudioIoDescriptor.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_IO_DESCRIPTOR_H
+#define ANDROID_AUDIO_IO_DESCRIPTOR_H
+
+namespace android {
+
+enum audio_io_config_event {
+ AUDIO_OUTPUT_OPENED,
+ AUDIO_OUTPUT_CLOSED,
+ AUDIO_OUTPUT_CONFIG_CHANGED,
+ AUDIO_INPUT_OPENED,
+ AUDIO_INPUT_CLOSED,
+ AUDIO_INPUT_CONFIG_CHANGED,
+};
+
+// audio input/output descriptor used to cache output configurations in client process to avoid
+// frequent calls through IAudioFlinger
+class AudioIoDescriptor : public RefBase {
+public:
+ AudioIoDescriptor() :
+ mIoHandle(AUDIO_IO_HANDLE_NONE),
+ mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(AUDIO_CHANNEL_NONE),
+ mFrameCount(0), mLatency(0)
+ {
+ memset(&mPatch, 0, sizeof(struct audio_patch));
+ }
+
+ virtual ~AudioIoDescriptor() {}
+
+ audio_port_handle_t getDeviceId() {
+ if (mPatch.num_sources != 0 && mPatch.num_sinks != 0) {
+ if (mPatch.sources[0].type == AUDIO_PORT_TYPE_MIX) {
+ // this is an output mix
+ // FIXME: the API only returns the first device in case of multiple device selection
+ return mPatch.sinks[0].id;
+ } else {
+ // this is an input mix
+ return mPatch.sources[0].id;
+ }
+ }
+ return AUDIO_PORT_HANDLE_NONE;
+ }
+
+ audio_io_handle_t mIoHandle;
+ struct audio_patch mPatch;
+ uint32_t mSamplingRate;
+ audio_format_t mFormat;
+ audio_channel_mask_t mChannelMask;
+ size_t mFrameCount;
+ uint32_t mLatency;
+};
+
+
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_IO_DESCRIPTOR_H*/
diff --git a/include/media/AudioPolicy.h b/include/media/AudioPolicy.h
index a755e1e..feed402 100644
--- a/include/media/AudioPolicy.h
+++ b/include/media/AudioPolicy.h
@@ -38,8 +38,17 @@
#define MIX_TYPE_PLAYERS 0
#define MIX_TYPE_RECORDERS 1
-#define ROUTE_FLAG_RENDER 0x1
-#define ROUTE_FLAG_LOOP_BACK (0x1 << 1)
+// definition of the different events that can be reported on a dynamic policy from
+// AudioSystem's implementation of the AudioPolicyClient interface
+// keep in sync with AudioSystem.java
+#define DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE 0
+
+#define MIX_STATE_DISABLED -1
+#define MIX_STATE_IDLE 0
+#define MIX_STATE_MIXING 1
+
+#define MIX_ROUTE_FLAG_RENDER 0x1
+#define MIX_ROUTE_FLAG_LOOP_BACK (0x1 << 1)
#define MAX_MIXES_PER_POLICY 10
#define MAX_CRITERIA_PER_MIX 20
@@ -61,11 +70,15 @@
class AudioMix {
public:
+ // flag on an AudioMix indicating the activity on this mix (IDLE, MIXING)
+ // must be reported through the AudioPolicyClient interface
+ static const uint32_t kCbFlagNotifyActivity = 0x1;
+
AudioMix() {}
AudioMix(Vector<AttributeMatchCriterion> criteria, uint32_t mixType, audio_config_t format,
- uint32_t routeFlags, String8 registrationId) :
+ uint32_t routeFlags, String8 registrationId, uint32_t flags) :
mCriteria(criteria), mMixType(mixType), mFormat(format),
- mRouteFlags(routeFlags), mRegistrationId(registrationId) {}
+ mRouteFlags(routeFlags), mRegistrationId(registrationId), mCbFlags(flags){}
status_t readFromParcel(Parcel *parcel);
status_t writeToParcel(Parcel *parcel) const;
@@ -75,6 +88,7 @@
audio_config_t mFormat;
uint32_t mRouteFlags;
String8 mRegistrationId;
+ uint32_t mCbFlags; // flags indicating which callbacks to use, see kCbFlag*
};
}; // namespace android
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 7be2c3e..c4c7b0e 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -129,8 +129,12 @@
/* Constructs an uninitialized AudioRecord. No connection with
* AudioFlinger takes place. Use set() after this.
+ *
+ * Parameters:
+ *
+ * opPackageName: The package name used for app ops.
*/
- AudioRecord();
+ AudioRecord(const String16& opPackageName);
/* Creates an AudioRecord object and registers it with AudioFlinger.
* Once created, the track needs to be started before it can be used.
@@ -143,6 +147,7 @@
* format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
* 16 bits per sample).
* channelMask: Channel mask, such that audio_is_input_channel(channelMask) is true.
+ * opPackageName: The package name used for app ops.
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the
* latency of the track. The actual size selected by the AudioRecord could
@@ -165,6 +170,7 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ const String16& opPackageName,
size_t frameCount = 0,
callback_t cbf = NULL,
void* user = NULL,
@@ -172,6 +178,8 @@
int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+ int uid = -1,
+ pid_t pid = -1,
const audio_attributes_t* pAttributes = NULL);
/* Terminates the AudioRecord and unregisters it from AudioFlinger.
@@ -208,6 +216,8 @@
int sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+ int uid = -1,
+ pid_t pid = -1,
const audio_attributes_t* pAttributes = NULL);
/* Result of constructing the AudioRecord. This must be checked for successful initialization
@@ -335,6 +345,13 @@
* After draining these frames of data, the caller should release them with releaseBuffer().
* If the track buffer is not empty, obtainBuffer() returns as many contiguous
* full frames as are available immediately.
+ *
+ * If nonContig is non-NULL, it is an output parameter that will be set to the number of
+ * additional non-contiguous frames that are predicted to be available immediately,
+ * if the client were to release the first frames and then call obtainBuffer() again.
+ * This value is only a prediction, and needs to be confirmed.
+ * It will be set to zero for an error return.
+ *
* If the track buffer is empty and track is stopped, obtainBuffer() returns WOULD_BLOCK
* regardless of the value of waitCount.
* If the track buffer is empty and track is not stopped, obtainBuffer() blocks with a
@@ -364,11 +381,58 @@
* raw pointer to the buffer
*/
- status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
+ status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
+ size_t *nonContig = NULL);
+
+ // Explicit Routing
+ /**
+ * TODO Document this method.
+ */
+ status_t setInputDevice(audio_port_handle_t deviceId);
+
+ /**
+ * TODO Document this method.
+ */
+ audio_port_handle_t getInputDevice();
+
+ /* Returns the ID of the audio device actually used by the input to which this AudioRecord
+ * is attached.
+ * A value of AUDIO_PORT_HANDLE_NONE indicates the AudioRecord is not attached to any input.
+ *
+ * Parameters:
+ * none.
+ */
+ audio_port_handle_t getRoutedDeviceId();
+
+ /* Add an AudioDeviceCallback. The caller will be notified when the audio device
+ * to which this AudioRecord is routed is updated.
+ * Replaces any previously installed callback.
+ * Parameters:
+ * callback: The callback interface
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the same callback is already installed.
+ * NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t addAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+ /* remove an AudioDeviceCallback.
+ * Parameters:
+ * callback: The callback interface
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the callback is not installed
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t removeAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback);
private:
/* If nonContig is non-NULL, it is an output parameter that will be set to the number of
- * additional non-contiguous frames that are available immediately.
+ * additional non-contiguous frames that are predicted to be available immediately,
+ * if the client were to release the first frames and then call obtainBuffer() again.
+ * This value is only a prediction, and needs to be confirmed.
+ * It will be set to zero for an error return.
* FIXME We could pass an array of Buffers instead of only one Buffer to obtainBuffer(),
* in case the requested amount of frames is in two or more non-contiguous regions.
* FIXME requested and elapsed are both relative times. Consider changing to absolute time.
@@ -462,7 +526,7 @@
// caller must hold lock on mLock for all _l methods
- status_t openRecord_l(size_t epoch);
+ status_t openRecord_l(size_t epoch, const String16& opPackageName);
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreRecord_l(const char *from);
@@ -499,6 +563,8 @@
status_t mStatus;
+ String16 mOpPackageName; // The package name used for app ops.
+
size_t mFrameCount; // corresponds to current IAudioRecord, value is
// reported back by AudioFlinger to the client
size_t mReqFrameCount; // frame count to request the first or next time
@@ -548,7 +614,14 @@
sp<DeathNotifier> mDeathNotifier;
uint32_t mSequence; // incremented for each new IAudioRecord attempt
+ int mClientUid;
+ pid_t mClientPid;
audio_attributes_t mAttributes;
+
+ // For Device Selection API
+ // a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
+ audio_port_handle_t mSelectedDeviceId;
+ sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
};
}; // namespace android
diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h
index b705efa..6cf2ca9 100644
--- a/include/media/AudioResamplerPublic.h
+++ b/include/media/AudioResamplerPublic.h
@@ -17,6 +17,11 @@
#ifndef ANDROID_AUDIO_RESAMPLER_PUBLIC_H
#define ANDROID_AUDIO_RESAMPLER_PUBLIC_H
+#include <stdint.h>
+#include <math.h>
+
+namespace android {
+
// AUDIO_RESAMPLER_DOWN_RATIO_MAX is the maximum ratio between the original
// audio sample rate and the target rate when downsampling,
// as permitted in the audio framework, e.g. AudioTrack and AudioFlinger.
@@ -26,6 +31,85 @@
// TODO: replace with an API
#define AUDIO_RESAMPLER_DOWN_RATIO_MAX 256
+// AUDIO_RESAMPLER_UP_RATIO_MAX is the maximum suggested ratio between the original
+// audio sample rate and the target rate when upsampling. It is loosely enforced by
+// the system. One issue with large upsampling ratios is the approximation by
+// an int32_t of the phase increments, making the resulting sample rate inexact.
+#define AUDIO_RESAMPLER_UP_RATIO_MAX 65536
+
+// AUDIO_TIMESTRETCH_SPEED_MIN and AUDIO_TIMESTRETCH_SPEED_MAX define the min and max time stretch
+// speeds supported by the system. These are enforced by the system and values outside this range
+// will result in a runtime error.
+// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than
+// the ones specified here
+// AUDIO_TIMESTRETCH_SPEED_MIN_DELTA is the minimum absolute speed difference that might trigger a
+// parameter update
+#define AUDIO_TIMESTRETCH_SPEED_MIN 0.01f
+#define AUDIO_TIMESTRETCH_SPEED_MAX 20.0f
+#define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f
+#define AUDIO_TIMESTRETCH_SPEED_MIN_DELTA 0.0001f
+
+// AUDIO_TIMESTRETCH_PITCH_MIN and AUDIO_TIMESTRETCH_PITCH_MAX define the min and max time stretch
+// pitch shifting supported by the system. These are not enforced by the system and values
+// outside this range might result in a pitch different than the one requested.
+// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than
+// the ones specified here.
+// AUDIO_TIMESTRETCH_PITCH_MIN_DELTA is the minimum absolute pitch difference that might trigger a
+// parameter update
+#define AUDIO_TIMESTRETCH_PITCH_MIN 0.25f
+#define AUDIO_TIMESTRETCH_PITCH_MAX 4.0f
+#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f
+#define AUDIO_TIMESTRETCH_PITCH_MIN_DELTA 0.0001f
+
+
+//Determines the current algorithm used for stretching
+enum AudioTimestretchStretchMode : int32_t {
+ AUDIO_TIMESTRETCH_STRETCH_DEFAULT = 0,
+ AUDIO_TIMESTRETCH_STRETCH_SPEECH = 1,
+ //TODO: add more stretch modes/algorithms
+};
+
+//Limits for AUDIO_TIMESTRETCH_STRETCH_SPEECH mode
+#define TIMESTRETCH_SONIC_SPEED_MIN 0.1f
+#define TIMESTRETCH_SONIC_SPEED_MAX 6.0f
+
+//Determines behavior of Timestretch if current algorithm can't perform
+//with current parameters.
+// FALLBACK_CUT_REPEAT: (internal only) for speed <1.0 will truncate frames
+// for speed > 1.0 will repeat frames
+// FALLBACK_MUTE: will set all processed frames to zero
+// FALLBACK_FAIL: will stop program execution and log a fatal error
+enum AudioTimestretchFallbackMode : int32_t {
+ AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT = -1,
+ AUDIO_TIMESTRETCH_FALLBACK_DEFAULT = 0,
+ AUDIO_TIMESTRETCH_FALLBACK_MUTE = 1,
+ AUDIO_TIMESTRETCH_FALLBACK_FAIL = 2,
+};
+
+struct AudioPlaybackRate {
+ float mSpeed;
+ float mPitch;
+ enum AudioTimestretchStretchMode mStretchMode;
+ enum AudioTimestretchFallbackMode mFallbackMode;
+};
+
+static const AudioPlaybackRate AUDIO_PLAYBACK_RATE_DEFAULT = {
+ AUDIO_TIMESTRETCH_SPEED_NORMAL,
+ AUDIO_TIMESTRETCH_PITCH_NORMAL,
+ AUDIO_TIMESTRETCH_STRETCH_DEFAULT,
+ AUDIO_TIMESTRETCH_FALLBACK_DEFAULT
+};
+
+static inline bool isAudioPlaybackRateEqual(const AudioPlaybackRate &pr1,
+ const AudioPlaybackRate &pr2) {
+ return fabs(pr1.mSpeed - pr2.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
+ fabs(pr1.mPitch - pr2.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA &&
+ pr2.mStretchMode == pr2.mStretchMode &&
+ pr2.mFallbackMode == pr2.mFallbackMode;
+}
+
+// TODO: Consider putting these inlines into a class scope
+
// Returns the source frames needed to resample to destination frames. This is not a precise
// value and depends on the resampler (and possibly how it handles rounding internally).
// Nevertheless, this should be an upper bound on the requirements of the resampler.
@@ -39,4 +123,38 @@
size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1);
}
+// An upper bound for the number of destination frames possible from srcFrames
+// after sample rate conversion. This may be used for buffer sizing.
+static inline size_t destinationFramesPossible(size_t srcFrames, uint32_t srcSampleRate,
+ uint32_t dstSampleRate) {
+ if (srcSampleRate == dstSampleRate) {
+ return srcFrames;
+ }
+ uint64_t dstFrames = (uint64_t)srcFrames * dstSampleRate / srcSampleRate;
+ return dstFrames > 2 ? dstFrames - 2 : 0;
+}
+
+static inline size_t sourceFramesNeededWithTimestretch(
+ uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
+ float speed) {
+ // required is the number of input frames the resampler needs
+ size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
+ // to deliver this, the time stretcher requires:
+ return required * (double)speed + 1 + 1; // accounting for rounding dependencies
+}
+
+// Identifies sample rates that we associate with music
+// and thus eligible for better resampling and fast capture.
+// This is somewhat less than 44100 to allow for pitch correction
+// involving resampling as well as asynchronous resampling.
+#define AUDIO_PROCESSING_MUSIC_RATE 40000
+
+static inline bool isMusicRate(uint32_t sampleRate) {
+ return sampleRate >= AUDIO_PROCESSING_MUSIC_RATE;
+}
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
#endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index f5db1bb..3241e9c 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
#include <hardware/audio_effect.h>
#include <media/AudioPolicy.h>
+#include <media/AudioIoDescriptor.h>
#include <media/IAudioFlingerClient.h>
#include <media/IAudioPolicyServiceClient.h>
#include <system/audio.h>
@@ -29,6 +30,7 @@
namespace android {
typedef void (*audio_error_callback)(status_t err);
+typedef void (*dynamic_policy_callback)(int event, String8 regId, int val);
class IAudioFlinger;
class IAudioPolicyService;
@@ -89,6 +91,7 @@
static String8 getParameters(const String8& keys);
static void setErrorCallback(audio_error_callback cb);
+ static void setDynPolicyCallback(dynamic_policy_callback cb);
// helper function to obtain AudioFlinger service handle
static const sp<IAudioFlinger> get_audio_flinger();
@@ -155,33 +158,6 @@
// or no HW sync source is used.
static audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId);
- // types of io configuration change events received with ioConfigChanged()
- enum io_config_event {
- OUTPUT_OPENED,
- OUTPUT_CLOSED,
- OUTPUT_CONFIG_CHANGED,
- INPUT_OPENED,
- INPUT_CLOSED,
- INPUT_CONFIG_CHANGED,
- STREAM_CONFIG_CHANGED,
- NUM_CONFIG_EVENTS
- };
-
- // audio output descriptor used to cache output configurations in client process to avoid
- // frequent calls through IAudioFlinger
- class OutputDescriptor {
- public:
- OutputDescriptor()
- : samplingRate(0), format(AUDIO_FORMAT_DEFAULT), channelMask(0), frameCount(0), latency(0)
- {}
-
- uint32_t samplingRate;
- audio_format_t format;
- audio_channel_mask_t channelMask;
- size_t frameCount;
- uint32_t latency;
- };
-
// Events used to synchronize actions between audio sessions.
// For instance SYNC_EVENT_PRESENTATION_COMPLETE can be used to delay recording start until
// playback is complete on another audio session.
@@ -221,14 +197,16 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL);
static status_t getOutputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- uint32_t samplingRate = 0,
- audio_format_t format = AUDIO_FORMAT_DEFAULT,
- audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
- audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
- const audio_offload_info_t *offloadInfo = NULL);
+ audio_io_handle_t *output,
+ audio_session_t session,
+ audio_stream_type_t *stream,
+ uid_t uid,
+ uint32_t samplingRate = 0,
+ audio_format_t format = AUDIO_FORMAT_DEFAULT,
+ audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+ const audio_offload_info_t *offloadInfo = NULL);
static status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
audio_session_t session);
@@ -244,10 +222,12 @@
static status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_input_flags_t flags);
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
static status_t startInput(audio_io_handle_t input,
audio_session_t session);
@@ -331,6 +311,12 @@
static status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration);
+ static status_t startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle);
+ static status_t stopAudioSource(audio_io_handle_t handle);
+
+
// ----------------------------------------------------------------------------
class AudioPortCallback : public RefBase
@@ -346,17 +332,42 @@
};
- static status_t addAudioPortCallback(const sp<AudioPortCallback>& callBack);
- static status_t removeAudioPortCallback(const sp<AudioPortCallback>& callBack);
+ static status_t addAudioPortCallback(const sp<AudioPortCallback>& callback);
+ static status_t removeAudioPortCallback(const sp<AudioPortCallback>& callback);
+
+ class AudioDeviceCallback : public RefBase
+ {
+ public:
+
+ AudioDeviceCallback() {}
+ virtual ~AudioDeviceCallback() {}
+
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId) = 0;
+ };
+
+ static status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ audio_io_handle_t audioIo);
+ static status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ audio_io_handle_t audioIo);
+
+ static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
private:
class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
{
public:
- AudioFlingerClient() {
+ AudioFlingerClient() :
+ mInBuffSize(0), mInSamplingRate(0),
+ mInFormat(AUDIO_FORMAT_DEFAULT), mInChannelMask(AUDIO_CHANNEL_NONE) {
}
+ void clearIoCache();
+ status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask, size_t* buffSize);
+ sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);
+
// DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -364,7 +375,27 @@
// indicate a change in the configuration of an output or input: keeps the cached
// values for output/input parameters up-to-date in client process
- virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
+ virtual void ioConfigChanged(audio_io_config_event event,
+ const sp<AudioIoDescriptor>& ioDesc);
+
+
+ status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ audio_io_handle_t audioIo);
+ status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ audio_io_handle_t audioIo);
+
+ audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
+
+ private:
+ Mutex mLock;
+ DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> > mIoDescriptors;
+ DefaultKeyedVector<audio_io_handle_t, Vector < sp<AudioDeviceCallback> > >
+ mAudioDeviceCallbacks;
+ // cached values for recording getInputBufferSize() queries
+ size_t mInBuffSize; // zero indicates cache is invalid
+ uint32_t mInSamplingRate;
+ audio_format_t mInFormat;
+ audio_channel_mask_t mInChannelMask;
};
class AudioPolicyServiceClient: public IBinder::DeathRecipient,
@@ -374,8 +405,8 @@
AudioPolicyServiceClient() {
}
- status_t addAudioPortCallback(const sp<AudioPortCallback>& callBack);
- status_t removeAudioPortCallback(const sp<AudioPortCallback>& callBack);
+ status_t addAudioPortCallback(const sp<AudioPortCallback>& callback);
+ status_t removeAudioPortCallback(const sp<AudioPortCallback>& callback);
// DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -383,23 +414,26 @@
// IAudioPolicyServiceClient
virtual void onAudioPortListUpdate();
virtual void onAudioPatchListUpdate();
+ virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
private:
Mutex mLock;
Vector <sp <AudioPortCallback> > mAudioPortCallbacks;
};
+ static const sp<AudioFlingerClient> getAudioFlingerClient();
+ static sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);
+
static sp<AudioFlingerClient> gAudioFlingerClient;
static sp<AudioPolicyServiceClient> gAudioPolicyServiceClient;
friend class AudioFlingerClient;
friend class AudioPolicyServiceClient;
static Mutex gLock; // protects gAudioFlinger and gAudioErrorCallback,
- static Mutex gLockCache; // protects gOutputs, gPrevInSamplingRate, gPrevInFormat,
- // gPrevInChannelMask and gInBuffSize
static Mutex gLockAPS; // protects gAudioPolicyService and gAudioPolicyServiceClient
static sp<IAudioFlinger> gAudioFlinger;
static audio_error_callback gAudioErrorCallback;
+ static dynamic_policy_callback gDynPolicyCallback;
static size_t gInBuffSize;
// previous parameters for recording buffer size queries
@@ -408,10 +442,6 @@
static audio_channel_mask_t gPrevInChannelMask;
static sp<IAudioPolicyService> gAudioPolicyService;
-
- // list of output descriptors containing cached parameters
- // (sampling rate, framecount, channel count...)
- static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
};
}; // namespace android
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index d9b7057..458f4b4 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -21,6 +21,7 @@
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
#include <media/IAudioTrack.h>
+#include <media/AudioResamplerPublic.h>
#include <utils/threads.h>
namespace android {
@@ -359,6 +360,26 @@
/* Return current source sample rate in Hz */
uint32_t getSampleRate() const;
+ /* Return the original source sample rate in Hz. This corresponds to the sample rate
+ * if playback rate had normal speed and pitch.
+ */
+ uint32_t getOriginalSampleRate() const;
+
+ /* Set source playback rate for timestretch
+ * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
+ * 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
+ *
+ * AUDIO_TIMESTRETCH_SPEED_MIN <= speed <= AUDIO_TIMESTRETCH_SPEED_MAX
+ * AUDIO_TIMESTRETCH_PITCH_MIN <= pitch <= AUDIO_TIMESTRETCH_PITCH_MAX
+ *
+ * Speed increases the playback rate of media, but does not alter pitch.
+ * Pitch increases the "tonal frequency" of media, but does not affect the playback rate.
+ */
+ status_t setPlaybackRate(const AudioPlaybackRate &playbackRate);
+
+ /* Return current playback rate */
+ const AudioPlaybackRate& getPlaybackRate() const;
+
/* Enables looping and sets the start and end points of looping.
* Only supported for static buffer mode.
*
@@ -477,6 +498,35 @@
audio_io_handle_t getOutput() const;
public:
+ /* Selects the audio device to use for output of this AudioTrack. A value of
+ * AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
+ *
+ * Parameters:
+ * The device ID of the selected device (as returned by the AudioDevicesManager API).
+ *
+ * Returned value:
+ * - NO_ERROR: successful operation
+ * TODO: what else can happen here?
+ */
+ status_t setOutputDevice(audio_port_handle_t deviceId);
+
+ /* Returns the ID of the audio device selected for this AudioTrack.
+ * A value of AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
+ *
+ * Parameters:
+ * none.
+ */
+ audio_port_handle_t getOutputDevice();
+
+ /* Returns the ID of the audio device actually used by the output to which this AudioTrack is
+ * attached.
+ * A value of AUDIO_PORT_HANDLE_NONE indicates the audio track is not attached to any output.
+ *
+ * Parameters:
+ * none.
+ */
+ audio_port_handle_t getRoutedDeviceId();
+
/* Returns the unique session ID associated with this track.
*
* Parameters:
@@ -623,6 +673,28 @@
*/
status_t getTimestamp(AudioTimestamp& timestamp);
+ /* Add an AudioDeviceCallback. The caller will be notified when the audio device to which this
+ * AudioTrack is routed is updated.
+ * Replaces any previously installed callback.
+ * Parameters:
+ * callback: The callback interface
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the same callback is already installed.
+ * NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+ /* remove an AudioDeviceCallback.
+ * Parameters:
+ * callback: The callback interface
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the callback is not installed
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t removeAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback);
+
protected:
/* copying audio tracks is not allowed */
AudioTrack(const AudioTrack& other);
@@ -699,6 +771,9 @@
// increment mPosition by the delta of mServer, and return new value of mPosition
uint32_t updateAndGetPosition_l();
+ // check sample rate and speed is compatible with AudioTrack
+ bool isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const;
+
// Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
@@ -710,6 +785,8 @@
float mVolume[2];
float mSendLevel;
mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it
+ uint32_t mOriginalSampleRate;
+ AudioPlaybackRate mPlaybackRate;
size_t mFrameCount; // corresponds to current IAudioTrack, value is
// reported back by AudioFlinger to the client
size_t mReqFrameCount; // frame count to request the first or next time
@@ -792,6 +869,10 @@
int64_t mStartUs; // the start time after flush or stop.
// only used for offloaded and direct tracks.
+ bool mPreviousTimestampValid;// true if mPreviousTimestamp is valid
+ bool mRetrogradeMotionReported; // reduce log spam
+ AudioTimestamp mPreviousTimestamp; // used to detect retrograde motion
+
audio_output_flags_t mFlags;
// const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
// mLock must be held to read or write those bits reliably.
@@ -817,6 +898,10 @@
bool mInUnderrun; // whether track is currently in underrun state
uint32_t mPausedPosition;
+ // For Device Selection API
+ // a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
+ audio_port_handle_t mSelectedDeviceId;
+
private:
class DeathNotifier : public IBinder::DeathRecipient {
public:
@@ -831,6 +916,8 @@
uint32_t mSequence; // incremented for each new IAudioTrack attempt
int mClientUid;
pid_t mClientPid;
+
+ sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
};
class TimedAudioTrack : public AudioTrack
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index f927a80..3f7fd09 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -85,9 +85,11 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ const String16& callingPackage,
size_t *pFrameCount,
track_flags_t *flags,
pid_t tid, // -1 means unused, otherwise must be valid non-0
+ int clientUid,
int *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
@@ -198,6 +200,7 @@
// AudioFlinger doesn't take over handle reference from client
audio_io_handle_t output,
int sessionId,
+ const String16& callingPackage,
status_t *status,
int *id,
int *enabled) = 0;
diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h
index 75a9971..0080bc9 100644
--- a/include/media/IAudioFlingerClient.h
+++ b/include/media/IAudioFlingerClient.h
@@ -22,6 +22,7 @@
#include <binder/IInterface.h>
#include <utils/KeyedVector.h>
#include <system/audio.h>
+#include <media/AudioIoDescriptor.h>
namespace android {
@@ -33,7 +34,8 @@
DECLARE_META_INTERFACE(AudioFlingerClient);
// Notifies a change of audio input/output configuration.
- virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) = 0;
+ virtual void ioConfigChanged(audio_io_config_event event,
+ const sp<AudioIoDescriptor>& ioDesc) = 0;
};
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index fecc6f1..ee462a0 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -62,10 +62,12 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
const audio_offload_info_t *offloadInfo = NULL) = 0;
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -77,12 +79,14 @@
audio_stream_type_t stream,
audio_session_t session) = 0;
virtual status_t getInputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *input,
- audio_session_t session,
- uint32_t samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- audio_input_flags_t flags) = 0;
+ audio_io_handle_t *input,
+ audio_session_t session,
+ uid_t uid,
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE) = 0;
virtual status_t startInput(audio_io_handle_t input,
audio_session_t session) = 0;
virtual status_t stopInput(audio_io_handle_t input,
@@ -154,6 +158,11 @@
virtual audio_mode_t getPhoneState() = 0;
virtual status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration) = 0;
+
+ virtual status_t startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle) = 0;
+ virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
};
diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h
index 59df046..a7f2cc3 100644
--- a/include/media/IAudioPolicyServiceClient.h
+++ b/include/media/IAudioPolicyServiceClient.h
@@ -35,6 +35,8 @@
virtual void onAudioPortListUpdate() = 0;
// Notifies a change of audio patch configuration.
virtual void onAudioPatchListUpdate() = 0;
+ // Notifies a change in the mixing state of a specific mix in a dynamic audio policy
+ virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0;
};
diff --git a/include/media/ICrypto.h b/include/media/ICrypto.h
index 07742ca..ea316de 100644
--- a/include/media/ICrypto.h
+++ b/include/media/ICrypto.h
@@ -25,6 +25,7 @@
namespace android {
struct AString;
+class IMemory;
struct ICrypto : public IInterface {
DECLARE_META_INTERFACE(Crypto);
@@ -43,12 +44,14 @@
virtual void notifyResolution(uint32_t width, uint32_t height) = 0;
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) = 0;
+
virtual ssize_t decrypt(
bool secure,
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) = 0;
@@ -61,6 +64,9 @@
virtual status_t onTransact(
uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags = 0);
+private:
+ void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
+ void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
};
} // namespace android
diff --git a/include/media/IDataSource.h b/include/media/IDataSource.h
new file mode 100644
index 0000000..07e46f7
--- /dev/null
+++ b/include/media/IDataSource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IDATASOURCE_H
+#define ANDROID_IDATASOURCE_H
+
+#include <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class IMemory;
+
+// A binder interface for implementing a stagefright DataSource remotely.
+class IDataSource : public IInterface {
+public:
+ DECLARE_META_INTERFACE(DataSource);
+
+ // Get the memory that readAt writes into.
+ virtual sp<IMemory> getIMemory() = 0;
+ // Read up to |size| bytes into the memory returned by getIMemory(). Returns
+ // the number of bytes read, or -1 on error. |size| must not be larger than
+ // the buffer.
+ virtual ssize_t readAt(off64_t offset, size_t size) = 0;
+ // Get the size, or -1 if the size is unknown.
+ virtual status_t getSize(off64_t* size) = 0;
+ // This should be called before deleting |this|. The other methods may
+ // return errors if they're called after calling close().
+ virtual void close() = 0;
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IDataSource);
+};
+
+// ----------------------------------------------------------------------------
+
+class BnDataSource : public BnInterface<IDataSource> {
+public:
+ virtual status_t onTransact(uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IDATASOURCE_H
diff --git a/include/media/IMediaCodecList.h b/include/media/IMediaCodecList.h
index e93ea8b..12b52d7 100644
--- a/include/media/IMediaCodecList.h
+++ b/include/media/IMediaCodecList.h
@@ -21,6 +21,8 @@
#include <binder/IInterface.h>
#include <binder/Parcel.h>
+#include <media/stagefright/foundation/AMessage.h>
+
namespace android {
struct MediaCodecInfo;
@@ -33,6 +35,8 @@
virtual size_t countCodecs() const = 0;
virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const = 0;
+ virtual const sp<AMessage> getGlobalSettings() const = 0;
+
virtual ssize_t findCodecByType(
const char *type, bool encoder, size_t startIndex = 0) const = 0;
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index 2529800..c90f254 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -26,6 +26,7 @@
namespace android {
+class IDataSource;
struct IMediaHTTPService;
class IMediaMetadataRetriever: public IInterface
@@ -40,6 +41,7 @@
const KeyedVector<String8, String8> *headers = NULL) = 0;
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
+ virtual status_t setDataSource(const sp<IDataSource>& dataSource) = 0;
virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option) = 0;
virtual sp<IMemory> extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index 4153c25..0fd8933 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -31,9 +31,12 @@
class Parcel;
class Surface;
-class IStreamSource;
+class IDataSource;
+struct IStreamSource;
class IGraphicBufferProducer;
struct IMediaHTTPService;
+struct AudioPlaybackRate;
+struct AVSyncSettings;
class IMediaPlayer: public IInterface
{
@@ -49,6 +52,7 @@
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setDataSource(const sp<IStreamSource>& source) = 0;
+ virtual status_t setDataSource(const sp<IDataSource>& source) = 0;
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) = 0;
virtual status_t prepareAsync() = 0;
@@ -56,7 +60,11 @@
virtual status_t stop() = 0;
virtual status_t pause() = 0;
virtual status_t isPlaying(bool* state) = 0;
- virtual status_t setPlaybackRate(float rate) = 0;
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) = 0;
+ virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) = 0;
+ virtual status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) = 0;
+ virtual status_t getSyncSettings(AVSyncSettings* sync /* nonnull */,
+ float* videoFps /* nonnull */) = 0;
virtual status_t seekTo(int msec) = 0;
virtual status_t getCurrentPosition(int* msec) = 0;
virtual status_t getDuration(int* msec) = 0;
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 49a3d61..a316ce2 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -47,7 +47,7 @@
public:
DECLARE_META_INTERFACE(MediaPlayerService);
- virtual sp<IMediaRecorder> createMediaRecorder() = 0;
+ virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName) = 0;
virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0;
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId = 0)
= 0;
@@ -65,8 +65,8 @@
// display client when display connection, disconnection or errors occur.
// The assumption is that at most one remote display will be connected to the
// provided interface at a time.
- virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
- const String8& iface) = 0;
+ virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName,
+ const sp<IRemoteDisplayClient>& client, const String8& iface) = 0;
// codecs and audio devices usage tracking for the battery app
enum BatteryDataBits {
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 509c06b..77ed5d3 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -26,6 +26,7 @@
class ICamera;
class ICameraRecordingProxy;
class IMediaRecorderClient;
+class IGraphicBufferConsumer;
class IGraphicBufferProducer;
class IMediaRecorder: public IInterface
@@ -55,6 +56,7 @@
virtual status_t init() = 0;
virtual status_t close() = 0;
virtual status_t release() = 0;
+ virtual status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface) = 0;
virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() = 0;
};
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 6def65b..d33d142 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -20,6 +20,7 @@
#include <binder/IInterface.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IGraphicBufferConsumer.h>
#include <ui/GraphicBuffer.h>
#include <utils/List.h>
#include <utils/String8.h>
@@ -113,6 +114,14 @@
node_id node, OMX_U32 port_index,
sp<IGraphicBufferProducer> *bufferProducer) = 0;
+ virtual status_t createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer) = 0;
+
+ virtual status_t setInputSurface(
+ node_id node, OMX_U32 port_index,
+ const sp<IGraphicBufferConsumer> &bufferConsumer) = 0;
+
virtual status_t signalEndOfInputStream(node_id node) = 0;
// This API clearly only makes sense if the caller lives in the
diff --git a/include/media/IResourceManagerClient.h b/include/media/IResourceManagerClient.h
index 3587aea..aa0cd88 100644
--- a/include/media/IResourceManagerClient.h
+++ b/include/media/IResourceManagerClient.h
@@ -18,6 +18,7 @@
#define ANDROID_IRESOURCEMANAGERCLIENT_H
#include <utils/RefBase.h>
+#include <utils/String8.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
@@ -29,6 +30,7 @@
DECLARE_META_INTERFACE(ResourceManagerClient);
virtual bool reclaimResource() = 0;
+ virtual String8 getName() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h
index 149bd49..4a6aafd 100644
--- a/include/media/IStreamSource.h
+++ b/include/media/IStreamSource.h
@@ -23,7 +23,7 @@
namespace android {
struct AMessage;
-struct IMemory;
+class IMemory;
struct IStreamListener;
struct IStreamSource : public IInterface {
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index cd56adb..4067b47 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -32,9 +32,11 @@
namespace android {
struct AMessage;
-struct Parcel;
+class Parcel;
struct CodecCapabilities;
+typedef KeyedVector<AString, AString> CodecSettings;
+
struct MediaCodecInfo : public RefBase {
struct ProfileLevel {
uint32_t mProfile;
@@ -104,6 +106,7 @@
MediaCodecInfo(AString name, bool encoder, const char *mime);
void addQuirk(const char *name);
status_t addMime(const char *mime);
+ status_t updateMime(const char *mime);
status_t initializeCapabilities(const CodecCapabilities &caps);
void addDetail(const AString &key, const AString &value);
void addFeature(const AString &key, int32_t value);
@@ -114,6 +117,7 @@
DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
friend class MediaCodecList;
+ friend class MediaCodecListOverridesTest;
};
} // namespace android
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index 38dbb20..bce6ee3 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -25,6 +25,7 @@
namespace android {
+class DataSource;
struct IMediaHTTPService;
// Abstract base class
@@ -40,6 +41,7 @@
const KeyedVector<String8, String8> *headers = NULL) = 0;
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
+ virtual status_t setDataSource(const sp<DataSource>& source) = 0;
virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) = 0;
virtual MediaAlbumArt* extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index d6fe390..fa917f9 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -26,8 +26,10 @@
#include <utils/RefBase.h>
#include <media/mediaplayer.h>
+#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
+#include <media/AVSyncSettings.h>
#include <media/Metadata.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
@@ -36,6 +38,7 @@
namespace android {
+class DataSource;
class Parcel;
class Surface;
class IGraphicBufferProducer;
@@ -131,11 +134,12 @@
virtual void pause() = 0;
virtual void close() = 0;
- virtual status_t setPlaybackRatePermille(int32_t rate) { return INVALID_OPERATION; }
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate) = 0;
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
virtual bool needsTrailingPadding() { return true; }
- virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR; };
- virtual String8 getParameters(const String8& keys) { return String8::empty(); };
+ virtual status_t setParameters(const String8& /* keyValuePairs */) { return NO_ERROR; }
+ virtual String8 getParameters(const String8& /* keys */) { return String8::empty(); }
};
MediaPlayerBase() : mCookie(0), mNotify(0) {}
@@ -143,7 +147,7 @@
virtual status_t initCheck() = 0;
virtual bool hardwareOutput() = 0;
- virtual status_t setUID(uid_t uid) {
+ virtual status_t setUID(uid_t /* uid */) {
return INVALID_OPERATION;
}
@@ -154,7 +158,11 @@
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
- virtual status_t setDataSource(const sp<IStreamSource> &source) {
+ virtual status_t setDataSource(const sp<IStreamSource>& /* source */) {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t setDataSource(const sp<DataSource>& /* source */) {
return INVALID_OPERATION;
}
@@ -168,7 +176,31 @@
virtual status_t stop() = 0;
virtual status_t pause() = 0;
virtual bool isPlaying() = 0;
- virtual status_t setPlaybackRate(float rate) { return INVALID_OPERATION; }
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) {
+ // by default, players only support setting rate to the default
+ if (!isAudioPlaybackRateEqual(rate, AUDIO_PLAYBACK_RATE_DEFAULT)) {
+ return BAD_VALUE;
+ }
+ return OK;
+ }
+ virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
+ *rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+ return OK;
+ }
+ virtual status_t setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
+ // By default, players only support setting sync source to default; all other sync
+ // settings are ignored. There is no requirement for getters to return set values.
+ if (sync.mSource != AVSYNC_SOURCE_DEFAULT) {
+ return BAD_VALUE;
+ }
+ return OK;
+ }
+ virtual status_t getSyncSettings(
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
+ *sync = AVSyncSettings();
+ *videoFps = -1.f;
+ return OK;
+ }
virtual status_t seekTo(int msec) = 0;
virtual status_t getCurrentPosition(int *msec) = 0;
virtual status_t getDuration(int *msec) = 0;
@@ -179,13 +211,13 @@
virtual status_t getParameter(int key, Parcel *reply) = 0;
// default no-op implementation of optional extensions
- virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) {
+ virtual status_t setRetransmitEndpoint(const struct sockaddr_in* /* endpoint */) {
return INVALID_OPERATION;
}
- virtual status_t getRetransmitEndpoint(struct sockaddr_in* endpoint) {
+ virtual status_t getRetransmitEndpoint(struct sockaddr_in* /* endpoint */) {
return INVALID_OPERATION;
}
- virtual status_t setNextPlayer(const sp<MediaPlayerBase>& next) {
+ virtual status_t setNextPlayer(const sp<MediaPlayerBase>& /* next */) {
return OK;
}
@@ -205,8 +237,8 @@
// the known metadata should be returned.
// @param[inout] records Parcel where the player appends its metadata.
// @return OK if the call was successful.
- virtual status_t getMetadata(const media::Metadata::Filter& ids,
- Parcel *records) {
+ virtual status_t getMetadata(const media::Metadata::Filter& /* ids */,
+ Parcel* /* records */) {
return INVALID_OPERATION;
};
@@ -229,7 +261,7 @@
if (notifyCB) notifyCB(cookie, msg, ext1, ext2, obj);
}
- virtual status_t dump(int fd, const Vector<String16> &args) const {
+ virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
return INVALID_OPERATION;
}
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index f55063e..d6cc4bb 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -26,10 +26,12 @@
class ICameraRecordingProxy;
class Surface;
+class IGraphicBufferConsumer;
class IGraphicBufferProducer;
struct MediaRecorderBase {
- MediaRecorderBase() {}
+ MediaRecorderBase(const String16 &opPackageName)
+ : mOpPackageName(opPackageName) {}
virtual ~MediaRecorderBase() {}
virtual status_t init() = 0;
@@ -55,8 +57,13 @@
virtual status_t reset() = 0;
virtual status_t getMaxAmplitude(int *max) = 0;
virtual status_t dump(int fd, const Vector<String16>& args) const = 0;
+ virtual status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface) = 0;
virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() const = 0;
+
+protected:
+ String16 mOpPackageName;
+
private:
MediaRecorderBase(const MediaRecorderBase &);
MediaRecorderBase &operator=(const MediaRecorderBase &);
diff --git a/include/media/MediaResource.h b/include/media/MediaResource.h
index 0b57c84..20f2cad 100644
--- a/include/media/MediaResource.h
+++ b/include/media/MediaResource.h
@@ -25,6 +25,8 @@
extern const char kResourceSecureCodec[];
extern const char kResourceNonSecureCodec[];
+extern const char kResourceAudioCodec[];
+extern const char kResourceVideoCodec[];
extern const char kResourceGraphicMemory[];
class MediaResource {
diff --git a/include/media/MediaResourcePolicy.h b/include/media/MediaResourcePolicy.h
index 1e1c341..9bc2eec 100644
--- a/include/media/MediaResourcePolicy.h
+++ b/include/media/MediaResourcePolicy.h
@@ -29,7 +29,7 @@
class MediaResourcePolicy {
public:
MediaResourcePolicy();
- MediaResourcePolicy(String8 type, uint64_t value);
+ MediaResourcePolicy(String8 type, String8 value);
void readFromParcel(const Parcel &parcel);
void writeToParcel(Parcel *parcel) const;
@@ -37,7 +37,7 @@
String8 toString() const;
String8 mType;
- uint64_t mValue;
+ String8 mValue;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/include/media/RingBuffer.h
similarity index 100%
rename from services/camera/libcameraservice/utils/RingBuffer.h
rename to include/media/RingBuffer.h
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index 6167dd6..b92f816 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -65,7 +65,8 @@
/* Constructor.
* See AudioEffect constructor for details on parameters.
*/
- Visualizer(int32_t priority = 0,
+ Visualizer(const String16& opPackageName,
+ int32_t priority = 0,
effect_callback_t cbf = NULL,
void* user = NULL,
int sessionId = 0);
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index b35cf32..f655f35 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -25,6 +25,7 @@
namespace android {
+class IDataSource;
struct IMediaHTTPService;
class IMediaPlayerService;
class IMediaMetadataRetriever;
@@ -57,6 +58,7 @@
METADATA_KEY_IS_DRM = 22,
METADATA_KEY_LOCATION = 23,
METADATA_KEY_VIDEO_ROTATION = 24,
+ METADATA_KEY_CAPTURE_FRAMERATE = 25,
// Add more here...
};
@@ -74,6 +76,7 @@
const KeyedVector<String8, String8> *headers = NULL);
status_t setDataSource(int fd, int64_t offset, int64_t length);
+ status_t setDataSource(const sp<IDataSource>& dataSource);
sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
sp<IMemory> extractAlbumArt();
const char* extractMetadata(int keyCode);
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 808e893..3fe749c 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -20,6 +20,8 @@
#include <arpa/inet.h>
#include <binder/IMemory.h>
+
+#include <media/AudioResamplerPublic.h>
#include <media/IMediaPlayerClient.h>
#include <media/IMediaPlayer.h>
#include <media/IMediaDeathNotifier.h>
@@ -32,8 +34,9 @@
namespace android {
-class Surface;
+struct AVSyncSettings;
class IGraphicBufferProducer;
+class Surface;
enum media_event_type {
MEDIA_NOP = 0, // interface test message
@@ -50,6 +53,7 @@
MEDIA_ERROR = 100,
MEDIA_INFO = 200,
MEDIA_SUBTITLE_DATA = 201,
+ MEDIA_META_DATA = 202,
};
// Generic error codes for the media player framework. Errors are fatal, the
@@ -183,6 +187,7 @@
MEDIA_TRACK_TYPE_AUDIO = 2,
MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
MEDIA_TRACK_TYPE_SUBTITLE = 4,
+ MEDIA_TRACK_TYPE_METADATA = 5,
};
// ----------------------------------------------------------------------------
@@ -211,6 +216,7 @@
status_t setDataSource(int fd, int64_t offset, int64_t length);
status_t setDataSource(const sp<IStreamSource> &source);
+ status_t setDataSource(const sp<IDataSource> &source);
status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer);
status_t setListener(const sp<MediaPlayerListener>& listener);
@@ -220,7 +226,12 @@
status_t stop();
status_t pause();
bool isPlaying();
- status_t setPlaybackRate(float rate);
+ status_t setPlaybackSettings(const AudioPlaybackRate& rate);
+ status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
+ status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint);
+ status_t getSyncSettings(
+ AVSyncSettings* sync /* nonnull */,
+ float* videoFps /* nonnull */);
status_t getVideoWidth(int *w);
status_t getVideoHeight(int *h);
status_t seekTo(int msec);
@@ -275,7 +286,6 @@
int mVideoWidth;
int mVideoHeight;
int mAudioSessionId;
- float mPlaybackRate;
float mSendLevel;
struct sockaddr_in mRetransmitEndpoint;
bool mRetransmitEndpointValid;
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 74a6469..15ff82d 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -32,6 +32,7 @@
class ICamera;
class ICameraRecordingProxy;
class IGraphicBufferProducer;
+struct PersistentSurface;
class Surface;
typedef void (*media_completion_f)(status_t status, void *cookie);
@@ -209,7 +210,7 @@
public virtual IMediaDeathNotifier
{
public:
- MediaRecorder();
+ MediaRecorder(const String16& opPackageName);
~MediaRecorder();
void died();
@@ -236,6 +237,7 @@
status_t close();
status_t release();
void notify(int msg, int ext1, int ext2);
+ status_t setInputSurface(const sp<PersistentSurface>& surface);
sp<IGraphicBufferProducer> querySurfaceMediaSourceFromMediaServer();
private:
diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h
index 86417a5..aa60a19 100644
--- a/include/media/stagefright/AACWriter.h
+++ b/include/media/stagefright/AACWriter.h
@@ -24,7 +24,7 @@
namespace android {
struct MediaSource;
-struct MetaData;
+class MetaData;
struct AACWriter : public MediaWriter {
AACWriter(int fd);
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index c1483f3..54c57ad 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -44,9 +44,12 @@
virtual void initiateAllocateComponent(const sp<AMessage> &msg);
virtual void initiateConfigureComponent(const sp<AMessage> &msg);
virtual void initiateCreateInputSurface();
+ virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface);
virtual void initiateStart();
virtual void initiateShutdown(bool keepComponentAllocated = false);
+ virtual status_t setSurface(const sp<Surface> &surface);
+
virtual void signalFlush();
virtual void signalResume();
@@ -113,7 +116,9 @@
kWhatDrainDeferredMessages = 'drai',
kWhatAllocateComponent = 'allo',
kWhatConfigureComponent = 'conf',
+ kWhatSetSurface = 'setS',
kWhatCreateInputSurface = 'cisf',
+ kWhatSetInputSurface = 'sisf',
kWhatSignalEndOfInputStream = 'eois',
kWhatStart = 'star',
kWhatRequestIDRFrame = 'ridr',
@@ -192,6 +197,7 @@
List<sp<AMessage> > mDeferredQueue;
bool mSentFormat;
+ bool mIsVideo;
bool mIsEncoder;
bool mUseMetadataOnEncoderOutput;
bool mShutdownInProgress;
@@ -209,6 +215,7 @@
int32_t mChannelMask;
unsigned mDequeueCounter;
bool mStoreMetaDataInOutputBuffers;
+ bool mLegacyAdaptiveExperiment;
int32_t mMetaDataBuffersToSubmit;
size_t mNumUndequeuedBuffers;
@@ -228,6 +235,9 @@
status_t freeBuffersOnPort(OMX_U32 portIndex);
status_t freeBuffer(OMX_U32 portIndex, size_t i);
+ status_t handleSetSurface(const sp<Surface> &surface);
+ status_t setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */);
+
status_t configureOutputBuffersFromNativeWindow(
OMX_U32 *nBufferCount, OMX_U32 *nBufferSize,
OMX_U32 *nMinUndequeuedBuffers);
@@ -300,6 +310,7 @@
OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
status_t setPriority(int32_t priority);
+ status_t setOperatingRate(float rateFloat, bool isVideo);
status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
@@ -318,8 +329,6 @@
status_t initNativeWindow();
- status_t pushBlankBuffersToNativeWindow();
-
// Returns true iff all buffers on the given port have status
// OWNED_BY_US or OWNED_BY_NATIVE_WINDOW.
bool allYourBuffersAreBelongToUs(OMX_U32 portIndex);
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index bac878b..b38be55 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -26,7 +26,7 @@
namespace android {
struct MediaSource;
-struct MetaData;
+class MetaData;
struct AMRWriter : public MediaWriter {
AMRWriter(int fd);
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 14afb85..e0cd965 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -25,9 +25,10 @@
namespace android {
-class MediaSource;
+struct AudioPlaybackRate;
class AudioTrack;
-class AwesomePlayer;
+struct AwesomePlayer;
+class MediaSource;
class AudioPlayer : public TimeSource {
public:
@@ -73,7 +74,8 @@
bool isSeeking();
bool reachedEOS(status_t *finalStatus);
- status_t setPlaybackRatePermille(int32_t ratePermille);
+ status_t setPlaybackRate(const AudioPlaybackRate &rate);
+ status_t getPlaybackRate(AudioPlaybackRate *rate /* nonnull */);
void notifyAudioEOS();
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 4c9aaad..50cf371 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -35,6 +35,7 @@
// _not_ a bitmask of audio_channels_t constants.
AudioSource(
audio_source_t inputSource,
+ const String16 &opPackageName,
uint32_t sampleRate,
uint32_t channels = 1);
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index dd0a106..96dfd7e 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -188,7 +188,7 @@
void releaseCamera();
private:
- friend class CameraSourceListener;
+ friend struct CameraSourceListener;
Mutex mLock;
Condition mFrameAvailableCondition;
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 1bf27a6..989df4f 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -26,6 +26,7 @@
namespace android {
struct ABuffer;
+struct PersistentSurface;
struct CodecBase : public AHandler {
enum {
@@ -39,6 +40,7 @@
kWhatComponentAllocated = 'cAll',
kWhatComponentConfigured = 'cCon',
kWhatInputSurfaceCreated = 'isfc',
+ kWhatInputSurfaceAccepted = 'isfa',
kWhatSignaledInputEOS = 'seos',
kWhatBuffersAllocated = 'allc',
};
@@ -48,12 +50,16 @@
virtual void initiateAllocateComponent(const sp<AMessage> &msg) = 0;
virtual void initiateConfigureComponent(const sp<AMessage> &msg) = 0;
virtual void initiateCreateInputSurface() = 0;
+ virtual void initiateSetInputSurface(
+ const sp<PersistentSurface> &surface) = 0;
virtual void initiateStart() = 0;
virtual void initiateShutdown(bool keepComponentAllocated = false) = 0;
// require an explicit message handler
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+ virtual status_t setSurface(const sp<Surface> &surface) { return INVALID_OPERATION; }
+
virtual void signalFlush() = 0;
virtual void signalResume() = 0;
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 3630263..dcde36f 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -32,6 +32,7 @@
struct AMessage;
struct AString;
+class IDataSource;
struct IMediaHTTPService;
class String8;
struct HTTPBase;
@@ -53,11 +54,15 @@
HTTPBase *httpSource = NULL);
static sp<DataSource> CreateMediaHTTP(const sp<IMediaHTTPService> &httpService);
+ static sp<DataSource> CreateFromIDataSource(const sp<IDataSource> &source);
DataSource() {}
virtual status_t initCheck() const = 0;
+ // Returns the number of bytes read, or -1 on failure. It's not an error if
+ // this returns zero; it just means the given offset is equal to, or
+ // beyond, the end of the source.
virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
// Convenience methods:
diff --git a/include/media/stagefright/MediaClock.h b/include/media/stagefright/MediaClock.h
index e9c09a1..dd1a809 100644
--- a/include/media/stagefright/MediaClock.h
+++ b/include/media/stagefright/MediaClock.h
@@ -42,6 +42,7 @@
void updateMaxTimeMedia(int64_t maxTimeMediaUs);
void setPlaybackRate(float rate);
+ float getPlaybackRate() const;
// query media time corresponding to real time |realUs|, and save the
// result in |outMediaUs|.
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 8241e19..6e14fc5 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -20,6 +20,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <media/hardware/CryptoAPI.h>
+#include <media/MediaResource.h>
#include <media/stagefright/foundation/AHandler.h>
#include <utils/Vector.h>
@@ -30,8 +31,13 @@
struct AReplyToken;
struct AString;
struct CodecBase;
-struct ICrypto;
struct IBatteryStats;
+struct ICrypto;
+class IMemory;
+struct MemoryDealer;
+class IResourceManagerClient;
+class IResourceManagerService;
+struct PersistentSurface;
struct SoftwareRenderer;
struct Surface;
@@ -51,6 +57,7 @@
CB_OUTPUT_AVAILABLE = 2,
CB_ERROR = 3,
CB_OUTPUT_FORMAT_CHANGED = 4,
+ CB_RESOURCE_RECLAIMED = 5,
};
struct BatteryNotifier;
@@ -61,6 +68,8 @@
static sp<MediaCodec> CreateByComponentName(
const sp<ALooper> &looper, const char *name, status_t *err = NULL);
+ static sp<PersistentSurface> CreatePersistentInputSurface();
+
status_t configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
@@ -71,6 +80,8 @@
status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+ status_t setInputSurface(const sp<PersistentSurface> &surface);
+
status_t start();
// Returns to a state in which the component remains allocated but
@@ -126,6 +137,8 @@
status_t getOutputFormat(sp<AMessage> *format) const;
status_t getInputFormat(sp<AMessage> *format) const;
+ status_t getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const;
+
status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const;
@@ -133,6 +146,8 @@
status_t getOutputFormat(size_t index, sp<AMessage> *format);
status_t getInputBuffer(size_t index, sp<ABuffer> *buffer);
+ status_t setSurface(const sp<Surface> &nativeWindow);
+
status_t requestIDRFrame();
// Notification will be posted once there "is something to do", i.e.
@@ -149,6 +164,11 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
private:
+ // used by ResourceManagerClient
+ status_t reclaim();
+ friend struct ResourceManagerClient;
+
+private:
enum State {
UNINITIALIZED,
INITIALIZING,
@@ -171,7 +191,9 @@
enum {
kWhatInit = 'init',
kWhatConfigure = 'conf',
+ kWhatSetSurface = 'sSur',
kWhatCreateInputSurface = 'cisf',
+ kWhatSetInputSurface = 'sisf',
kWhatStart = 'strt',
kWhatStop = 'stop',
kWhatRelease = 'rele',
@@ -207,18 +229,45 @@
kFlagGatherCodecSpecificData = 512,
kFlagIsAsync = 1024,
kFlagIsComponentAllocated = 2048,
+ kFlagPushBlankBuffersOnShutdown = 4096,
};
struct BufferInfo {
uint32_t mBufferID;
sp<ABuffer> mData;
sp<ABuffer> mEncryptedData;
+ sp<IMemory> mSharedEncryptedBuffer;
sp<AMessage> mNotify;
sp<AMessage> mFormat;
bool mOwnedByClient;
};
+ struct ResourceManagerServiceProxy : public IBinder::DeathRecipient {
+ ResourceManagerServiceProxy();
+ ~ResourceManagerServiceProxy();
+
+ void init();
+
+ // implements DeathRecipient
+ virtual void binderDied(const wp<IBinder>& /*who*/);
+
+ void addResource(
+ int pid,
+ int64_t clientId,
+ const sp<IResourceManagerClient> client,
+ const Vector<MediaResource> &resources);
+
+ void removeResource(int64_t clientId);
+
+ bool reclaimResource(int callingPid, const Vector<MediaResource> &resources);
+
+ private:
+ Mutex mLock;
+ sp<IResourceManagerService> mService;
+ };
+
State mState;
+ bool mReleasedByResourceManager;
sp<ALooper> mLooper;
sp<ALooper> mCodecLooper;
sp<CodecBase> mCodec;
@@ -226,20 +275,29 @@
sp<AReplyToken> mReplyID;
uint32_t mFlags;
status_t mStickyError;
- sp<Surface> mNativeWindow;
+ sp<Surface> mSurface;
SoftwareRenderer *mSoftRenderer;
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
sp<AMessage> mCallback;
+ sp<MemoryDealer> mDealer;
+
+ sp<IResourceManagerClient> mResourceManagerClient;
+ sp<ResourceManagerServiceProxy> mResourceManagerService;
bool mBatteryStatNotified;
bool mIsVideo;
+ int32_t mVideoWidth;
+ int32_t mVideoHeight;
// initial create parameters
AString mInitName;
bool mInitNameIsType;
bool mInitIsEncoder;
+ // configure parameter
+ sp<AMessage> mConfigureMsg;
+
// Used only to synchronize asynchronous getBufferAndFormat
// across all the other (synchronous) buffer state change
// operations, such as de/queueIn/OutputBuffer, start and
@@ -262,13 +320,14 @@
sp<AMessage> mActivityNotify;
bool mHaveInputSurface;
+ bool mHavePendingInputBuffers;
MediaCodec(const sp<ALooper> &looper);
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
- static void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
+ void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
status_t init(const AString &name, bool nameIsType, bool encoder);
@@ -291,8 +350,9 @@
void extractCSD(const sp<AMessage> &format);
status_t queueCSDInputBuffer(size_t bufferIndex);
- status_t setNativeWindow(
- const sp<Surface> &surface);
+ status_t handleSetSurface(const sp<Surface> &surface);
+ status_t connectToSurface(const sp<Surface> &surface);
+ status_t disconnectFromSurface();
void postActivityNotificationIfPossible();
@@ -307,6 +367,9 @@
void updateBatteryStat();
bool isExecuting() const;
+ uint64_t getGraphicBufferSize();
+ void addResource(const String8 &type, const String8 &subtype, uint64_t value);
+
/* called to get the last codec error when the sticky flag is set.
* if no such codec error is found, returns UNKNOWN_ERROR.
*/
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index c2bbe4d..ce34338 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -48,9 +48,14 @@
return mCodecInfos.itemAt(index);
}
+ virtual const sp<AMessage> getGlobalSettings() const;
+
// to be used by MediaPlayerService alone
static sp<IMediaCodecList> getLocalInstance();
+ // only to be used in getLocalInstance
+ void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
+
private:
class BinderDeathObserver : public IBinder::DeathRecipient {
void binderDied(const wp<IBinder> &the_late_who __unused);
@@ -60,6 +65,7 @@
enum Section {
SECTION_TOPLEVEL,
+ SECTION_SETTINGS,
SECTION_DECODERS,
SECTION_DECODER,
SECTION_DECODER_TYPE,
@@ -74,10 +80,14 @@
status_t mInitCheck;
Section mCurrentSection;
+ bool mUpdate;
Vector<Section> mPastSections;
int32_t mDepth;
AString mHrefBase;
+ sp<AMessage> mGlobalSettings;
+ KeyedVector<AString, CodecSettings> mOverrides;
+
Vector<sp<MediaCodecInfo> > mCodecInfos;
sp<MediaCodecInfo> mCurrentInfo;
sp<IOMX> mOMX;
@@ -87,7 +97,6 @@
status_t initCheck() const;
void parseXMLFile(const char *path);
- void parseTopLevelXMLFile(const char *path);
static void StartElementHandlerWrapper(
void *me, const char *name, const char **attrs);
@@ -98,9 +107,12 @@
void endElementHandler(const char *name);
status_t includeXMLFile(const char **attrs);
+ status_t addSettingFromAttributes(const char **attrs);
status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
+ void setCurrentCodecInfo(bool encoder, const char *name, const char *type);
+
status_t addQuirk(const char **attrs);
status_t addTypeFromAttributes(const char **attrs);
status_t addLimit(const char **attrs);
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
index 7b8f59d..a991b02 100644
--- a/include/media/stagefright/MediaCodecSource.h
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -23,10 +23,11 @@
namespace android {
-class ALooper;
+struct ALooper;
class AMessage;
struct AReplyToken;
class IGraphicBufferProducer;
+class IGraphicBufferConsumer;
class MediaCodec;
class MetaData;
@@ -41,6 +42,7 @@
const sp<ALooper> &looper,
const sp<AMessage> &format,
const sp<MediaSource> &source,
+ const sp<IGraphicBufferConsumer> &consumer = NULL,
uint32_t flags = 0);
bool isVideo() const { return mIsVideo; }
@@ -79,6 +81,7 @@
const sp<ALooper> &looper,
const sp<AMessage> &outputFormat,
const sp<MediaSource> &source,
+ const sp<IGraphicBufferConsumer> &consumer,
uint32_t flags = 0);
status_t onStart(MetaData *params);
@@ -107,6 +110,7 @@
bool mDoMoreWorkPending;
sp<AMessage> mEncoderActivityNotify;
sp<IGraphicBufferProducer> mGraphicBufferProducer;
+ sp<IGraphicBufferConsumer> mGraphicBufferConsumer;
List<MediaBuffer *> mInputBufferQueue;
List<size_t> mAvailEncoderInputIndices;
List<int64_t> mDecodingTimeQueue; // decoding time (us) for video
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index a0036e0..3b58122 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -64,6 +64,7 @@
extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
extern const char *MEDIA_MIMETYPE_TEXT_VTT;
extern const char *MEDIA_MIMETYPE_TEXT_CEA_608;
+extern const char *MEDIA_MIMETYPE_DATA_METADATA;
} // namespace android
diff --git a/include/media/stagefright/MediaFilter.h b/include/media/stagefright/MediaFilter.h
index 7b3f700..d0a572c 100644
--- a/include/media/stagefright/MediaFilter.h
+++ b/include/media/stagefright/MediaFilter.h
@@ -34,6 +34,8 @@
virtual void initiateAllocateComponent(const sp<AMessage> &msg);
virtual void initiateConfigureComponent(const sp<AMessage> &msg);
virtual void initiateCreateInputSurface();
+ virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface);
+
virtual void initiateStart();
virtual void initiateShutdown(bool keepComponentAllocated = false);
diff --git a/include/media/stagefright/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h
index e6538d1..fa855a8 100644
--- a/include/media/stagefright/MediaMuxer.h
+++ b/include/media/stagefright/MediaMuxer.h
@@ -29,9 +29,9 @@
struct ABuffer;
struct AMessage;
struct MediaAdapter;
-struct MediaBuffer;
+class MediaBuffer;
struct MediaSource;
-struct MetaData;
+class MetaData;
struct MediaWriter;
// MediaMuxer is used to mux multiple tracks into a video. Currently, we only
diff --git a/include/media/stagefright/MediaSync.h b/include/media/stagefright/MediaSync.h
index 8bb8c7f..1eef211 100644
--- a/include/media/stagefright/MediaSync.h
+++ b/include/media/stagefright/MediaSync.h
@@ -20,9 +20,12 @@
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
#include <media/stagefright/foundation/AHandler.h>
#include <utils/Condition.h>
+#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
namespace android {
@@ -72,14 +75,11 @@
// Called when MediaSync is used to render video. It should be called
// before createInputSurface().
- status_t configureSurface(const sp<IGraphicBufferProducer> &output);
+ status_t setSurface(const sp<IGraphicBufferProducer> &output);
// Called when audio track is used as media clock source. It should be
// called before updateQueuedAudioData().
- // |nativeSampleRateInHz| is the sample rate of audio data fed into audio
- // track. It's the same number used to create AudioTrack.
- status_t configureAudioTrack(
- const sp<AudioTrack> &audioTrack, uint32_t nativeSampleRateInHz);
+ status_t setAudioTrack(const sp<AudioTrack> &audioTrack);
// Create a surface for client to render video frames. This is the surface
// on which the client should render video frames. Those video frames will
@@ -98,21 +98,34 @@
// Set the consumer name of the input queue.
void setName(const AString &name);
- // Set the playback in a desired speed.
- // This method can be called any time.
- // |rate| is the ratio between desired speed and the normal one, and should
- // be non-negative. The meaning of rate values:
- // 1.0 -- normal playback
- // 0.0 -- stop or pause
- // larger than 1.0 -- faster than normal speed
- // between 0.0 and 1.0 -- slower than normal speed
- status_t setPlaybackRate(float rate);
-
// Get the media clock used by the MediaSync so that the client can obtain
// corresponding media time or real time via
// MediaClock::getMediaTime() and MediaClock::getRealTimeFor().
sp<const MediaClock> getMediaClock();
+ // Set the video frame rate hint - this is used by the video FrameScheduler
+ status_t setVideoFrameRateHint(float rate);
+
+ // Get the video frame rate measurement from the FrameScheduler
+ // returns -1 if there is no measurement
+ float getVideoFrameRate();
+
+ // Set the sync settings parameters.
+ status_t setSyncSettings(const AVSyncSettings &syncSettings);
+
+ // Gets the sync settings parameters.
+ void getSyncSettings(AVSyncSettings *syncSettings /* nonnull */);
+
+ // Sets the playback rate using playback settings.
+ // This method can be called any time.
+ status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+
+ // Gets the playback rate (playback settings parameters).
+ void getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+
+ // Get the play time for pending audio frames in audio sink.
+ status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
+
protected:
virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -156,7 +169,7 @@
class OutputListener : public BnProducerListener,
public IBinder::DeathRecipient {
public:
- OutputListener(const sp<MediaSync> &sync);
+ OutputListener(const sp<MediaSync> &sync, const sp<IGraphicBufferProducer> &output);
virtual ~OutputListener();
// From IProducerListener
@@ -167,6 +180,7 @@
private:
sp<MediaSync> mSync;
+ sp<IGraphicBufferProducer> mOutput;
};
// mIsAbandoned is set to true when the input or output dies.
@@ -179,6 +193,7 @@
size_t mNumOutstandingBuffers;
sp<IGraphicBufferConsumer> mInput;
sp<IGraphicBufferProducer> mOutput;
+ int mUsageFlagsFromOutput;
sp<AudioTrack> mAudioTrack;
uint32_t mNativeSampleRateInHz;
@@ -187,9 +202,25 @@
int64_t mNextBufferItemMediaUs;
List<BufferItem> mBufferItems;
+
+ // Keep track of buffers received from |mInput|. This is needed because
+ // it's possible the consumer of |mOutput| could return a different
+ // GraphicBuffer::handle (e.g., due to passing buffers through IPC),
+ // and that could cause problem if the producer of |mInput| only
+ // supports pre-registered buffers.
+ KeyedVector<uint64_t, sp<GraphicBuffer> > mBuffersFromInput;
+
+ // Keep track of buffers sent to |mOutput|. When a new output surface comes
+ // in, those buffers will be returned to input and old output surface will
+ // be disconnected immediately.
+ KeyedVector<uint64_t, sp<GraphicBuffer> > mBuffersSentToOutput;
+
sp<ALooper> mLooper;
float mPlaybackRate;
+ AudioPlaybackRate mPlaybackSettings;
+ AVSyncSettings mSyncSettings;
+
sp<MediaClock> mMediaClock;
MediaSync();
@@ -218,7 +249,7 @@
// It gets called from an OutputListener.
// During this callback, we detach the buffer from the output, and release
// it to the input. A blocked onFrameAvailable call will be allowed to proceed.
- void onBufferReleasedByOutput();
+ void onBufferReleasedByOutput(sp<IGraphicBufferProducer> &output);
// Return |buffer| back to the input.
void returnBufferToInput_l(const sp<GraphicBuffer> &buffer, const sp<Fence> &fence);
@@ -228,6 +259,22 @@
// up. This must be called with mMutex locked.
void onAbandoned_l(bool isInput);
+ // Set the playback in a desired speed.
+ // This method can be called any time.
+ // |rate| is the ratio between desired speed and the normal one, and should
+ // be non-negative. The meaning of rate values:
+ // 1.0 -- normal playback
+ // 0.0 -- stop or pause
+ // larger than 1.0 -- faster than normal speed
+ // between 0.0 and 1.0 -- slower than normal speed
+ void updatePlaybackRate_l(float rate);
+
+ // apply new sync settings
+ void resync_l();
+
+ // apply playback settings only - without resyncing or updating playback rate
+ status_t setPlaybackSettings_l(const AudioPlaybackRate &rate);
+
// helper.
bool isPlaying() { return mPlaybackRate != 0.0; }
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index e27ea1d..8e02506 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -24,7 +24,7 @@
namespace android {
struct MediaSource;
-struct MetaData;
+class MetaData;
struct MediaWriter : public RefBase {
MediaWriter()
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 087d016..ca80123 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -75,6 +75,8 @@
kKeyDecoderComponent = 'decC', // cstring
kKeyBufferID = 'bfID',
kKeyMaxInputSize = 'inpS',
+ kKeyMaxWidth = 'maxW',
+ kKeyMaxHeight = 'maxH',
kKeyThumbnailTime = 'thbT', // int64_t (usecs)
kKeyTrackID = 'trID',
kKeyIsDRM = 'idrm', // int32_t (bool)
@@ -98,6 +100,7 @@
kKeyCompilation = 'cpil', // cstring
kKeyLocation = 'loc ', // cstring
kKeyTimeScale = 'tmsl', // int32_t
+ kKeyCaptureFramerate = 'capF', // float (capture fps)
// video profile and level
kKeyVideoProfile = 'vprf', // int32_t
@@ -173,6 +176,9 @@
kKeyTrackIsDefault = 'dflt', // bool (int32_t)
// Similar to MediaFormat.KEY_IS_FORCED_SUBTITLE but pertains to av tracks as well.
kKeyTrackIsForced = 'frcd', // bool (int32_t)
+
+ // H264 supplemental enhancement information offsets/sizes
+ kKeySEI = 'sei ', // raw data
};
enum {
diff --git a/include/media/stagefright/NativeWindowWrapper.h b/include/media/stagefright/NativeWindowWrapper.h
deleted file mode 100644
index cfeec22..0000000
--- a/include/media/stagefright/NativeWindowWrapper.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 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 NATIVE_WINDOW_WRAPPER_H_
-
-#define NATIVE_WINDOW_WRAPPER_H_
-
-#include <gui/Surface.h>
-
-namespace android {
-
-// Surface derives from ANativeWindow which derives from multiple
-// base classes, in order to carry it in AMessages, we'll temporarily wrap it
-// into a NativeWindowWrapper.
-
-struct NativeWindowWrapper : RefBase {
- NativeWindowWrapper(
- const sp<Surface> &surfaceTextureClient) :
- mSurfaceTextureClient(surfaceTextureClient) { }
-
- sp<ANativeWindow> getNativeWindow() const {
- return mSurfaceTextureClient;
- }
-
- sp<Surface> getSurfaceTextureClient() const {
- return mSurfaceTextureClient;
- }
-
-private:
- const sp<Surface> mSurfaceTextureClient;
-
- DISALLOW_EVIL_CONSTRUCTORS(NativeWindowWrapper);
-};
-
-} // namespace android
-
-#endif // NATIVE_WINDOW_WRAPPER_H_
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 402e7f8..fd74452 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -30,12 +30,12 @@
struct ABuffer;
struct AMessage;
-struct DataSource;
+class DataSource;
struct IMediaHTTPService;
-struct MediaBuffer;
+class MediaBuffer;
struct MediaExtractor;
struct MediaSource;
-struct MetaData;
+class MetaData;
struct NuMediaExtractor : public RefBase {
enum SampleFlags {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 84b1b1a..7fabcb3 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -298,7 +298,6 @@
status_t queueBufferToNativeWindow(BufferInfo *info);
status_t cancelBufferToNativeWindow(BufferInfo *info);
BufferInfo* dequeueBufferFromNativeWindow();
- status_t pushBlankBuffersToNativeWindow();
status_t freeBuffersOnPort(
OMX_U32 portIndex, bool onlyThoseWeOwn = false);
@@ -347,7 +346,6 @@
status_t configureCodec(const sp<MetaData> &meta);
- status_t applyRotation();
status_t waitForBufferFilled_l();
int64_t getDecodingTimeUs();
diff --git a/include/media/stagefright/PersistentSurface.h b/include/media/stagefright/PersistentSurface.h
new file mode 100644
index 0000000..a35b9f1
--- /dev/null
+++ b/include/media/stagefright/PersistentSurface.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PERSISTENT_SURFACE_H_
+
+#define PERSISTENT_SURFACE_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct PersistentSurface : public RefBase {
+ PersistentSurface(
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ const sp<IGraphicBufferConsumer>& bufferConsumer) :
+ mBufferProducer(bufferProducer),
+ mBufferConsumer(bufferConsumer) { }
+
+ sp<IGraphicBufferProducer> getBufferProducer() const {
+ return mBufferProducer;
+ }
+
+ sp<IGraphicBufferConsumer> getBufferConsumer() const {
+ return mBufferConsumer;
+ }
+
+private:
+ const sp<IGraphicBufferProducer> mBufferProducer;
+ const sp<IGraphicBufferConsumer> mBufferConsumer;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PersistentSurface);
+};
+
+} // namespace android
+
+#endif // PERSISTENT_SURFACE_H_
diff --git a/include/media/stagefright/SurfaceUtils.h b/include/media/stagefright/SurfaceUtils.h
new file mode 100644
index 0000000..c1a9c0a
--- /dev/null
+++ b/include/media/stagefright/SurfaceUtils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SURFACE_UTILS_H_
+
+#define SURFACE_UTILS_H_
+
+#include <utils/Errors.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+status_t setNativeWindowSizeFormatAndUsage(
+ ANativeWindow *nativeWindow /* nonnull */,
+ int width, int height, int format, int rotation, int usage);
+status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */);
+
+} // namespace android
+
+#endif // SURFACE_UTILS_H_
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
index ec3a10e..5e9d7d4 100644
--- a/include/media/stagefright/Utils.h
+++ b/include/media/stagefright/Utils.h
@@ -41,7 +41,7 @@
uint64_t ntoh64(uint64_t x);
uint64_t hton64(uint64_t x);
-struct MetaData;
+class MetaData;
struct AMessage;
status_t convertMetaDataToMessage(
const sp<MetaData> &meta, sp<AMessage> *format);
@@ -71,11 +71,20 @@
sp<AMessage> mMeta;
HLSTime(const sp<AMessage> &meta = NULL);
- int64_t getSegmentTimeUs(bool midpoint = false) const;
+ int64_t getSegmentTimeUs() const;
};
bool operator <(const HLSTime &t0, const HLSTime &t1);
+// read and write various object to/from AMessage
+
+void writeToAMessage(sp<AMessage> msg, const AudioPlaybackRate &rate);
+void readFromAMessage(const sp<AMessage> &msg, AudioPlaybackRate *rate /* nonnull */);
+
+void writeToAMessage(sp<AMessage> msg, const AVSyncSettings &sync, float videoFpsHint);
+void readFromAMessage(
+ const sp<AMessage> &msg, AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
+
} // namespace android
#endif // UTILS_H_
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
index 1d0e2cb..a97dd9b 100644
--- a/include/media/stagefright/foundation/ADebug.h
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -108,6 +108,26 @@
// remove redundant segments of a codec name, and return a newly allocated
// string suitable for debugging
static char *GetDebugName(const char *name);
+
+ inline static bool isExperimentEnabled(
+ const char *name __unused /* nonnull */, bool allow __unused = true) {
+#ifdef ENABLE_STAGEFRIGHT_EXPERIMENTS
+ if (!strcmp(name, "legacy-adaptive")) {
+ return getExperimentFlag(allow, name, 2, 1); // every other day
+ } else if (!strcmp(name, "legacy-setsurface")) {
+ return getExperimentFlag(allow, name, 3, 1); // every third day
+ } else {
+ ALOGE("unknown experiment '%s' (disabled)", name);
+ }
+#endif
+ return false;
+ }
+
+private:
+ // pass in allow, so we can print in the log if the experiment is disabled
+ static bool getExperimentFlag(
+ bool allow, const char *name, uint64_t modulo, uint64_t limit,
+ uint64_t plus = 0, uint64_t timeDivisor = 24 * 60 * 60 /* 1 day */);
};
} // namespace android
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 4c6bd21..83b9444 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -28,7 +28,7 @@
struct ABuffer;
struct AHandler;
struct AString;
-struct Parcel;
+class Parcel;
struct AReplyToken : public RefBase {
AReplyToken(const sp<ALooper> &looper)
diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
index 822dbb3..2f6d532 100644
--- a/include/media/stagefright/foundation/AString.h
+++ b/include/media/stagefright/foundation/AString.h
@@ -24,7 +24,7 @@
namespace android {
class String8;
-struct Parcel;
+class Parcel;
struct AString {
AString();
diff --git a/include/media/stagefright/foundation/AUtils.h b/include/media/stagefright/foundation/AUtils.h
index d7ecf50..47444c1 100644
--- a/include/media/stagefright/foundation/AUtils.h
+++ b/include/media/stagefright/foundation/AUtils.h
@@ -61,6 +61,28 @@
return a > b ? a : b;
}
+template<class T>
+void ENSURE_UNSIGNED_TYPE() {
+ T TYPE_MUST_BE_UNSIGNED[(T)-1 < 0 ? -1 : 0] __unused;
+}
+
+// needle is in range [hayStart, hayStart + haySize)
+template<class T, class U>
+inline static bool isInRange(const T &hayStart, const U &haySize, const T &needle) {
+ ENSURE_UNSIGNED_TYPE<U>();
+ return (T)(hayStart + haySize) >= hayStart && needle >= hayStart && (U)(needle - hayStart) < haySize;
+}
+
+// [needleStart, needleStart + needleSize) is in range [hayStart, hayStart + haySize)
+template<class T, class U>
+inline static bool isInRange(
+ const T &hayStart, const U &haySize, const T &needleStart, const U &needleSize) {
+ ENSURE_UNSIGNED_TYPE<U>();
+ return isInRange(hayStart, haySize, needleStart)
+ && (T)(needleStart + needleSize) >= needleStart
+ && (U)(needleStart + needleSize - hayStart) <= haySize;
+}
+
/* T must be integer type, period must be positive */
template<class T>
inline static T periodicError(const T &val, const T &period) {
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
index 37ef674..6f7c693 100644
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ b/include/media/stagefright/timedtext/TimedTextDriver.h
@@ -24,7 +24,7 @@
namespace android {
-class ALooper;
+struct ALooper;
struct IMediaHTTPService;
class MediaPlayerBase;
class MediaSource;
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 5644428..1e5064f 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -25,6 +25,7 @@
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <audio_utils/roundup.h>
+#include <media/AudioResamplerPublic.h>
#include <media/SingleStateQueue.h>
namespace android {
@@ -113,6 +114,8 @@
mPosLoopQueue;
};
+typedef SingleStateQueue<AudioPlaybackRate> PlaybackRateQueue;
+
// ----------------------------------------------------------------------------
// Important: do not add any virtual methods, including ~
@@ -159,6 +162,8 @@
uint32_t mSampleRate; // AudioTrack only: client's requested sample rate in Hz
// or 0 == default. Write-only client, read-only server.
+ PlaybackRateQueue::Shared mPlaybackRateQueue;
+
// client write-only, server read-only
uint16_t mSendLevel; // Fixed point U4.12 so 0x1000 means 1.0
@@ -313,7 +318,8 @@
AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false)
: ClientProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/,
- clientInServer) { }
+ clientInServer),
+ mPlaybackRateMutator(&cblk->mPlaybackRateQueue) { }
virtual ~AudioTrackClientProxy() { }
// No barriers on the following operations, so the ordering of loads/stores
@@ -333,6 +339,10 @@
mCblk->mSampleRate = sampleRate;
}
+ void setPlaybackRate(const AudioPlaybackRate& playbackRate) {
+ mPlaybackRateMutator.push(playbackRate);
+ }
+
virtual void flush();
virtual uint32_t getUnderrunFrames() const {
@@ -344,6 +354,9 @@
bool getStreamEndDone() const;
status_t waitStreamEndDone(const struct timespec *requested);
+
+private:
+ PlaybackRateQueue::Mutator mPlaybackRateMutator;
};
class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -458,8 +471,10 @@
public:
AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
- : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) {
+ : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
+ mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
mCblk->mSampleRate = sampleRate;
+ mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
}
protected:
virtual ~AudioTrackServerProxy() { }
@@ -493,6 +508,13 @@
// Return the total number of frames that AudioFlinger has obtained and released
virtual size_t framesReleased() const { return mCblk->mServer; }
+
+ // Return the playback speed and pitch read atomically. Not multi-thread safe on server side.
+ AudioPlaybackRate getPlaybackRate();
+
+private:
+ AudioPlaybackRate mPlaybackRate; // last observed playback rate
+ PlaybackRateQueue::Observer mPlaybackRateObserver;
};
class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 3b260d6..0c18828 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -7,6 +7,9 @@
LOCAL_MODULE:= libmedia_helper
LOCAL_MODULE_TAGS := optional
+LOCAL_C_FLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
@@ -19,6 +22,7 @@
IAudioTrack.cpp \
IAudioRecord.cpp \
ICrypto.cpp \
+ IDataSource.cpp \
IDrm.cpp \
IDrmClient.cpp \
IHDCP.cpp \
@@ -83,5 +87,8 @@
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 7d8222f..bbeb854 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -35,13 +35,14 @@
// ---------------------------------------------------------------------------
-AudioEffect::AudioEffect()
- : mStatus(NO_INIT)
+AudioEffect::AudioEffect(const String16& opPackageName)
+ : mStatus(NO_INIT), mOpPackageName(opPackageName)
{
}
AudioEffect::AudioEffect(const effect_uuid_t *type,
+ const String16& opPackageName,
const effect_uuid_t *uuid,
int32_t priority,
effect_callback_t cbf,
@@ -49,12 +50,13 @@
int sessionId,
audio_io_handle_t io
)
- : mStatus(NO_INIT)
+ : mStatus(NO_INIT), mOpPackageName(opPackageName)
{
mStatus = set(type, uuid, priority, cbf, user, sessionId, io);
}
AudioEffect::AudioEffect(const char *typeStr,
+ const String16& opPackageName,
const char *uuidStr,
int32_t priority,
effect_callback_t cbf,
@@ -62,7 +64,7 @@
int sessionId,
audio_io_handle_t io
)
- : mStatus(NO_INIT)
+ : mStatus(NO_INIT), mOpPackageName(opPackageName)
{
effect_uuid_t type;
effect_uuid_t *pType = NULL;
@@ -128,7 +130,7 @@
mIEffectClient = new EffectClient(this);
iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
- mIEffectClient, priority, io, mSessionId, &mStatus, &mId, &enabled);
+ mIEffectClient, priority, io, mSessionId, mOpPackageName, &mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
diff --git a/media/libmedia/AudioPolicy.cpp b/media/libmedia/AudioPolicy.cpp
index c7dafcb..9d07011 100644
--- a/media/libmedia/AudioPolicy.cpp
+++ b/media/libmedia/AudioPolicy.cpp
@@ -68,6 +68,7 @@
mFormat.format = (audio_format_t)parcel->readInt32();
mRouteFlags = parcel->readInt32();
mRegistrationId = parcel->readString8();
+ mCbFlags = (uint32_t)parcel->readInt32();
size_t size = (size_t)parcel->readInt32();
if (size > MAX_CRITERIA_PER_MIX) {
size = MAX_CRITERIA_PER_MIX;
@@ -89,6 +90,7 @@
parcel->writeInt32(mFormat.format);
parcel->writeInt32(mRouteFlags);
parcel->writeString8(mRegistrationId);
+ parcel->writeInt32(mCbFlags);
size_t size = mCriteria.size();
if (size > MAX_CRITERIA_PER_MIX) {
size = MAX_CRITERIA_PER_MIX;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 100a914..3868f13 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -65,9 +65,10 @@
// ---------------------------------------------------------------------------
-AudioRecord::AudioRecord()
- : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
+AudioRecord::AudioRecord(const String16 &opPackageName)
+ : mStatus(NO_INIT), mOpPackageName(opPackageName), mSessionId(AUDIO_SESSION_ALLOCATE),
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
}
@@ -76,6 +77,7 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ const String16& opPackageName,
size_t frameCount,
callback_t cbf,
void* user,
@@ -83,15 +85,20 @@
int sessionId,
transfer_type transferType,
audio_input_flags_t flags,
+ int uid,
+ pid_t pid,
const audio_attributes_t* pAttributes)
- : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE),
+ : mStatus(NO_INIT),
+ mOpPackageName(opPackageName),
+ mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(NULL)
+ mProxy(NULL),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
- pAttributes);
+ uid, pid, pAttributes);
}
AudioRecord::~AudioRecord()
@@ -107,6 +114,10 @@
mAudioRecordThread->requestExitAndWait();
mAudioRecordThread.clear();
}
+ // No lock here: worst case we remove a NULL callback which will be a nop
+ if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+ }
IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
mAudioRecord.clear();
mCblkMemory.clear();
@@ -131,12 +142,15 @@
int sessionId,
transfer_type transferType,
audio_input_flags_t flags,
+ int uid,
+ pid_t pid,
const audio_attributes_t* pAttributes)
{
ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
- "notificationFrames %u, sessionId %d, transferType %d, flags %#x",
+ "notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s "
+ "uid %d, pid %d",
inputSource, sampleRate, format, channelMask, frameCount, notificationFrames,
- sessionId, transferType, flags);
+ sessionId, transferType, flags, String8(mOpPackageName).string(), uid, pid);
switch (transferType) {
case TRANSFER_DEFAULT:
@@ -189,13 +203,9 @@
}
// validate parameters
- if (!audio_is_valid_format(format)) {
- ALOGE("Invalid format %#x", format);
- return BAD_VALUE;
- }
- // Temporary restriction: AudioFlinger currently supports 16-bit PCM only
- if (format != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("Format %#x is not supported", format);
+ // AudioFlinger capture only supports linear PCM
+ if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
+ ALOGE("Format %#x is not linear pcm", format);
return BAD_VALUE;
}
mFormat = format;
@@ -227,6 +237,19 @@
}
ALOGV("set(): mSessionId %d", mSessionId);
+ int callingpid = IPCThreadState::self()->getCallingPid();
+ int mypid = getpid();
+ if (uid == -1 || (callingpid != mypid)) {
+ mClientUid = IPCThreadState::self()->getCallingUid();
+ } else {
+ mClientUid = uid;
+ }
+ if (pid == -1 || (callingpid != mypid)) {
+ mClientPid = callingpid;
+ } else {
+ mClientPid = pid;
+ }
+
mFlags = flags;
mCbf = cbf;
@@ -237,7 +260,7 @@
}
// create the IAudioRecord
- status_t status = openRecord_l(0 /*epoch*/);
+ status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
if (status != NO_ERROR) {
if (mAudioRecordThread != 0) {
@@ -285,6 +308,8 @@
mNewPosition = mProxy->getPosition() + mUpdatePeriod;
int32_t flags = android_atomic_acquire_load(&mCblk->mFlags);
+ mActive = true;
+
status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
status = mAudioRecord->start(event, triggerSession);
@@ -297,9 +322,9 @@
}
if (status != NO_ERROR) {
+ mActive = false;
ALOGE("start() status %d", status);
} else {
- mActive = true;
sp<AudioRecordThread> t = mAudioRecordThread;
if (t != 0) {
t->resume();
@@ -419,10 +444,38 @@
return AudioSystem::getInputFramesLost(getInputPrivate());
}
+// ---- Explicit Routing ---------------------------------------------------
+status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) {
+ AutoMutex lock(mLock);
+ if (mSelectedDeviceId != deviceId) {
+ mSelectedDeviceId = deviceId;
+ // stop capture so that audio policy manager does not reject the new instance start request
+ // as only one capture can be active at a time.
+ if (mAudioRecord != 0 && mActive) {
+ mAudioRecord->stop();
+ }
+ android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+ }
+ return NO_ERROR;
+}
+
+audio_port_handle_t AudioRecord::getInputDevice() {
+ AutoMutex lock(mLock);
+ return mSelectedDeviceId;
+}
+
+audio_port_handle_t AudioRecord::getRoutedDeviceId() {
+ AutoMutex lock(mLock);
+ if (mInput == AUDIO_IO_HANDLE_NONE) {
+ return AUDIO_PORT_HANDLE_NONE;
+ }
+ return AudioSystem::getDeviceIdForIo(mInput);
+}
+
// -------------------------------------------------------------------------
// must be called with mLock held
-status_t AudioRecord::openRecord_l(size_t epoch)
+status_t AudioRecord::openRecord_l(size_t epoch, const String16& opPackageName)
{
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
if (audioFlinger == 0) {
@@ -462,10 +515,16 @@
}
}
+ if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+ }
+
audio_io_handle_t input;
status_t status = AudioSystem::getInputForAttr(&mAttributes, &input,
(audio_session_t)mSessionId,
- mSampleRate, mFormat, mChannelMask, mFlags);
+ IPCThreadState::self()->getCallingUid(),
+ mSampleRate, mFormat, mChannelMask,
+ mFlags, mSelectedDeviceId);
if (status != NO_ERROR) {
ALOGE("Could not get audio input for record source %d, sample rate %u, format %#x, "
@@ -488,11 +547,14 @@
sp<IMemory> iMem; // for cblk
sp<IMemory> bufferMem;
sp<IAudioRecord> record = audioFlinger->openRecord(input,
- mSampleRate, mFormat,
+ mSampleRate,
+ mFormat,
mChannelMask,
+ opPackageName,
&temp,
&trackFlags,
tid,
+ mClientUid,
&mSessionId,
¬ificationFrames,
iMem,
@@ -589,6 +651,10 @@
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
+ if (mDeviceCallback != 0) {
+ AudioSystem::addAudioDeviceCallback(mDeviceCallback, mInput);
+ }
+
return NO_ERROR;
}
@@ -600,15 +666,21 @@
return status;
}
-status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
+status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig)
{
if (audioBuffer == NULL) {
+ if (nonContig != NULL) {
+ *nonContig = 0;
+ }
return BAD_VALUE;
}
if (mTransfer != TRANSFER_OBTAIN) {
audioBuffer->frameCount = 0;
audioBuffer->size = 0;
audioBuffer->raw = NULL;
+ if (nonContig != NULL) {
+ *nonContig = 0;
+ }
return INVALID_OPERATION;
}
@@ -627,7 +699,7 @@
ALOGE("%s invalid waitCount %d", __func__, waitCount);
requested = NULL;
}
- return obtainBuffer(audioBuffer, requested);
+ return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig);
}
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
@@ -1012,7 +1084,7 @@
// It will also delete the strong references on previous IAudioRecord and IMemory
size_t position = mProxy->getPosition();
mNewPosition = position + mUpdatePeriod;
- status_t result = openRecord_l(position);
+ status_t result = openRecord_l(position, mOpPackageName);
if (result == NO_ERROR) {
if (mActive) {
// callback thread or sync event hasn't changed
@@ -1028,6 +1100,48 @@
return result;
}
+status_t AudioRecord::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+ if (callback == 0) {
+ ALOGW("%s adding NULL callback!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ if (mDeviceCallback == callback) {
+ ALOGW("%s adding same callback!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ status_t status = NO_ERROR;
+ if (mInput != AUDIO_IO_HANDLE_NONE) {
+ if (mDeviceCallback != 0) {
+ ALOGW("%s callback already present!", __FUNCTION__);
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+ }
+ status = AudioSystem::addAudioDeviceCallback(callback, mInput);
+ }
+ mDeviceCallback = callback;
+ return status;
+}
+
+status_t AudioRecord::removeAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+ if (callback == 0) {
+ ALOGW("%s removing NULL callback!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ if (mDeviceCallback != callback) {
+ ALOGW("%s removing different callback!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (mInput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+ }
+ mDeviceCallback = 0;
+ return NO_ERROR;
+}
+
// =========================================================================
void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 9150a94..4c2e77b 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -32,20 +32,12 @@
// client singleton for AudioFlinger binder interface
Mutex AudioSystem::gLock;
-Mutex AudioSystem::gLockCache;
Mutex AudioSystem::gLockAPS;
sp<IAudioFlinger> AudioSystem::gAudioFlinger;
sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
+dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL;
-// Cached values for output handles
-DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(NULL);
-
-// Cached values for recording queries, all protected by gLock
-uint32_t AudioSystem::gPrevInSamplingRate;
-audio_format_t AudioSystem::gPrevInFormat;
-audio_channel_mask_t AudioSystem::gPrevInChannelMask;
-size_t AudioSystem::gInBuffSize = 0; // zero indicates cache is invalid
// establish binder interface to AudioFlinger service
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
@@ -84,6 +76,25 @@
return af;
}
+const sp<AudioSystem::AudioFlingerClient> AudioSystem::getAudioFlingerClient()
+{
+ // calling get_audio_flinger() will initialize gAudioFlingerClient if needed
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return 0;
+ Mutex::Autolock _l(gLock);
+ return gAudioFlingerClient;
+}
+
+sp<AudioIoDescriptor> AudioSystem::getIoDescriptor(audio_io_handle_t ioHandle)
+{
+ sp<AudioIoDescriptor> desc;
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc != 0) {
+ desc = afc->getIoDescriptor(ioHandle);
+ }
+ return desc;
+}
+
/* static */ status_t AudioSystem::checkAudioFlinger()
{
if (defaultServiceManager()->checkService(String16("media.audio_flinger")) != 0) {
@@ -257,18 +268,13 @@
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
-
- Mutex::Autolock _l(gLockCache);
-
- OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
- if (outputDesc == NULL) {
+ sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);
+ if (outputDesc == 0) {
ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output);
- gLockCache.unlock();
*samplingRate = af->sampleRate(output);
- gLockCache.lock();
} else {
ALOGV("getOutputSamplingRate() reading from output desc");
- *samplingRate = outputDesc->samplingRate;
+ *samplingRate = outputDesc->mSamplingRate;
}
if (*samplingRate == 0) {
ALOGE("AudioSystem::getSamplingRate failed for output %d", output);
@@ -301,16 +307,11 @@
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
-
- Mutex::Autolock _l(gLockCache);
-
- OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
- if (outputDesc == NULL) {
- gLockCache.unlock();
+ sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);
+ if (outputDesc == 0) {
*frameCount = af->frameCount(output);
- gLockCache.lock();
} else {
- *frameCount = outputDesc->frameCount;
+ *frameCount = outputDesc->mFrameCount;
}
if (*frameCount == 0) {
ALOGE("AudioSystem::getFrameCount failed for output %d", output);
@@ -343,16 +344,11 @@
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
-
- Mutex::Autolock _l(gLockCache);
-
- OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
- if (outputDesc == NULL) {
- gLockCache.unlock();
+ sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);
+ if (outputDesc == 0) {
*latency = af->latency(output);
- gLockCache.lock();
} else {
- *latency = outputDesc->latency;
+ *latency = outputDesc->mLatency;
}
ALOGV("getLatency() output %d, latency %d", output, *latency);
@@ -363,34 +359,11 @@
status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize)
{
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- return PERMISSION_DENIED;
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc == 0) {
+ return NO_INIT;
}
- Mutex::Autolock _l(gLockCache);
- // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values
- size_t inBuffSize = gInBuffSize;
- if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
- || (channelMask != gPrevInChannelMask)) {
- gLockCache.unlock();
- inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
- gLockCache.lock();
- if (inBuffSize == 0) {
- ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
- sampleRate, format, channelMask);
- return BAD_VALUE;
- }
- // A benign race is possible here: we could overwrite a fresher cache entry
- // save the request params
- gPrevInSamplingRate = sampleRate;
- gPrevInFormat = format;
- gPrevInChannelMask = channelMask;
-
- gInBuffSize = inBuffSize;
- }
- *buffSize = inBuffSize;
-
- return NO_ERROR;
+ return afc->getInputBufferSize(sampleRate, format, channelMask, buffSize);
}
status_t AudioSystem::setVoiceVolume(float value)
@@ -452,6 +425,17 @@
// ---------------------------------------------------------------------------
+
+void AudioSystem::AudioFlingerClient::clearIoCache()
+{
+ Mutex::Autolock _l(mLock);
+ mIoDescriptors.clear();
+ mInBuffSize = 0;
+ mInSamplingRate = 0;
+ mInFormat = AUDIO_FORMAT_DEFAULT;
+ mInChannelMask = AUDIO_CHANNEL_NONE;
+}
+
void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused)
{
audio_error_callback cb = NULL;
@@ -461,11 +445,8 @@
cb = gAudioErrorCallback;
}
- {
- // clear output handles and stream to output map caches
- Mutex::Autolock _l(gLockCache);
- AudioSystem::gOutputs.clear();
- }
+ // clear output handles and stream to output map caches
+ clearIoCache();
if (cb) {
cb(DEAD_OBJECT);
@@ -473,76 +454,189 @@
ALOGW("AudioFlinger server died!");
}
-void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle_t ioHandle,
- const void *param2) {
+void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event event,
+ const sp<AudioIoDescriptor>& ioDesc) {
ALOGV("ioConfigChanged() event %d", event);
- const OutputDescriptor *desc;
- audio_stream_type_t stream;
- if (ioHandle == AUDIO_IO_HANDLE_NONE) return;
+ if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
- Mutex::Autolock _l(AudioSystem::gLockCache);
+ audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+ Vector < sp<AudioDeviceCallback> > callbacks;
- switch (event) {
- case STREAM_CONFIG_CHANGED:
- break;
- case OUTPUT_OPENED: {
- if (gOutputs.indexOfKey(ioHandle) >= 0) {
- ALOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
- break;
- }
- if (param2 == NULL) break;
- desc = (const OutputDescriptor *)param2;
+ {
+ Mutex::Autolock _l(mLock);
- OutputDescriptor *outputDesc = new OutputDescriptor(*desc);
- gOutputs.add(ioHandle, outputDesc);
- ALOGV("ioConfigChanged() new output samplingRate %u, format %#x channel mask %#x "
- "frameCount %zu latency %d",
- outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask,
- outputDesc->frameCount, outputDesc->latency);
+ switch (event) {
+ case AUDIO_OUTPUT_OPENED:
+ case AUDIO_INPUT_OPENED: {
+ if (getIoDescriptor(ioDesc->mIoHandle) != 0) {
+ ALOGV("ioConfigChanged() opening already existing output! %d", ioDesc->mIoHandle);
+ break;
+ }
+ mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
+
+ if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+ deviceId = ioDesc->getDeviceId();
+ ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+ if (ioIndex >= 0) {
+ callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ }
+ }
+ ALOGV("ioConfigChanged() new %s opened %d samplingRate %u, format %#x channel mask %#x "
+ "frameCount %zu deviceId %d", event == AUDIO_OUTPUT_OPENED ? "output" : "input",
+ ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, ioDesc->mChannelMask,
+ ioDesc->mFrameCount, ioDesc->getDeviceId());
+ } break;
+ case AUDIO_OUTPUT_CLOSED:
+ case AUDIO_INPUT_CLOSED: {
+ if (getIoDescriptor(ioDesc->mIoHandle) == 0) {
+ ALOGW("ioConfigChanged() closing unknown %s %d",
+ event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
+ break;
+ }
+ ALOGV("ioConfigChanged() %s %d closed",
+ event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
+
+ mIoDescriptors.removeItem(ioDesc->mIoHandle);
+ mAudioDeviceCallbacks.removeItem(ioDesc->mIoHandle);
+ } break;
+
+ case AUDIO_OUTPUT_CONFIG_CHANGED:
+ case AUDIO_INPUT_CONFIG_CHANGED: {
+ sp<AudioIoDescriptor> oldDesc = getIoDescriptor(ioDesc->mIoHandle);
+ if (oldDesc == 0) {
+ ALOGW("ioConfigChanged() modifying unknown output! %d", ioDesc->mIoHandle);
+ break;
+ }
+
+ deviceId = oldDesc->getDeviceId();
+ mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
+
+ if (deviceId != ioDesc->getDeviceId()) {
+ deviceId = ioDesc->getDeviceId();
+ ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+ if (ioIndex >= 0) {
+ callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ }
+ }
+ ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x "
+ "channel mask %#x frameCount %zu deviceId %d",
+ event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input",
+ ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat,
+ ioDesc->mChannelMask, ioDesc->mFrameCount, ioDesc->getDeviceId());
+
} break;
- case OUTPUT_CLOSED: {
- if (gOutputs.indexOfKey(ioHandle) < 0) {
- ALOGW("ioConfigChanged() closing unknown output! %d", ioHandle);
- break;
}
- ALOGV("ioConfigChanged() output %d closed", ioHandle);
-
- gOutputs.removeItem(ioHandle);
- } break;
-
- case OUTPUT_CONFIG_CHANGED: {
- int index = gOutputs.indexOfKey(ioHandle);
- if (index < 0) {
- ALOGW("ioConfigChanged() modifying unknown output! %d", ioHandle);
- break;
- }
- if (param2 == NULL) break;
- desc = (const OutputDescriptor *)param2;
-
- ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %#x "
- "channel mask %#x frameCount %zu latency %d",
- ioHandle, desc->samplingRate, desc->format,
- desc->channelMask, desc->frameCount, desc->latency);
- OutputDescriptor *outputDesc = gOutputs.valueAt(index);
- delete outputDesc;
- outputDesc = new OutputDescriptor(*desc);
- gOutputs.replaceValueFor(ioHandle, outputDesc);
- } break;
- case INPUT_OPENED:
- case INPUT_CLOSED:
- case INPUT_CONFIG_CHANGED:
- break;
-
+ }
+ // callbacks.size() != 0 => ioDesc->mIoHandle and deviceId are valid
+ for (size_t i = 0; i < callbacks.size(); i++) {
+ callbacks[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
}
}
-void AudioSystem::setErrorCallback(audio_error_callback cb)
+status_t AudioSystem::AudioFlingerClient::getInputBufferSize(
+ uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask, size_t* buffSize)
+{
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ return PERMISSION_DENIED;
+ }
+ Mutex::Autolock _l(mLock);
+ // Do we have a stale mInBuffSize or are we requesting the input buffer size for new values
+ if ((mInBuffSize == 0) || (sampleRate != mInSamplingRate) || (format != mInFormat)
+ || (channelMask != mInChannelMask)) {
+ size_t inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
+ if (inBuffSize == 0) {
+ ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
+ sampleRate, format, channelMask);
+ return BAD_VALUE;
+ }
+ // A benign race is possible here: we could overwrite a fresher cache entry
+ // save the request params
+ mInSamplingRate = sampleRate;
+ mInFormat = format;
+ mInChannelMask = channelMask;
+
+ mInBuffSize = inBuffSize;
+ }
+
+ *buffSize = mInBuffSize;
+
+ return NO_ERROR;
+}
+
+sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_handle_t ioHandle)
+{
+ sp<AudioIoDescriptor> desc;
+ ssize_t index = mIoDescriptors.indexOfKey(ioHandle);
+ if (index >= 0) {
+ desc = mIoDescriptors.valueAt(index);
+ }
+ return desc;
+}
+
+status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
+ const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+ Mutex::Autolock _l(mLock);
+ Vector < sp<AudioDeviceCallback> > callbacks;
+ ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
+ if (ioIndex >= 0) {
+ callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ }
+
+ for (size_t cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
+ if (callbacks[cbIndex] == callback) {
+ return INVALID_OPERATION;
+ }
+ }
+ callbacks.add(callback);
+
+ mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
+ return NO_ERROR;
+}
+
+status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
+ const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+ Mutex::Autolock _l(mLock);
+ ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
+ if (ioIndex < 0) {
+ return INVALID_OPERATION;
+ }
+ Vector < sp<AudioDeviceCallback> > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+
+ size_t cbIndex;
+ for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
+ if (callbacks[cbIndex] == callback) {
+ break;
+ }
+ }
+ if (cbIndex == callbacks.size()) {
+ return INVALID_OPERATION;
+ }
+ callbacks.removeAt(cbIndex);
+ if (callbacks.size() != 0) {
+ mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
+ } else {
+ mAudioDeviceCallbacks.removeItem(audioIo);
+ }
+ return NO_ERROR;
+}
+
+/* static */ void AudioSystem::setErrorCallback(audio_error_callback cb)
{
Mutex::Autolock _l(gLock);
gAudioErrorCallback = cb;
}
+/*static*/ void AudioSystem::setDynPolicyCallback(dynamic_policy_callback cb)
+{
+ Mutex::Autolock _l(gLock);
+ gDynPolicyCallback = cb;
+}
+
// client singleton for AudioPolicyService binder interface
// protected by gLockAPS
sp<IAudioPolicyService> AudioSystem::gAudioPolicyService;
@@ -654,17 +748,19 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
- return aps->getOutputForAttr(attr, output, session, stream,
+ return aps->getOutputForAttr(attr, output, session, stream, uid,
samplingRate, format, channelMask,
- flags, offloadInfo);
+ flags, selectedDeviceId, offloadInfo);
}
status_t AudioSystem::startOutput(audio_io_handle_t output,
@@ -697,14 +793,17 @@
status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_input_flags_t flags)
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
- return aps->getInputForAttr(attr, input, session, samplingRate, format, channelMask, flags);
+ return aps->getInputForAttr(
+ attr, input, session, uid, samplingRate, format, channelMask, flags, selectedDeviceId);
}
status_t AudioSystem::startInput(audio_io_handle_t input,
@@ -859,11 +958,10 @@
// called by restoreTrack_l(), which needs new IAudioFlinger and IAudioPolicyService instances
ALOGV("clearAudioConfigCache()");
{
- Mutex::Autolock _l(gLockCache);
- gOutputs.clear();
- }
- {
Mutex::Autolock _l(gLock);
+ if (gAudioFlingerClient != 0) {
+ gAudioFlingerClient->clearIoCache();
+ }
gAudioFlinger.clear();
}
{
@@ -929,7 +1027,7 @@
return aps->setAudioPortConfig(config);
}
-status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callBack)
+status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callback)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -938,10 +1036,11 @@
if (gAudioPolicyServiceClient == 0) {
return NO_INIT;
}
- return gAudioPolicyServiceClient->addAudioPortCallback(callBack);
+ return gAudioPolicyServiceClient->addAudioPortCallback(callback);
}
-status_t AudioSystem::removeAudioPortCallback(const sp<AudioPortCallback>& callBack)
+/*static*/
+status_t AudioSystem::removeAudioPortCallback(const sp<AudioPortCallback>& callback)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -950,9 +1049,39 @@
if (gAudioPolicyServiceClient == 0) {
return NO_INIT;
}
- return gAudioPolicyServiceClient->removeAudioPortCallback(callBack);
+ return gAudioPolicyServiceClient->removeAudioPortCallback(callback);
}
+status_t AudioSystem::addAudioDeviceCallback(
+ const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc == 0) {
+ return NO_INIT;
+ }
+ return afc->addAudioDeviceCallback(callback, audioIo);
+}
+
+status_t AudioSystem::removeAudioDeviceCallback(
+ const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc == 0) {
+ return NO_INIT;
+ }
+ return afc->removeAudioDeviceCallback(callback, audioIo);
+}
+
+audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo)
+{
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+ const sp<AudioIoDescriptor> desc = getIoDescriptor(audioIo);
+ if (desc == 0) {
+ return AUDIO_PORT_HANDLE_NONE;
+ }
+ return desc->getDeviceId();
+}
status_t AudioSystem::acquireSoundTriggerSession(audio_session_t *session,
audio_io_handle_t *ioHandle,
@@ -984,28 +1113,44 @@
return aps->registerPolicyMixes(mixes, registration);
}
+status_t AudioSystem::startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->startAudioSource(source, attributes, handle);
+}
+
+status_t AudioSystem::stopAudioSource(audio_io_handle_t handle)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->stopAudioSource(handle);
+}
+
// ---------------------------------------------------------------------------
status_t AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(
- const sp<AudioPortCallback>& callBack)
+ const sp<AudioPortCallback>& callback)
{
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) {
- if (mAudioPortCallbacks[i] == callBack) {
+ if (mAudioPortCallbacks[i] == callback) {
return INVALID_OPERATION;
}
}
- mAudioPortCallbacks.add(callBack);
+ mAudioPortCallbacks.add(callback);
return NO_ERROR;
}
status_t AudioSystem::AudioPolicyServiceClient::removeAudioPortCallback(
- const sp<AudioPortCallback>& callBack)
+ const sp<AudioPortCallback>& callback)
{
Mutex::Autolock _l(mLock);
size_t i;
for (i = 0; i < mAudioPortCallbacks.size(); i++) {
- if (mAudioPortCallbacks[i] == callBack) {
+ if (mAudioPortCallbacks[i] == callback) {
break;
}
}
@@ -1016,6 +1161,7 @@
return NO_ERROR;
}
+
void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate()
{
Mutex::Autolock _l(mLock);
@@ -1032,6 +1178,21 @@
}
}
+void AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(
+ String8 regId, int32_t state)
+{
+ ALOGV("AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(%s, %d)", regId.string(), state);
+ dynamic_policy_callback cb = NULL;
+ {
+ Mutex::Autolock _l(AudioSystem::gLock);
+ cb = gDynPolicyCallback;
+ }
+
+ if (cb != NULL) {
+ cb(DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE, regId, state);
+ }
+}
+
void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
{
{
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index ce30c62..db316b0 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -56,6 +56,42 @@
return convertTimespecToUs(tv);
}
+// FIXME: we don't use the pitch setting in the time stretcher (not working);
+// instead we emulate it using our sample rate converter.
+static const bool kFixPitch = true; // enable pitch fix
+static inline uint32_t adjustSampleRate(uint32_t sampleRate, float pitch)
+{
+ return kFixPitch ? (sampleRate * pitch + 0.5) : sampleRate;
+}
+
+static inline float adjustSpeed(float speed, float pitch)
+{
+ return kFixPitch ? (speed / pitch) : speed;
+}
+
+static inline float adjustPitch(float pitch)
+{
+ return kFixPitch ? AUDIO_TIMESTRETCH_PITCH_NORMAL : pitch;
+}
+
+// Must match similar computation in createTrack_l in Threads.cpp.
+// TODO: Move to a common library
+static size_t calculateMinFrameCount(
+ uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
+ uint32_t sampleRate, float speed)
+{
+ // Ensure that buffer depth covers at least audio hardware latency
+ uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
+ ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u "
+ "sampleRate %u speed %f minBufCount: %u",
+ afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
+ return minBufCount * sourceFramesNeededWithTimestretch(
+ sampleRate, afFrameCount, afSampleRate, speed);
+}
+
// static
status_t AudioTrack::getMinFrameCount(
size_t* frameCount,
@@ -94,13 +130,10 @@
return status;
}
- // Ensure that buffer depth covers at least audio hardware latency
- uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
- if (minBufCount < 2) {
- minBufCount = 2;
- }
+ // When called from createTrack, speed is 1.0f (normal speed).
+ // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
+ *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);
- *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate);
// The formula above should always produce a non-zero value under normal circumstances:
// AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
// Return error in the unlikely event that it does not, as that's part of the API contract.
@@ -109,8 +142,8 @@
streamType, sampleRate);
return BAD_VALUE;
}
- ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u",
- *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency);
+ ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
+ *frameCount, afFrameCount, afSampleRate, afLatency);
return NO_ERROR;
}
@@ -121,7 +154,8 @@
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0)
+ mPausedPosition(0),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
mAttributes.usage = AUDIO_USAGE_UNKNOWN;
@@ -149,7 +183,8 @@
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0)
+ mPausedPosition(0),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
@@ -177,7 +212,8 @@
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0)
+ mPausedPosition(0),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
mStatus = set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
@@ -198,6 +234,10 @@
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
}
+ // No lock here: worst case we remove a NULL callback which will be a nop
+ if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+ }
IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
mAudioTrack.clear();
mCblkMemory.clear();
@@ -357,6 +397,8 @@
return BAD_VALUE;
}
mSampleRate = sampleRate;
+ mOriginalSampleRate = sampleRate;
+ mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// Make copy of input parameter offloadInfo so that in the future:
// (a) createTrack_l doesn't need it as an input parameter
@@ -433,6 +475,7 @@
mSequence = 1;
mObservedSequence = mSequence;
mInUnderrun = false;
+ mPreviousTimestampValid = false;
return NO_ERROR;
}
@@ -459,6 +502,8 @@
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mPosition = 0;
+ mPreviousTimestampValid = false;
+
// For offloaded tracks, we don't know if the hardware counters are really zero here,
// since the flush is asynchronous and stop may not fully drain.
// We save the time when the track is started to later verify whether
@@ -683,12 +728,15 @@
if (AudioSystem::getSamplingRate(mOutput, &afSamplingRate) != NO_ERROR) {
return NO_INIT;
}
- if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
+ // pitch is emulated by adjusting speed and sampleRate
+ const uint32_t effectiveSampleRate = adjustSampleRate(rate, mPlaybackRate.mPitch);
+ if (rate == 0 || effectiveSampleRate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
return BAD_VALUE;
}
+ // TODO: Should we also check if the buffer size is compatible?
mSampleRate = rate;
- mProxy->setSampleRate(rate);
+ mProxy->setSampleRate(effectiveSampleRate);
return NO_ERROR;
}
@@ -716,6 +764,61 @@
return mSampleRate;
}
+uint32_t AudioTrack::getOriginalSampleRate() const
+{
+ if (mIsTimed) {
+ return 0;
+ }
+
+ return mOriginalSampleRate;
+}
+
+status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate)
+{
+ AutoMutex lock(mLock);
+ if (isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
+ return NO_ERROR;
+ }
+ if (mIsTimed || isOffloadedOrDirect_l()) {
+ return INVALID_OPERATION;
+ }
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+ return INVALID_OPERATION;
+ }
+ // pitch is emulated by adjusting speed and sampleRate
+ const uint32_t effectiveRate = adjustSampleRate(mSampleRate, playbackRate.mPitch);
+ const float effectiveSpeed = adjustSpeed(playbackRate.mSpeed, playbackRate.mPitch);
+ const float effectivePitch = adjustPitch(playbackRate.mPitch);
+ if (effectiveSpeed < AUDIO_TIMESTRETCH_SPEED_MIN
+ || effectiveSpeed > AUDIO_TIMESTRETCH_SPEED_MAX
+ || effectivePitch < AUDIO_TIMESTRETCH_PITCH_MIN
+ || effectivePitch > AUDIO_TIMESTRETCH_PITCH_MAX) {
+ return BAD_VALUE;
+ //TODO: add function in AudioResamplerPublic.h to check for validity.
+ }
+ // Check if the buffer size is compatible.
+ if (!isSampleRateSpeedAllowed_l(effectiveRate, effectiveSpeed)) {
+ ALOGV("setPlaybackRate(%f, %f) failed", playbackRate.mSpeed, playbackRate.mPitch);
+ return BAD_VALUE;
+ }
+ mPlaybackRate = playbackRate;
+ mProxy->setPlaybackRate(playbackRate);
+
+ //modify this
+ AudioPlaybackRate playbackRateTemp = playbackRate;
+ playbackRateTemp.mSpeed = effectiveSpeed;
+ playbackRateTemp.mPitch = effectivePitch;
+ mProxy->setPlaybackRate(playbackRateTemp);
+ mProxy->setSampleRate(effectiveRate); // FIXME: not quite "atomic" with setPlaybackRate
+ return NO_ERROR;
+}
+
+const AudioPlaybackRate& AudioTrack::getPlaybackRate() const
+{
+ AutoMutex lock(mLock);
+ return mPlaybackRate;
+}
+
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
@@ -909,6 +1012,7 @@
mNewPosition = mUpdatePeriod;
(void) updateAndGetPosition_l();
mPosition = 0;
+ mPreviousTimestampValid = false;
#if 0
// The documentation is not clear on the behavior of reload() and the restoration
// of loop count. Historically we have not restored loop count, start, end,
@@ -928,6 +1032,28 @@
return mOutput;
}
+status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+ AutoMutex lock(mLock);
+ if (mSelectedDeviceId != deviceId) {
+ mSelectedDeviceId = deviceId;
+ android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+ }
+ return NO_ERROR;
+}
+
+audio_port_handle_t AudioTrack::getOutputDevice() {
+ AutoMutex lock(mLock);
+ return mSelectedDeviceId;
+}
+
+audio_port_handle_t AudioTrack::getRoutedDeviceId() {
+ AutoMutex lock(mLock);
+ if (mOutput == AUDIO_IO_HANDLE_NONE) {
+ return AUDIO_PORT_HANDLE_NONE;
+ }
+ return AudioSystem::getDeviceIdForIo(mOutput);
+}
+
status_t AudioTrack::attachAuxEffect(int effectId)
{
AutoMutex lock(mLock);
@@ -957,14 +1083,18 @@
return NO_INIT;
}
+ if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+ }
audio_io_handle_t output;
audio_stream_type_t streamType = mStreamType;
audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL;
- status_t status = AudioSystem::getOutputForAttr(attr, &output,
- (audio_session_t)mSessionId, &streamType,
- mSampleRate, mFormat, mChannelMask,
- mFlags, mOffloadInfo);
+ status_t status;
+ status = AudioSystem::getOutputForAttr(attr, &output,
+ (audio_session_t)mSessionId, &streamType, mClientUid,
+ mSampleRate, mFormat, mChannelMask,
+ mFlags, mSelectedDeviceId, mOffloadInfo);
if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) {
ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u, format %#x,"
@@ -1001,6 +1131,7 @@
}
if (mSampleRate == 0) {
mSampleRate = afSampleRate;
+ mOriginalSampleRate = afSampleRate;
}
// Client decides whether the track is TIMED (see below), but can only express a preference
// for FAST. Server will perform additional tests.
@@ -1067,8 +1198,17 @@
// there _is_ a frameCount parameter. We silently ignore it.
frameCount = mSharedBuffer->size() / mFrameSize;
} else {
- // For fast and normal streaming tracks,
- // the frame count calculations and checks are done by server
+ // For fast tracks the frame count calculations and checks are done by server
+
+ if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+ // for normal tracks precompute the frame count based on speed.
+ const size_t minFrameCount = calculateMinFrameCount(
+ afLatency, afFrameCount, afSampleRate, mSampleRate,
+ mPlaybackRate.mSpeed);
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
+ }
+ }
}
IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
@@ -1211,6 +1351,7 @@
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
+ // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack)
// FIXME don't believe this lie
mLatency = afLatency + (1000*frameCount) / mSampleRate;
@@ -1235,12 +1376,24 @@
gain_from_float(mVolume[AUDIO_INTERLEAVE_RIGHT])));
mProxy->setSendLevel(mSendLevel);
- mProxy->setSampleRate(mSampleRate);
+ const uint32_t effectiveSampleRate = adjustSampleRate(mSampleRate, mPlaybackRate.mPitch);
+ const float effectiveSpeed = adjustSpeed(mPlaybackRate.mSpeed, mPlaybackRate.mPitch);
+ const float effectivePitch = adjustPitch(mPlaybackRate.mPitch);
+ mProxy->setSampleRate(effectiveSampleRate);
+
+ AudioPlaybackRate playbackRateTemp = mPlaybackRate;
+ playbackRateTemp.mSpeed = effectiveSpeed;
+ playbackRateTemp.mPitch = effectivePitch;
+ mProxy->setPlaybackRate(playbackRateTemp);
mProxy->setMinimum(mNotificationFramesAct);
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this);
+ if (mDeviceCallback != 0) {
+ AudioSystem::addAudioDeviceCallback(mDeviceCallback, mOutput);
+ }
+
return NO_ERROR;
}
@@ -1255,12 +1408,18 @@
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig)
{
if (audioBuffer == NULL) {
+ if (nonContig != NULL) {
+ *nonContig = 0;
+ }
return BAD_VALUE;
}
if (mTransfer != TRANSFER_OBTAIN) {
audioBuffer->frameCount = 0;
audioBuffer->size = 0;
audioBuffer->raw = NULL;
+ if (nonContig != NULL) {
+ *nonContig = 0;
+ }
return INVALID_OPERATION;
}
@@ -1546,7 +1705,8 @@
// AudioSystem cache. We should not exit here but after calling the callback so
// that the upper layers can recreate the track
if (!isOffloadedOrDirect_l() || (mSequence == mObservedSequence)) {
- status_t status = restoreTrack_l("processAudioBuffer");
+ status_t status __unused = restoreTrack_l("processAudioBuffer");
+ // FIXME unused status
// after restoration, continue below to make sure that the loop and buffer events
// are notified because they have been cleared from mCblk->mFlags above.
}
@@ -1598,6 +1758,7 @@
// Cache other fields that will be needed soon
uint32_t sampleRate = mSampleRate;
+ float speed = mPlaybackRate.mSpeed;
uint32_t notificationFrames = mNotificationFramesAct;
if (mRefreshRemaining) {
mRefreshRemaining = false;
@@ -1726,7 +1887,7 @@
if (minFrames != (uint32_t) ~0) {
// This "fudge factor" avoids soaking CPU, and compensates for late progress by server
static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
- ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs;
+ ns = ((double)minFrames * 1000000000) / ((double)sampleRate * speed) + kFudgeNs;
}
// If not supplying data by EVENT_MORE_DATA, then we're done
@@ -1767,7 +1928,8 @@
if (mRetryOnPartialBuffer && !isOffloaded()) {
mRetryOnPartialBuffer = false;
if (avail < mRemainingFrames) {
- int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
+ int64_t myns = ((double)(mRemainingFrames - avail) * 1100000000)
+ / ((double)sampleRate * speed);
if (ns < 0 || myns < ns) {
ns = myns;
}
@@ -1822,7 +1984,7 @@
// that total to a sum == notificationFrames.
if (0 < misalignment && misalignment <= mRemainingFrames) {
mRemainingFrames = misalignment;
- return (mRemainingFrames * 1100000000LL) / sampleRate;
+ return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed);
}
#endif
@@ -1917,6 +2079,41 @@
return mPosition += (uint32_t) delta;
}
+bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const
+{
+ // applicable for mixing tracks only (not offloaded or direct)
+ if (mStaticProxy != 0) {
+ return true; // static tracks do not have issues with buffer sizing.
+ }
+ status_t status;
+ uint32_t afLatency;
+ status = AudioSystem::getLatency(mOutput, &afLatency);
+ if (status != NO_ERROR) {
+ ALOGE("getLatency(%d) failed status %d", mOutput, status);
+ return false;
+ }
+
+ size_t afFrameCount;
+ status = AudioSystem::getFrameCount(mOutput, &afFrameCount);
+ if (status != NO_ERROR) {
+ ALOGE("getFrameCount(output=%d) status %d", mOutput, status);
+ return false;
+ }
+
+ uint32_t afSampleRate;
+ status = AudioSystem::getSamplingRate(mOutput, &afSampleRate);
+ if (status != NO_ERROR) {
+ ALOGE("getSamplingRate(output=%d) status %d", mOutput, status);
+ return false;
+ }
+
+ const size_t minFrameCount =
+ calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, speed);
+ ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu",
+ mFrameCount, minFrameCount);
+ return mFrameCount >= minFrameCount;
+}
+
status_t AudioTrack::setParameters(const String8& keyValuePairs)
{
AutoMutex lock(mLock);
@@ -1926,6 +2123,11 @@
status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
{
AutoMutex lock(mLock);
+
+ bool previousTimestampValid = mPreviousTimestampValid;
+ // Set false here to cover all the error return cases.
+ mPreviousTimestampValid = false;
+
// FIXME not implemented for fast tracks; should use proxy and SSQ
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
return INVALID_OPERATION;
@@ -1982,7 +2184,8 @@
return WOULD_BLOCK; // stale timestamp time, occurs before start.
}
const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
- const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate;
+ const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
+ / ((double)mSampleRate * mPlaybackRate.mSpeed);
if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) {
// Verify that the counter can't count faster than the sample rate
@@ -2023,6 +2226,46 @@
// IAudioTrack. And timestamp.mPosition is initially in server's
// point of view, so we need to apply the same fudge factor to it.
}
+
+ // Prevent retrograde motion in timestamp.
+ // This is sometimes caused by erratic reports of the available space in the ALSA drivers.
+ if (status == NO_ERROR) {
+ if (previousTimestampValid) {
+#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec)
+ const uint64_t previousTimeNanos = TIME_TO_NANOS(mPreviousTimestamp.mTime);
+ const uint64_t currentTimeNanos = TIME_TO_NANOS(timestamp.mTime);
+#undef TIME_TO_NANOS
+ if (currentTimeNanos < previousTimeNanos) {
+ ALOGW("retrograde timestamp time");
+ // FIXME Consider blocking this from propagating upwards.
+ }
+
+ // Looking at signed delta will work even when the timestamps
+ // are wrapping around.
+ int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition
+ - mPreviousTimestamp.mPosition);
+ // position can bobble slightly as an artifact; this hides the bobble
+ static const int32_t MINIMUM_POSITION_DELTA = 8;
+ if (deltaPosition < 0) {
+ // Only report once per position instead of spamming the log.
+ if (!mRetrogradeMotionReported) {
+ ALOGW("retrograde timestamp position corrected, %d = %u - %u",
+ deltaPosition,
+ timestamp.mPosition,
+ mPreviousTimestamp.mPosition);
+ mRetrogradeMotionReported = true;
+ }
+ } else {
+ mRetrogradeMotionReported = false;
+ }
+ if (deltaPosition < MINIMUM_POSITION_DELTA) {
+ timestamp = mPreviousTimestamp; // Use last valid timestamp.
+ }
+ }
+ mPreviousTimestamp = timestamp;
+ mPreviousTimestampValid = true;
+ }
+
return status;
}
@@ -2069,7 +2312,8 @@
snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat,
mChannelCount, mFrameCount);
result.append(buffer);
- snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
+ snprintf(buffer, 255, " sample rate(%u), speed(%f), status(%d)\n",
+ mSampleRate, mPlaybackRate.mSpeed, mStatus);
result.append(buffer);
snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency);
result.append(buffer);
@@ -2083,6 +2327,48 @@
return mProxy->getUnderrunFrames();
}
+status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+ if (callback == 0) {
+ ALOGW("%s adding NULL callback!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ if (mDeviceCallback == callback) {
+ ALOGW("%s adding same callback!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ status_t status = NO_ERROR;
+ if (mOutput != AUDIO_IO_HANDLE_NONE) {
+ if (mDeviceCallback != 0) {
+ ALOGW("%s callback already present!", __FUNCTION__);
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+ }
+ status = AudioSystem::addAudioDeviceCallback(callback, mOutput);
+ }
+ mDeviceCallback = callback;
+ return status;
+}
+
+status_t AudioTrack::removeAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+ if (callback == 0) {
+ ALOGW("%s removing NULL callback!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ if (mDeviceCallback != callback) {
+ ALOGW("%s removing different callback!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (mOutput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+ }
+ mDeviceCallback = 0;
+ return NO_ERROR;
+}
+
// =========================================================================
void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 6d5f1af..1d7aed2 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -619,8 +619,9 @@
// Rather than shutting down on a corrupt flush, just treat it as a full flush
if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, "
- "filled %d=%#x",
- mFlush, flush, front, rear, mask, newFront, filled, filled);
+ "filled %zd=%#x",
+ mFlush, flush, front, rear,
+ (unsigned)mask, newFront, filled, (unsigned)filled);
newFront = rear;
}
mFlush = flush;
@@ -793,6 +794,12 @@
(void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
}
+AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate()
+{ // do not call from multiple threads without holding lock
+ mPlaybackRateObserver.poll(mPlaybackRate);
+ return mPlaybackRate;
+}
+
// ---------------------------------------------------------------------------
StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp
index 41994dc..3020136 100644
--- a/media/libmedia/CharacterEncodingDetector.cpp
+++ b/media/libmedia/CharacterEncodingDetector.cpp
@@ -89,7 +89,6 @@
// try combined detection of artist/album/title etc.
char buf[1024];
buf[0] = 0;
- int idx;
bool allprintable = true;
for (int i = 0; i < size; i++) {
const char *name = mNames.getEntry(i);
@@ -169,7 +168,6 @@
const char *name = mNames.getEntry(i);
uint8_t* src = (uint8_t *)mValues.getEntry(i);
int len = strlen((char *)src);
- uint8_t* dest = src;
ALOGV("@@@ checking %s", name);
const char *s = mValues.getEntry(i);
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 38055f9..d722fe9 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -174,9 +174,11 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ const String16& opPackageName,
size_t *pFrameCount,
track_flags_t *flags,
pid_t tid,
+ int clientUid,
int *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
@@ -190,11 +192,13 @@
data.writeInt32(sampleRate);
data.writeInt32(format);
data.writeInt32(channelMask);
+ data.writeString16(opPackageName);
size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0;
data.writeInt64(frameCount);
track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT;
data.writeInt32(lFlags);
data.writeInt32((int32_t) tid);
+ data.writeInt32((int32_t) clientUid);
int lSessionId = AUDIO_SESSION_ALLOCATE;
if (sessionId != NULL) {
lSessionId = *sessionId;
@@ -702,6 +706,7 @@
int32_t priority,
audio_io_handle_t output,
int sessionId,
+ const String16& opPackageName,
status_t *status,
int *id,
int *enabled)
@@ -722,6 +727,7 @@
data.writeInt32(priority);
data.writeInt32((int32_t) output);
data.writeInt32(sessionId);
+ data.writeString16(opPackageName);
status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
if (lStatus != NO_ERROR) {
@@ -950,18 +956,19 @@
uint32_t sampleRate = data.readInt32();
audio_format_t format = (audio_format_t) data.readInt32();
audio_channel_mask_t channelMask = data.readInt32();
+ const String16& opPackageName = data.readString16();
size_t frameCount = data.readInt64();
track_flags_t flags = (track_flags_t) data.readInt32();
pid_t tid = (pid_t) data.readInt32();
+ int clientUid = data.readInt32();
int sessionId = data.readInt32();
size_t notificationFrames = data.readInt64();
sp<IMemory> cblk;
sp<IMemory> buffers;
status_t status;
sp<IAudioRecord> record = openRecord(input,
- sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId,
- ¬ificationFrames,
- cblk, buffers, &status);
+ sampleRate, format, channelMask, opPackageName, &frameCount, &flags, tid,
+ clientUid, &sessionId, ¬ificationFrames, cblk, buffers, &status);
LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
reply->writeInt64(frameCount);
reply->writeInt32(flags);
@@ -1247,12 +1254,13 @@
int32_t priority = data.readInt32();
audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
int sessionId = data.readInt32();
+ const String16 opPackageName = data.readString16();
status_t status;
int id;
int enabled;
sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId,
- &status, &id, &enabled);
+ opPackageName, &status, &id, &enabled);
reply->writeInt32(status);
reply->writeInt32(id);
reply->writeInt32(enabled);
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 641e6c1..3429d36 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -39,25 +39,18 @@
{
}
- void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2)
+ void ioConfigChanged(audio_io_config_event event, const sp<AudioIoDescriptor>& ioDesc)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
data.writeInt32(event);
- data.writeInt32((int32_t) ioHandle);
- if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
- uint32_t stream = *(const uint32_t *)param2;
- ALOGV("ioConfigChanged stream %d", stream);
- data.writeInt32(stream);
- } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
- const AudioSystem::OutputDescriptor *desc =
- (const AudioSystem::OutputDescriptor *)param2;
- data.writeInt32(desc->samplingRate);
- data.writeInt32(desc->format);
- data.writeInt32(desc->channelMask);
- data.writeInt64(desc->frameCount);
- data.writeInt32(desc->latency);
- }
+ data.writeInt32((int32_t)ioDesc->mIoHandle);
+ data.write(&ioDesc->mPatch, sizeof(struct audio_patch));
+ data.writeInt32(ioDesc->mSamplingRate);
+ data.writeInt32(ioDesc->mFormat);
+ data.writeInt32(ioDesc->mChannelMask);
+ data.writeInt64(ioDesc->mFrameCount);
+ data.writeInt32(ioDesc->mLatency);
remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -72,24 +65,16 @@
switch (code) {
case IO_CONFIG_CHANGED: {
CHECK_INTERFACE(IAudioFlingerClient, data, reply);
- int event = data.readInt32();
- audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
- const void *param2 = NULL;
- AudioSystem::OutputDescriptor desc;
- uint32_t stream;
- if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
- stream = data.readInt32();
- param2 = &stream;
- ALOGV("STREAM_CONFIG_CHANGED stream %d", stream);
- } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
- desc.samplingRate = data.readInt32();
- desc.format = (audio_format_t) data.readInt32();
- desc.channelMask = (audio_channel_mask_t) data.readInt32();
- desc.frameCount = data.readInt64();
- desc.latency = data.readInt32();
- param2 = &desc;
- }
- ioConfigChanged(event, ioHandle, param2);
+ audio_io_config_event event = (audio_io_config_event)data.readInt32();
+ sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
+ ioDesc->mIoHandle = (audio_io_handle_t) data.readInt32();
+ data.read(&ioDesc->mPatch, sizeof(struct audio_patch));
+ ioDesc->mSamplingRate = data.readInt32();
+ ioDesc->mFormat = (audio_format_t) data.readInt32();
+ ioDesc->mChannelMask = (audio_channel_mask_t) data.readInt32();
+ ioDesc->mFrameCount = data.readInt64();
+ ioDesc->mLatency = data.readInt32();
+ ioConfigChanged(event, ioDesc);
return NO_ERROR;
} break;
default:
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 39374d8..fd18f17 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -71,6 +71,8 @@
RELEASE_SOUNDTRIGGER_SESSION,
GET_PHONE_STATE,
REGISTER_POLICY_MIXES,
+ START_AUDIO_SOURCE,
+ STOP_AUDIO_SOURCE
};
#define MAX_ITEMS_PER_LIST 1024
@@ -169,10 +171,12 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
Parcel data, reply;
@@ -204,10 +208,12 @@
data.writeInt32(1);
data.writeInt32(*stream);
}
+ data.writeInt32(uid);
data.writeInt32(samplingRate);
data.writeInt32(static_cast <uint32_t>(format));
data.writeInt32(channelMask);
data.writeInt32(static_cast <uint32_t>(flags));
+ data.writeInt32(selectedDeviceId);
// hasOffloadInfo
if (offloadInfo == NULL) {
data.writeInt32(0);
@@ -271,10 +277,12 @@
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_input_flags_t flags)
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -288,10 +296,12 @@
}
data.write(attr, sizeof(audio_attributes_t));
data.writeInt32(session);
+ data.writeInt32(uid);
data.writeInt32(samplingRate);
data.writeInt32(static_cast <uint32_t>(format));
data.writeInt32(channelMask);
data.writeInt32(flags);
+ data.writeInt32(selectedDeviceId);
status_t status = remote()->transact(GET_INPUT_FOR_ATTR, data, &reply);
if (status != NO_ERROR) {
return status;
@@ -712,6 +722,42 @@
}
return status;
}
+
+ virtual status_t startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ if (source == NULL || attributes == NULL || handle == NULL) {
+ return BAD_VALUE;
+ }
+ data.write(source, sizeof(struct audio_port_config));
+ data.write(attributes, sizeof(audio_attributes_t));
+ status_t status = remote()->transact(START_AUDIO_SOURCE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = (status_t)reply.readInt32();
+ if (status != NO_ERROR) {
+ return status;
+ }
+ *handle = (audio_io_handle_t)reply.readInt32();
+ return status;
+ }
+
+ virtual status_t stopAudioSource(audio_io_handle_t handle)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ status_t status = remote()->transact(STOP_AUDIO_SOURCE, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = (status_t)reply.readInt32();
+ return status;
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -810,11 +856,13 @@
if (hasStream) {
stream = (audio_stream_type_t)data.readInt32();
}
+ uid_t uid = (uid_t)data.readInt32();
uint32_t samplingRate = data.readInt32();
audio_format_t format = (audio_format_t) data.readInt32();
audio_channel_mask_t channelMask = data.readInt32();
audio_output_flags_t flags =
static_cast <audio_output_flags_t>(data.readInt32());
+ audio_port_handle_t selectedDeviceId = data.readInt32();
bool hasOffloadInfo = data.readInt32() != 0;
audio_offload_info_t offloadInfo;
if (hasOffloadInfo) {
@@ -822,9 +870,9 @@
}
audio_io_handle_t output;
status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
- &output, session, &stream,
+ &output, session, &stream, uid,
samplingRate, format, channelMask,
- flags, hasOffloadInfo ? &offloadInfo : NULL);
+ flags, selectedDeviceId, hasOffloadInfo ? &offloadInfo : NULL);
reply->writeInt32(status);
reply->writeInt32(output);
reply->writeInt32(stream);
@@ -869,14 +917,16 @@
audio_attributes_t attr;
data.read(&attr, sizeof(audio_attributes_t));
audio_session_t session = (audio_session_t)data.readInt32();
+ uid_t uid = (uid_t)data.readInt32();
uint32_t samplingRate = data.readInt32();
audio_format_t format = (audio_format_t) data.readInt32();
audio_channel_mask_t channelMask = data.readInt32();
audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
+ audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32();
audio_io_handle_t input;
- status_t status = getInputForAttr(&attr, &input, session,
+ status_t status = getInputForAttr(&attr, &input, session, uid,
samplingRate, format, channelMask,
- flags);
+ flags, selectedDeviceId);
reply->writeInt32(status);
if (status == NO_ERROR) {
reply->writeInt32(input);
@@ -1221,6 +1271,27 @@
return NO_ERROR;
} break;
+ case START_AUDIO_SOURCE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ struct audio_port_config source;
+ data.read(&source, sizeof(struct audio_port_config));
+ audio_attributes_t attributes;
+ data.read(&attributes, sizeof(audio_attributes_t));
+ audio_io_handle_t handle;
+ status_t status = startAudioSource(&source, &attributes, &handle);
+ reply->writeInt32(status);
+ reply->writeInt32(handle);
+ return NO_ERROR;
+ } break;
+
+ case STOP_AUDIO_SOURCE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_io_handle_t handle = (audio_io_handle_t)data.readInt32();
+ status_t status = stopAudioSource(handle);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp
index 7c65878..65cc7d6 100644
--- a/media/libmedia/IAudioPolicyServiceClient.cpp
+++ b/media/libmedia/IAudioPolicyServiceClient.cpp
@@ -29,7 +29,8 @@
enum {
PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
- PATCH_LIST_UPDATE
+ PATCH_LIST_UPDATE,
+ MIX_STATE_UPDATE
};
class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient>
@@ -53,6 +54,15 @@
data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+ data.writeString8(regId);
+ data.writeInt32(state);
+ remote()->transact(MIX_STATE_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient");
@@ -73,6 +83,13 @@
onAudioPatchListUpdate();
return NO_ERROR;
} break;
+ case MIX_STATE_UPDATE: {
+ CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+ String8 regId = data.readString8();
+ int32_t state = data.readInt32();
+ onDynamicPolicyMixStateUpdate(regId, state);
+ return NO_ERROR;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
index c26c5bf..2f440fe 100644
--- a/media/libmedia/ICrypto.cpp
+++ b/media/libmedia/ICrypto.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <binder/Parcel.h>
+#include <binder/IMemory.h>
#include <media/ICrypto.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -34,6 +35,7 @@
REQUIRES_SECURE_COMPONENT,
DECRYPT,
NOTIFY_RESOLUTION,
+ SET_MEDIADRM_SESSION,
};
struct BpCrypto : public BpInterface<ICrypto> {
@@ -97,7 +99,7 @@
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) {
@@ -126,7 +128,8 @@
}
data.writeInt32(totalSize);
- data.write(srcPtr, totalSize);
+ data.writeStrongBinder(IInterface::asBinder(sharedBuffer));
+ data.writeInt32(offset);
data.writeInt32(numSubSamples);
data.write(subSamples, sizeof(CryptoPlugin::SubSample) * numSubSamples);
@@ -139,7 +142,7 @@
ssize_t result = reply.readInt32();
- if (result >= ERROR_DRM_VENDOR_MIN && result <= ERROR_DRM_VENDOR_MAX) {
+ if (isCryptoError(result)) {
errorDetailMsg->setTo(reply.readCString());
}
@@ -159,7 +162,28 @@
remote()->transact(NOTIFY_RESOLUTION, data, &reply);
}
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ remote()->transact(SET_MEDIADRM_SESSION, data, &reply);
+
+ return reply.readInt32();
+ }
+
private:
+ void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
+ uint32_t size = reply.readInt32();
+ vector.insertAt((size_t)0, size);
+ reply.read(vector.editArray(), size);
+ }
+
+ void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
+ data.writeInt32(vector.size());
+ data.write(vector.array(), vector.size());
+ }
+
DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
};
@@ -167,6 +191,17 @@
////////////////////////////////////////////////////////////////////////////////
+void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
+ uint32_t size = data.readInt32();
+ vector.insertAt((size_t)0, size);
+ data.read(vector.editArray(), size);
+}
+
+void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
+ reply->writeInt32(vector.size());
+ reply->write(vector.array(), vector.size());
+}
+
status_t BnCrypto::onTransact(
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
switch (code) {
@@ -245,8 +280,9 @@
data.read(iv, sizeof(iv));
size_t totalSize = data.readInt32();
- void *srcData = malloc(totalSize);
- data.read(srcData, totalSize);
+ sp<IMemory> sharedBuffer =
+ interface_cast<IMemory>(data.readStrongBinder());
+ int32_t offset = data.readInt32();
int32_t numSubSamples = data.readInt32();
@@ -265,20 +301,25 @@
}
AString errorDetailMsg;
- ssize_t result = decrypt(
+ ssize_t result;
+
+ if (offset + totalSize > sharedBuffer->size()) {
+ result = -EINVAL;
+ } else {
+ result = decrypt(
secure,
key,
iv,
mode,
- srcData,
+ sharedBuffer, offset,
subSamples, numSubSamples,
dstPtr,
&errorDetailMsg);
+ }
reply->writeInt32(result);
- if (result >= ERROR_DRM_VENDOR_MIN
- && result <= ERROR_DRM_VENDOR_MAX) {
+ if (isCryptoError(result)) {
reply->writeCString(errorDetailMsg.c_str());
}
@@ -294,9 +335,6 @@
delete[] subSamples;
subSamples = NULL;
- free(srcData);
- srcData = NULL;
-
return OK;
}
@@ -311,6 +349,15 @@
return OK;
}
+ case SET_MEDIADRM_SESSION:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId;
+ readVector(data, sessionId);
+ reply->writeInt32(setMediaDrmSession(sessionId));
+ return OK;
+ }
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IDataSource.cpp b/media/libmedia/IDataSource.cpp
new file mode 100644
index 0000000..76d1d68
--- /dev/null
+++ b/media/libmedia/IDataSource.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "IDataSource"
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#include <media/IDataSource.h>
+
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+enum {
+ GET_IMEMORY = IBinder::FIRST_CALL_TRANSACTION,
+ READ_AT,
+ GET_SIZE,
+ CLOSE,
+};
+
+struct BpDataSource : public BpInterface<IDataSource> {
+ BpDataSource(const sp<IBinder>& impl) : BpInterface<IDataSource>(impl) {}
+
+ virtual sp<IMemory> getIMemory() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+ remote()->transact(GET_IMEMORY, data, &reply);
+ sp<IBinder> binder = reply.readStrongBinder();
+ return interface_cast<IMemory>(binder);
+ }
+
+ virtual ssize_t readAt(off64_t offset, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+ data.writeInt64(offset);
+ data.writeInt64(size);
+ remote()->transact(READ_AT, data, &reply);
+ return reply.readInt64();
+ }
+
+ virtual status_t getSize(off64_t* size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+ remote()->transact(GET_SIZE, data, &reply);
+ status_t err = reply.readInt32();
+ *size = reply.readInt64();
+ return err;
+ }
+
+ virtual void close() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+ remote()->transact(CLOSE, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(DataSource, "android.media.IDataSource");
+
+status_t BnDataSource::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch (code) {
+ case GET_IMEMORY: {
+ CHECK_INTERFACE(IDataSource, data, reply);
+ reply->writeStrongBinder(IInterface::asBinder(getIMemory()));
+ return NO_ERROR;
+ } break;
+ case READ_AT: {
+ CHECK_INTERFACE(IDataSource, data, reply);
+ off64_t offset = (off64_t) data.readInt64();
+ size_t size = (size_t) data.readInt64();
+ reply->writeInt64(readAt(offset, size));
+ return NO_ERROR;
+ } break;
+ case GET_SIZE: {
+ CHECK_INTERFACE(IDataSource, data, reply);
+ off64_t size;
+ status_t err = getSize(&size);
+ reply->writeInt32(err);
+ reply->writeInt64(size);
+ return NO_ERROR;
+ } break;
+ case CLOSE: {
+ CHECK_INTERFACE(IDataSource, data, reply);
+ close();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace android
diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp
index 9122f75..f3a8902 100644
--- a/media/libmedia/IHDCP.cpp
+++ b/media/libmedia/IHDCP.cpp
@@ -241,8 +241,19 @@
case HDCP_ENCRYPT:
{
size_t size = data.readInt32();
+ size_t bufSize = 2 * size;
- void *inData = malloc(2 * size);
+ // watch out for overflow
+ void *inData = NULL;
+ if (bufSize > size) {
+ inData = malloc(bufSize);
+ }
+
+ if (inData == NULL) {
+ reply->writeInt32(ERROR_OUT_OF_RANGE);
+ return OK;
+ }
+
void *outData = (uint8_t *)inData + size;
data.read(inData, size);
@@ -273,11 +284,17 @@
size_t offset = data.readInt32();
size_t size = data.readInt32();
uint32_t streamCTR = data.readInt32();
- void *outData = malloc(size);
+ void *outData = NULL;
uint64_t inputCTR;
- status_t err = encryptNative(graphicBuffer, offset, size,
- streamCTR, &inputCTR, outData);
+ status_t err = ERROR_OUT_OF_RANGE;
+
+ outData = malloc(size);
+
+ if (outData != NULL) {
+ err = encryptNative(graphicBuffer, offset, size,
+ streamCTR, &inputCTR, outData);
+ }
reply->writeInt32(err);
@@ -295,8 +312,19 @@
case HDCP_DECRYPT:
{
size_t size = data.readInt32();
+ size_t bufSize = 2 * size;
- void *inData = malloc(2 * size);
+ // watch out for overflow
+ void *inData = NULL;
+ if (bufSize > size) {
+ inData = malloc(bufSize);
+ }
+
+ if (inData == NULL) {
+ reply->writeInt32(ERROR_OUT_OF_RANGE);
+ return OK;
+ }
+
void *outData = (uint8_t *)inData + size;
data.read(inData, size);
diff --git a/media/libmedia/IMediaCodecList.cpp b/media/libmedia/IMediaCodecList.cpp
index 80020db..e2df104 100644
--- a/media/libmedia/IMediaCodecList.cpp
+++ b/media/libmedia/IMediaCodecList.cpp
@@ -30,6 +30,7 @@
CREATE = IBinder::FIRST_CALL_TRANSACTION,
COUNT_CODECS,
GET_CODEC_INFO,
+ GET_GLOBAL_SETTINGS,
FIND_CODEC_BY_TYPE,
FIND_CODEC_BY_NAME,
};
@@ -64,6 +65,19 @@
}
}
+ virtual const sp<AMessage> getGlobalSettings() const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaCodecList::getInterfaceDescriptor());
+ remote()->transact(GET_GLOBAL_SETTINGS, data, &reply);
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ return AMessage::FromParcel(reply);
+ } else {
+ return NULL;
+ }
+ }
+
virtual ssize_t findCodecByType(
const char *type, bool encoder, size_t startIndex = 0) const
{
@@ -125,6 +139,20 @@
}
break;
+ case GET_GLOBAL_SETTINGS:
+ {
+ CHECK_INTERFACE(IMediaCodecList, data, reply);
+ const sp<AMessage> info = getGlobalSettings();
+ if (info != NULL) {
+ reply->writeInt32(OK);
+ info->writeToParcel(reply);
+ } else {
+ reply->writeInt32(-ERANGE);
+ }
+ return NO_ERROR;
+ }
+ break;
+
case FIND_CODEC_BY_TYPE:
{
CHECK_INTERFACE(IMediaCodecList, data, reply);
diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp
index 2ff7658..09137ef 100644
--- a/media/libmedia/IMediaHTTPConnection.cpp
+++ b/media/libmedia/IMediaHTTPConnection.cpp
@@ -24,6 +24,7 @@
#include <binder/Parcel.h>
#include <utils/String8.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaErrors.h>
namespace android {
@@ -106,11 +107,18 @@
return UNKNOWN_ERROR;
}
- int32_t len = reply.readInt32();
+ size_t len = reply.readInt32();
- if (len > 0) {
- memcpy(buffer, mMemory->pointer(), len);
+ if (len > size) {
+ ALOGE("requested %zu, got %zu", size, len);
+ return ERROR_OUT_OF_RANGE;
}
+ if (len > mMemory->size()) {
+ ALOGE("got %zu, but memory has %zu", len, mMemory->size());
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ memcpy(buffer, mMemory->pointer(), len);
return len;
}
diff --git a/media/libmedia/IMediaHTTPService.cpp b/media/libmedia/IMediaHTTPService.cpp
index f30d0f3..0c16a2b 100644
--- a/media/libmedia/IMediaHTTPService.cpp
+++ b/media/libmedia/IMediaHTTPService.cpp
@@ -44,6 +44,7 @@
status_t err = reply.readInt32();
if (err != OK) {
+ ALOGE("Unable to make HTTP connection (err = %d)", err);
return NULL;
}
diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp
index 230749e..1536337 100644
--- a/media/libmedia/IMediaLogService.cpp
+++ b/media/libmedia/IMediaLogService.cpp
@@ -45,7 +45,7 @@
data.writeStrongBinder(IInterface::asBinder(shared));
data.writeInt64((int64_t) size);
data.writeCString(name);
- status_t status = remote()->transact(REGISTER_WRITER, data, &reply);
+ status_t status __unused = remote()->transact(REGISTER_WRITER, data, &reply);
// FIXME ignores status
}
@@ -53,7 +53,7 @@
Parcel data, reply;
data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(shared));
- status_t status = remote()->transact(UNREGISTER_WRITER, data, &reply);
+ status_t status __unused = remote()->transact(UNREGISTER_WRITER, data, &reply);
// FIXME ignores status
}
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 551cffe..dbf524e 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <binder/Parcel.h>
+#include <media/IDataSource.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaMetadataRetriever.h>
#include <utils/String8.h>
@@ -65,6 +66,7 @@
DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
SET_DATA_SOURCE_URL,
SET_DATA_SOURCE_FD,
+ SET_DATA_SOURCE_CALLBACK,
GET_FRAME_AT_TIME,
EXTRACT_ALBUM_ART,
EXTRACT_METADATA,
@@ -125,6 +127,15 @@
return reply.readInt32();
}
+ status_t setDataSource(const sp<IDataSource>& source)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(source));
+ remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
+ return reply.readInt32();
+ }
+
sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
{
ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
@@ -229,12 +240,19 @@
} break;
case SET_DATA_SOURCE_FD: {
CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
- int fd = dup(data.readFileDescriptor());
+ int fd = data.readFileDescriptor();
int64_t offset = data.readInt64();
int64_t length = data.readInt64();
reply->writeInt32(setDataSource(fd, offset, length));
return NO_ERROR;
} break;
+ case SET_DATA_SOURCE_CALLBACK: {
+ CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
+ sp<IDataSource> source =
+ interface_cast<IDataSource>(data.readStrongBinder());
+ reply->writeInt32(setDataSource(source));
+ return NO_ERROR;
+ } break;
case GET_FRAME_AT_TIME: {
CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
int64_t timeUs = data.readInt64();
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index ce3009a..bde35f2 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -21,6 +21,10 @@
#include <binder/Parcel.h>
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
+
+#include <media/IDataSource.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayer.h>
#include <media/IStreamSource.h>
@@ -35,11 +39,15 @@
SET_DATA_SOURCE_URL,
SET_DATA_SOURCE_FD,
SET_DATA_SOURCE_STREAM,
+ SET_DATA_SOURCE_CALLBACK,
PREPARE_ASYNC,
START,
STOP,
IS_PLAYING,
- SET_PLAYBACK_RATE,
+ SET_PLAYBACK_SETTINGS,
+ GET_PLAYBACK_SETTINGS,
+ SET_SYNC_SETTINGS,
+ GET_SYNC_SETTINGS,
PAUSE,
SEEK_TO,
GET_CURRENT_POSITION,
@@ -121,6 +129,14 @@
return reply.readInt32();
}
+ status_t setDataSource(const sp<IDataSource> &source) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(source));
+ remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
+ return reply.readInt32();
+ }
+
// pass the buffered IGraphicBufferProducer to the media player service
status_t setVideoSurfaceTexture(const sp<IGraphicBufferProducer>& bufferProducer)
{
@@ -165,15 +181,63 @@
return reply.readInt32();
}
- status_t setPlaybackRate(float rate)
+ status_t setPlaybackSettings(const AudioPlaybackRate& rate)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
- data.writeFloat(rate);
- remote()->transact(SET_PLAYBACK_RATE, data, &reply);
+ data.writeFloat(rate.mSpeed);
+ data.writeFloat(rate.mPitch);
+ data.writeInt32((int32_t)rate.mFallbackMode);
+ data.writeInt32((int32_t)rate.mStretchMode);
+ remote()->transact(SET_PLAYBACK_SETTINGS, data, &reply);
return reply.readInt32();
}
+ status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ remote()->transact(GET_PLAYBACK_SETTINGS, data, &reply);
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ *rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+ rate->mSpeed = reply.readFloat();
+ rate->mPitch = reply.readFloat();
+ rate->mFallbackMode = (AudioTimestretchFallbackMode)reply.readInt32();
+ rate->mStretchMode = (AudioTimestretchStretchMode)reply.readInt32();
+ }
+ return err;
+ }
+
+ status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ data.writeInt32((int32_t)sync.mSource);
+ data.writeInt32((int32_t)sync.mAudioAdjustMode);
+ data.writeFloat(sync.mTolerance);
+ data.writeFloat(videoFpsHint);
+ remote()->transact(SET_SYNC_SETTINGS, data, &reply);
+ return reply.readInt32();
+ }
+
+ status_t getSyncSettings(AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ remote()->transact(GET_SYNC_SETTINGS, data, &reply);
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ AVSyncSettings settings;
+ settings.mSource = (AVSyncSource)reply.readInt32();
+ settings.mAudioAdjustMode = (AVSyncAudioAdjustMode)reply.readInt32();
+ settings.mTolerance = reply.readFloat();
+ *sync = settings;
+ *videoFps = reply.readFloat();
+ }
+ return err;
+ }
+
status_t pause()
{
Parcel data, reply;
@@ -406,6 +470,13 @@
reply->writeInt32(setDataSource(source));
return NO_ERROR;
}
+ case SET_DATA_SOURCE_CALLBACK: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ sp<IDataSource> source =
+ interface_cast<IDataSource>(data.readStrongBinder());
+ reply->writeInt32(setDataSource(source));
+ return NO_ERROR;
+ }
case SET_VIDEO_SURFACETEXTURE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
sp<IGraphicBufferProducer> bufferProducer =
@@ -436,9 +507,51 @@
reply->writeInt32(ret);
return NO_ERROR;
} break;
- case SET_PLAYBACK_RATE: {
+ case SET_PLAYBACK_SETTINGS: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
- reply->writeInt32(setPlaybackRate(data.readFloat()));
+ AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+ rate.mSpeed = data.readFloat();
+ rate.mPitch = data.readFloat();
+ rate.mFallbackMode = (AudioTimestretchFallbackMode)data.readInt32();
+ rate.mStretchMode = (AudioTimestretchStretchMode)data.readInt32();
+ reply->writeInt32(setPlaybackSettings(rate));
+ return NO_ERROR;
+ } break;
+ case GET_PLAYBACK_SETTINGS: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+ status_t err = getPlaybackSettings(&rate);
+ reply->writeInt32(err);
+ if (err == OK) {
+ reply->writeFloat(rate.mSpeed);
+ reply->writeFloat(rate.mPitch);
+ reply->writeInt32((int32_t)rate.mFallbackMode);
+ reply->writeInt32((int32_t)rate.mStretchMode);
+ }
+ return NO_ERROR;
+ } break;
+ case SET_SYNC_SETTINGS: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ AVSyncSettings sync;
+ sync.mSource = (AVSyncSource)data.readInt32();
+ sync.mAudioAdjustMode = (AVSyncAudioAdjustMode)data.readInt32();
+ sync.mTolerance = data.readFloat();
+ float videoFpsHint = data.readFloat();
+ reply->writeInt32(setSyncSettings(sync, videoFpsHint));
+ return NO_ERROR;
+ } break;
+ case GET_SYNC_SETTINGS: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ AVSyncSettings sync;
+ float videoFps;
+ status_t err = getSyncSettings(&sync, &videoFps);
+ reply->writeInt32(err);
+ if (err == OK) {
+ reply->writeInt32((int32_t)sync.mSource);
+ reply->writeInt32((int32_t)sync.mAudioAdjustMode);
+ reply->writeFloat(sync.mTolerance);
+ reply->writeFloat(videoFps);
+ }
return NO_ERROR;
} break;
case PAUSE: {
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index aa7b2e1..05f8670 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -78,10 +78,11 @@
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}
- virtual sp<IMediaRecorder> createMediaRecorder()
+ virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ data.writeString16(opPackageName);
remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
return interface_cast<IMediaRecorder>(reply.readStrongBinder());
}
@@ -128,11 +129,12 @@
return remote()->transact(PULL_BATTERY_DATA, data, reply);
}
- virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
- const String8& iface)
+ virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName,
+ const sp<IRemoteDisplayClient>& client, const String8& iface)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ data.writeString16(opPackageName);
data.writeStrongBinder(IInterface::asBinder(client));
data.writeString8(iface);
remote()->transact(LISTEN_FOR_REMOTE_DISPLAY, data, &reply);
@@ -166,7 +168,8 @@
} break;
case CREATE_MEDIA_RECORDER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
- sp<IMediaRecorder> recorder = createMediaRecorder();
+ const String16 opPackageName = data.readString16();
+ sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
reply->writeStrongBinder(IInterface::asBinder(recorder));
return NO_ERROR;
} break;
@@ -214,10 +217,11 @@
} break;
case LISTEN_FOR_REMOTE_DISPLAY: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ const String16 opPackageName = data.readString16();
sp<IRemoteDisplayClient> client(
interface_cast<IRemoteDisplayClient>(data.readStrongBinder()));
String8 iface(data.readString8());
- sp<IRemoteDisplay> display(listenForRemoteDisplay(client, iface));
+ sp<IRemoteDisplay> display(listenForRemoteDisplay(opPackageName, client, iface));
reply->writeStrongBinder(IInterface::asBinder(display));
return NO_ERROR;
} break;
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 8ca256c..ee3b584 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -35,6 +35,7 @@
RELEASE = IBinder::FIRST_CALL_TRANSACTION,
INIT,
CLOSE,
+ SET_INPUT_SURFACE,
QUERY_SURFACE_MEDIASOURCE,
RESET,
STOP,
@@ -75,6 +76,16 @@
return reply.readInt32();
}
+ status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface)
+ {
+ ALOGV("setInputSurface(%p)", surface.get());
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(surface));
+ remote()->transact(SET_INPUT_SURFACE, data, &reply);
+ return reply.readInt32();
+ }
+
sp<IGraphicBufferProducer> querySurfaceMediaSource()
{
ALOGV("Query SurfaceMediaSource");
@@ -442,6 +453,14 @@
reply->writeInt32(setCamera(camera, proxy));
return NO_ERROR;
} break;
+ case SET_INPUT_SURFACE: {
+ ALOGV("SET_INPUT_SURFACE");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ sp<IGraphicBufferConsumer> surface = interface_cast<IGraphicBufferConsumer>(
+ data.readStrongBinder());
+ reply->writeInt32(setInputSurface(surface));
+ return NO_ERROR;
+ } break;
case QUERY_SURFACE_MEDIASOURCE: {
ALOGV("QUERY_SURFACE_MEDIASOURCE");
CHECK_INTERFACE(IMediaRecorder, data, reply);
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index e208df9..c14debf 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -41,6 +41,8 @@
USE_BUFFER,
USE_GRAPHIC_BUFFER,
CREATE_INPUT_SURFACE,
+ CREATE_PERSISTENT_INPUT_SURFACE,
+ SET_INPUT_SURFACE,
SIGNAL_END_OF_INPUT_STREAM,
STORE_META_DATA_IN_BUFFERS,
PREPARE_FOR_ADAPTIVE_PLAYBACK,
@@ -326,6 +328,50 @@
return err;
}
+ virtual status_t createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer) {
+ Parcel data, reply;
+ status_t err;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ err = remote()->transact(CREATE_PERSISTENT_INPUT_SURFACE, data, &reply);
+ if (err != OK) {
+ ALOGW("binder transaction failed: %d", err);
+ return err;
+ }
+
+ err = reply.readInt32();
+ if (err != OK) {
+ return err;
+ }
+
+ *bufferProducer = IGraphicBufferProducer::asInterface(
+ reply.readStrongBinder());
+ *bufferConsumer = IGraphicBufferConsumer::asInterface(
+ reply.readStrongBinder());
+
+ return err;
+ }
+
+ virtual status_t setInputSurface(
+ node_id node, OMX_U32 port_index,
+ const sp<IGraphicBufferConsumer> &bufferConsumer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ status_t err;
+ data.writeInt32((int32_t)node);
+ data.writeInt32(port_index);
+ data.writeStrongBinder(IInterface::asBinder(bufferConsumer));
+
+ err = remote()->transact(SET_INPUT_SURFACE, data, &reply);
+
+ if (err != OK) {
+ ALOGW("binder transaction failed: %d", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
virtual status_t signalEndOfInputStream(node_id node) {
Parcel data, reply;
status_t err;
@@ -781,6 +827,41 @@
return NO_ERROR;
}
+ case CREATE_PERSISTENT_INPUT_SURFACE:
+ {
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ sp<IGraphicBufferConsumer> bufferConsumer;
+ status_t err = createPersistentInputSurface(
+ &bufferProducer, &bufferConsumer);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->writeStrongBinder(IInterface::asBinder(bufferProducer));
+ reply->writeStrongBinder(IInterface::asBinder(bufferConsumer));
+ }
+
+ return NO_ERROR;
+ }
+
+ case SET_INPUT_SURFACE:
+ {
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+ node_id node = (node_id)data.readInt32();
+ OMX_U32 port_index = data.readInt32();
+
+ sp<IGraphicBufferConsumer> bufferConsumer =
+ interface_cast<IGraphicBufferConsumer>(data.readStrongBinder());
+
+ status_t err = setInputSurface(node, port_index, bufferConsumer);
+
+ reply->writeInt32(err);
+ return NO_ERROR;
+ }
+
case SIGNAL_END_OF_INPUT_STREAM:
{
CHECK_OMX_INTERFACE(IOMX, data, reply);
diff --git a/media/libmedia/IResourceManagerClient.cpp b/media/libmedia/IResourceManagerClient.cpp
index 6fa56fc..b3f56e8 100644
--- a/media/libmedia/IResourceManagerClient.cpp
+++ b/media/libmedia/IResourceManagerClient.cpp
@@ -25,6 +25,7 @@
enum {
RECLAIM_RESOURCE = IBinder::FIRST_CALL_TRANSACTION,
+ GET_NAME,
};
class BpResourceManagerClient: public BpInterface<IResourceManagerClient>
@@ -46,6 +47,19 @@
}
return ret;
}
+
+ virtual String8 getName() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IResourceManagerClient::getInterfaceDescriptor());
+
+ String8 ret;
+ status_t status = remote()->transact(GET_NAME, data, &reply);
+ if (status == NO_ERROR) {
+ ret = reply.readString8();
+ }
+ return ret;
+ }
+
};
IMPLEMENT_META_INTERFACE(ResourceManagerClient, "android.media.IResourceManagerClient");
@@ -62,6 +76,12 @@
reply->writeInt32(ret);
return NO_ERROR;
} break;
+ case GET_NAME: {
+ CHECK_INTERFACE(IResourceManagerClient, data, reply);
+ String8 ret = getName();
+ reply->writeString8(ret);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IResourceManagerService.cpp b/media/libmedia/IResourceManagerService.cpp
index 95a2d1c..7ae946d 100644
--- a/media/libmedia/IResourceManagerService.cpp
+++ b/media/libmedia/IResourceManagerService.cpp
@@ -38,11 +38,9 @@
template <typename T>
static void writeToParcel(Parcel *data, const Vector<T> &items) {
size_t size = items.size();
- size_t sizePosition = data->dataPosition();
// truncates size, but should be okay for this usecase
data->writeUint32(static_cast<uint32_t>(size));
for (size_t i = 0; i < size; i++) {
- size_t position = data->dataPosition();
items[i].writeToParcel(data);
}
}
@@ -121,7 +119,6 @@
switch (code) {
case CONFIG: {
CHECK_INTERFACE(IResourceManagerService, data, reply);
- int pid = data.readInt32();
sp<IResourceManagerClient> client(
interface_cast<IResourceManagerClient>(data.readStrongBinder()));
Vector<MediaResourcePolicy> policies;
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 7b4c4e2..8d3fa7b 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -206,6 +206,17 @@
return OK;
}
+status_t MediaCodecInfo::updateMime(const char *mime) {
+ ssize_t ix = getCapabilityIndex(mime);
+ if (ix < 0) {
+ ALOGE("updateMime mime not found %s", mime);
+ return -EINVAL;
+ }
+
+ mCurrentCaps = mCaps.valueAt(ix);
+ return OK;
+}
+
void MediaCodecInfo::removeMime(const char *mime) {
ssize_t ix = getCapabilityIndex(mime);
if (ix >= 0) {
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 47f9258..ae0061f 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -532,7 +532,6 @@
CHECK(refIndex != -1);
RequiredProfileRefInfo *info;
camcorder_quality refQuality;
- VideoCodec *codec = NULL;
// Check high and low from either camcorder profile, timelapse profile
// or high speed profile, but not all of them. Default, check camcorder profile
diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp
index 8be01bc..40ec0cb 100644
--- a/media/libmedia/MediaResource.cpp
+++ b/media/libmedia/MediaResource.cpp
@@ -23,6 +23,8 @@
const char kResourceSecureCodec[] = "secure-codec";
const char kResourceNonSecureCodec[] = "non-secure-codec";
+const char kResourceAudioCodec[] = "audio-codec";
+const char kResourceVideoCodec[] = "video-codec";
const char kResourceGraphicMemory[] = "graphic-memory";
MediaResource::MediaResource() : mValue(0) {}
@@ -50,7 +52,7 @@
String8 MediaResource::toString() const {
String8 str;
- str.appendFormat("%s/%s:%llu", mType.string(), mSubType.string(), mValue);
+ str.appendFormat("%s/%s:%llu", mType.string(), mSubType.string(), (unsigned long long)mValue);
return str;
}
diff --git a/media/libmedia/MediaResourcePolicy.cpp b/media/libmedia/MediaResourcePolicy.cpp
index 2bb996a..5210825 100644
--- a/media/libmedia/MediaResourcePolicy.cpp
+++ b/media/libmedia/MediaResourcePolicy.cpp
@@ -24,25 +24,25 @@
const char kPolicySupportsMultipleSecureCodecs[] = "supports-multiple-secure-codecs";
const char kPolicySupportsSecureWithNonSecureCodec[] = "supports-secure-with-non-secure-codec";
-MediaResourcePolicy::MediaResourcePolicy() : mValue(0) {}
+MediaResourcePolicy::MediaResourcePolicy() {}
-MediaResourcePolicy::MediaResourcePolicy(String8 type, uint64_t value)
+MediaResourcePolicy::MediaResourcePolicy(String8 type, String8 value)
: mType(type),
mValue(value) {}
void MediaResourcePolicy::readFromParcel(const Parcel &parcel) {
mType = parcel.readString8();
- mValue = parcel.readUint64();
+ mValue = parcel.readString8();
}
void MediaResourcePolicy::writeToParcel(Parcel *parcel) const {
parcel->writeString8(mType);
- parcel->writeUint64(mValue);
+ parcel->writeString8(mValue);
}
String8 MediaResourcePolicy::toString() const {
String8 str;
- str.appendFormat("%s:%llu", mType.string(), mValue);
+ str.appendFormat("%s:%s", mType.string(), mValue.string());
return str;
}
diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp
index d31f721..554dbae 100644
--- a/media/libmedia/MemoryLeakTrackUtil.cpp
+++ b/media/libmedia/MemoryLeakTrackUtil.cpp
@@ -173,7 +173,7 @@
#else
// Does nothing
-void dumpMemoryAddresses(int fd) {}
+void dumpMemoryAddresses(int fd __unused) {}
#endif
} // namespace android
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 2cc4685..6da5348 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -984,7 +984,6 @@
if ((mStartTime.tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) {
time_t sec = stopTime.tv_sec - mStartTime.tv_sec;
long nsec = stopTime.tv_nsec - mStartTime.tv_nsec;
- long durationMs;
if (nsec < 0) {
--sec;
nsec += 1000000000;
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 9d69b6a..dc46038 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -34,11 +34,12 @@
// ---------------------------------------------------------------------------
-Visualizer::Visualizer (int32_t priority,
+Visualizer::Visualizer (const String16& opPackageName,
+ int32_t priority,
effect_callback_t cbf,
void* user,
int sessionId)
- : AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId),
+ : AudioEffect(SL_IID_VISUALIZATION, opPackageName, NULL, priority, cbf, user, sessionId),
mCaptureRate(CAPTURE_RATE_DEF),
mCaptureSize(CAPTURE_SIZE_DEF),
mSampleRate(44100000),
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 873808a..9a76f58 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -129,6 +129,18 @@
return mRetriever->setDataSource(fd, offset, length);
}
+status_t MediaMetadataRetriever::setDataSource(
+ const sp<IDataSource>& dataSource)
+{
+ ALOGV("setDataSource(IDataSource)");
+ Mutex::Autolock _l(mLock);
+ if (mRetriever == 0) {
+ ALOGE("retriever is not initialized");
+ return INVALID_OPERATION;
+ }
+ return mRetriever->setDataSource(dataSource);
+}
+
sp<IMemory> MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option)
{
ALOGV("getFrameAtTime: time(%" PRId64 " us) option(%d)", timeUs, option);
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 5dd8c02..81a5e8c 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -32,7 +32,10 @@
#include <gui/Surface.h>
#include <media/mediaplayer.h>
+#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
+#include <media/AVSyncSettings.h>
+#include <media/IDataSource.h>
#include <binder/MemoryBase.h>
@@ -59,7 +62,6 @@
mLoop = false;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
- mPlaybackRate = 1.0;
mLockThreadId = 0;
mAudioSessionId = AudioSystem::newAudioUniqueId();
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
@@ -195,6 +197,22 @@
return err;
}
+status_t MediaPlayer::setDataSource(const sp<IDataSource> &source)
+{
+ ALOGV("setDataSource(IDataSource)");
+ status_t err = UNKNOWN_ERROR;
+ const sp<IMediaPlayerService>& service(getMediaPlayerService());
+ if (service != 0) {
+ sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
+ if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+ (NO_ERROR != player->setDataSource(source))) {
+ player.clear();
+ }
+ err = attachNewPlayer(player);
+ }
+ return err;
+}
+
status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply)
{
Mutex::Autolock _l(mLock);
@@ -372,6 +390,9 @@
if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) {
ALOGE("internal/external state mismatch corrected");
mCurrentState = MEDIA_PLAYER_PAUSED;
+ } else if ((mCurrentState & MEDIA_PLAYER_PAUSED) && temp) {
+ ALOGE("internal/external state mismatch corrected");
+ mCurrentState = MEDIA_PLAYER_STARTED;
}
return temp;
}
@@ -379,22 +400,50 @@
return false;
}
-status_t MediaPlayer::setPlaybackRate(float rate)
+status_t MediaPlayer::setPlaybackSettings(const AudioPlaybackRate& rate)
{
- ALOGV("setPlaybackRate: %f", rate);
- if (rate <= 0.0) {
+ ALOGV("setPlaybackSettings: %f %f %d %d",
+ rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+ // Negative speed and pitch does not make sense. Further validation will
+ // be done by the respective mediaplayers.
+ if (rate.mSpeed < 0.f || rate.mPitch < 0.f) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
- if (mPlayer != 0) {
- if (mPlaybackRate == rate) {
- return NO_ERROR;
+ if (mPlayer == 0) return INVALID_OPERATION;
+ status_t err = mPlayer->setPlaybackSettings(rate);
+ if (err == OK) {
+ if (rate.mSpeed == 0.f && mCurrentState == MEDIA_PLAYER_STARTED) {
+ mCurrentState = MEDIA_PLAYER_PAUSED;
+ } else if (rate.mSpeed != 0.f && mCurrentState == MEDIA_PLAYER_PAUSED) {
+ mCurrentState = MEDIA_PLAYER_STARTED;
}
- mPlaybackRate = rate;
- return mPlayer->setPlaybackRate(rate);
}
- ALOGV("setPlaybackRate: no active player");
- return INVALID_OPERATION;
+ return err;
+}
+
+status_t MediaPlayer::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == 0) return INVALID_OPERATION;
+ return mPlayer->getPlaybackSettings(rate);
+}
+
+status_t MediaPlayer::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint)
+{
+ ALOGV("setSyncSettings: %u %u %f %f",
+ sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == 0) return INVALID_OPERATION;
+ return mPlayer->setSyncSettings(sync, videoFpsHint);
+}
+
+status_t MediaPlayer::getSyncSettings(
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == 0) return INVALID_OPERATION;
+ return mPlayer->getSyncSettings(sync, videoFps);
}
status_t MediaPlayer::getVideoWidth(int *w)
@@ -840,6 +889,9 @@
case MEDIA_SUBTITLE_DATA:
ALOGV("Received subtitle data message");
break;
+ case MEDIA_META_DATA:
+ ALOGV("Received timed metadata message");
+ break;
default:
ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
break;
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index a2d6e53..8bbd8f1 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -27,6 +27,7 @@
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
#include <media/mediaplayer.h> // for MEDIA_ERROR_SERVER_DIED
+#include <media/stagefright/PersistentSurface.h>
#include <gui/IGraphicBufferProducer.h>
namespace android {
@@ -344,6 +345,24 @@
+status_t MediaRecorder::setInputSurface(const sp<PersistentSurface>& surface)
+{
+ ALOGV("setInputSurface");
+ if (mMediaRecorder == NULL) {
+ ALOGE("media recorder is not initialized yet");
+ return INVALID_OPERATION;
+ }
+ bool isInvalidState = (mCurrentState &
+ (MEDIA_RECORDER_PREPARED |
+ MEDIA_RECORDER_RECORDING));
+ if (isInvalidState) {
+ ALOGE("setInputSurface is called in an invalid state: %d", mCurrentState);
+ return INVALID_OPERATION;
+ }
+
+ return mMediaRecorder->setInputSurface(surface->getBufferConsumer());
+}
+
status_t MediaRecorder::setVideoFrameRate(int frames_per_second)
{
ALOGV("setVideoFrameRate(%d)", frames_per_second);
@@ -594,13 +613,13 @@
return INVALID_OPERATION;
}
-MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL)
+MediaRecorder::MediaRecorder(const String16& opPackageName) : mSurfaceMediaSource(NULL)
{
ALOGV("constructor");
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != NULL) {
- mMediaRecorder = service->createMediaRecorder();
+ mMediaRecorder = service->createMediaRecorder(opPackageName);
}
if (mMediaRecorder != NULL) {
mCurrentState = MEDIA_RECORDER_IDLE;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 4b31715..2c4e719 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -54,6 +54,9 @@
$(TOP)/frameworks/native/include/media/openmax \
$(TOP)/external/tremolo/Tremolo \
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
LOCAL_MODULE:= libmediaplayerservice
LOCAL_32_BIT_ONLY := true
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
index 8ee7c0b..147d35f 100644
--- a/media/libmediaplayerservice/Crypto.cpp
+++ b/media/libmediaplayerservice/Crypto.cpp
@@ -22,6 +22,7 @@
#include "Crypto.h"
+#include <binder/IMemory.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
@@ -88,7 +89,7 @@
// first check cache
Vector<uint8_t> uuidVector;
- uuidVector.appendArray(uuid, sizeof(uuid));
+ uuidVector.appendArray(uuid, sizeof(uuid[0]) * 16);
ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector);
if (index >= 0) {
if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) {
@@ -238,7 +239,7 @@
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) {
@@ -252,6 +253,8 @@
return -EINVAL;
}
+ const void *srcPtr = static_cast<uint8_t *>(sharedBuffer->pointer()) + offset;
+
return mPlugin->decrypt(
secure, key, iv, mode, srcPtr, subSamples, numSubSamples, dstPtr,
errorDetailMsg);
@@ -265,4 +268,14 @@
}
}
+status_t Crypto::setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+ Mutex::Autolock autoLock(mLock);
+
+ status_t result = NO_INIT;
+ if (mInitCheck == OK && mPlugin != NULL) {
+ result = mPlugin->setMediaDrmSession(sessionId);
+ }
+ return result;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h
index 0037c2e..99ea95d 100644
--- a/media/libmediaplayerservice/Crypto.h
+++ b/media/libmediaplayerservice/Crypto.h
@@ -47,12 +47,14 @@
virtual void notifyResolution(uint32_t width, uint32_t height);
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId);
+
virtual ssize_t decrypt(
bool secure,
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg);
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 49e01d1..a7f6f8b 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -52,6 +52,7 @@
KeyedVector<Vector<uint8_t>, String8> Drm::mUUIDToLibraryPathMap;
KeyedVector<String8, wp<SharedLibrary> > Drm::mLibraryPathToOpenLibraryMap;
Mutex Drm::mMapLock;
+Mutex Drm::mLock;
static bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) {
if (lhs.size() < rhs.size()) {
@@ -136,25 +137,57 @@
if (listener != NULL) {
Parcel obj;
- if (sessionId && sessionId->size()) {
- obj.writeInt32(sessionId->size());
- obj.write(sessionId->array(), sessionId->size());
- } else {
- obj.writeInt32(0);
- }
-
- if (data && data->size()) {
- obj.writeInt32(data->size());
- obj.write(data->array(), data->size());
- } else {
- obj.writeInt32(0);
- }
+ writeByteArray(obj, sessionId);
+ writeByteArray(obj, data);
Mutex::Autolock lock(mNotifyLock);
listener->notify(eventType, extra, &obj);
}
}
+void Drm::sendExpirationUpdate(Vector<uint8_t> const *sessionId,
+ int64_t expiryTimeInMS)
+{
+ mEventLock.lock();
+ sp<IDrmClient> listener = mListener;
+ mEventLock.unlock();
+
+ if (listener != NULL) {
+ Parcel obj;
+ writeByteArray(obj, sessionId);
+ obj.writeInt64(expiryTimeInMS);
+
+ Mutex::Autolock lock(mNotifyLock);
+ listener->notify(DrmPlugin::kDrmPluginEventExpirationUpdate, 0, &obj);
+ }
+}
+
+void Drm::sendKeysChange(Vector<uint8_t> const *sessionId,
+ Vector<DrmPlugin::KeyStatus> const *keyStatusList,
+ bool hasNewUsableKey)
+{
+ mEventLock.lock();
+ sp<IDrmClient> listener = mListener;
+ mEventLock.unlock();
+
+ if (listener != NULL) {
+ Parcel obj;
+ writeByteArray(obj, sessionId);
+
+ size_t nkeys = keyStatusList->size();
+ obj.writeInt32(keyStatusList->size());
+ for (size_t i = 0; i < nkeys; ++i) {
+ const DrmPlugin::KeyStatus *keyStatus = &keyStatusList->itemAt(i);
+ writeByteArray(obj, &keyStatus->mKeyId);
+ obj.writeInt32(keyStatus->mType);
+ }
+ obj.writeInt32(hasNewUsableKey);
+
+ Mutex::Autolock lock(mNotifyLock);
+ listener->notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &obj);
+ }
+}
+
/*
* Search the plugins directory for a plugin that supports the scheme
* specified by uuid
@@ -177,7 +210,7 @@
// first check cache
Vector<uint8_t> uuidVector;
- uuidVector.appendArray(uuid, sizeof(uuid));
+ uuidVector.appendArray(uuid, sizeof(uuid[0]) * 16);
ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector);
if (index >= 0) {
if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) {
@@ -325,7 +358,18 @@
status_t err = mPlugin->openSession(sessionId);
if (err == ERROR_DRM_RESOURCE_BUSY) {
bool retry = false;
+ mLock.unlock();
+ // reclaimSession may call back to closeSession, since mLock is shared between Drm
+ // instances, we should unlock here to avoid deadlock.
retry = DrmSessionManager::Instance()->reclaimSession(getCallingPid());
+ mLock.lock();
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mPlugin == NULL) {
+ return -EINVAL;
+ }
if (retry) {
err = mPlugin->openSession(sessionId);
}
@@ -744,7 +788,7 @@
return mPlugin->signRSA(sessionId, algorithm, message, wrappedKey, signature);
}
-void Drm::binderDied(const wp<IBinder> &the_late_who)
+void Drm::binderDied(const wp<IBinder> &the_late_who __unused)
{
mEventLock.lock();
mListener.clear();
@@ -756,4 +800,14 @@
closeFactory();
}
+void Drm::writeByteArray(Parcel &obj, Vector<uint8_t> const *array)
+{
+ if (array && array->size()) {
+ obj.writeInt32(array->size());
+ obj.write(array->array(), array->size());
+ } else {
+ obj.writeInt32(0);
+ }
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 7e8f246..056723c 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -26,8 +26,8 @@
namespace android {
-struct DrmFactory;
-struct DrmPlugin;
+class DrmFactory;
+class DrmPlugin;
struct DrmSessionClientInterface;
struct Drm : public BnDrm,
@@ -133,10 +133,17 @@
Vector<uint8_t> const *sessionId,
Vector<uint8_t> const *data);
+ virtual void sendExpirationUpdate(Vector<uint8_t> const *sessionId,
+ int64_t expiryTimeInMS);
+
+ virtual void sendKeysChange(Vector<uint8_t> const *sessionId,
+ Vector<DrmPlugin::KeyStatus> const *keyStatusList,
+ bool hasNewUsableKey);
+
virtual void binderDied(const wp<IBinder> &the_late_who);
private:
- mutable Mutex mLock;
+ static Mutex mLock;
status_t mInitCheck;
@@ -157,7 +164,7 @@
void findFactoryForScheme(const uint8_t uuid[16]);
bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]);
void closeFactory();
-
+ void writeByteArray(Parcel &obj, Vector<uint8_t> const *array);
DISALLOW_EVIL_CONSTRUCTORS(Drm);
};
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index 48884b9..ca33aed 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -131,6 +131,11 @@
GET_PLAYER_TYPE_IMPL(client, source);
}
+player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
+ const sp<DataSource> &source) {
+ GET_PLAYER_TYPE_IMPL(client, source);
+}
+
#undef GET_PLAYER_TYPE_IMPL
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
@@ -273,6 +278,13 @@
return 1.0;
}
+ virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+ const sp<DataSource>& /*source*/,
+ float /*curScore*/) {
+ // Only NuPlayer supports setting a DataSource source directly.
+ return 1.0;
+ }
+
virtual sp<MediaPlayerBase> createPlayer() {
ALOGV(" create NuPlayer");
return new NuPlayerDriver;
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.h b/media/libmediaplayerservice/MediaPlayerFactory.h
index 55ff918..7f9b3b5 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.h
+++ b/media/libmediaplayerservice/MediaPlayerFactory.h
@@ -43,6 +43,10 @@
const sp<IStreamSource> &/*source*/,
float /*curScore*/) { return 0.0; }
+ virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+ const sp<DataSource> &/*source*/,
+ float /*curScore*/) { return 0.0; }
+
virtual sp<MediaPlayerBase> createPlayer() = 0;
};
@@ -57,6 +61,8 @@
int64_t length);
static player_type getPlayerType(const sp<IMediaPlayer>& client,
const sp<IStreamSource> &source);
+ static player_type getPlayerType(const sp<IMediaPlayer>& client,
+ const sp<DataSource> &source);
static sp<MediaPlayerBase> createPlayer(player_type playerType,
void* cookie,
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index f113e21..4fc6fa3 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -307,10 +307,10 @@
ALOGV("MediaPlayerService destroyed");
}
-sp<IMediaRecorder> MediaPlayerService::createMediaRecorder()
+sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName)
{
pid_t pid = IPCThreadState::self()->getCallingPid();
- sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);
+ sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
wp<MediaRecorderClient> w = recorder;
Mutex::Autolock lock(mLock);
mMediaRecorderClients.add(w);
@@ -381,12 +381,13 @@
}
sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
+ const String16 &opPackageName,
const sp<IRemoteDisplayClient>& client, const String8& iface) {
if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
return NULL;
}
- return new RemoteDisplay(client, iface.string());
+ return new RemoteDisplay(opPackageName, client, iface.string());
}
status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector<String16>& args) const
@@ -413,7 +414,7 @@
return NO_ERROR;
}
-status_t MediaPlayerService::Client::dump(int fd, const Vector<String16>& args) const
+status_t MediaPlayerService::Client::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -746,7 +747,7 @@
return UNKNOWN_ERROR;
}
- ALOGV("st_dev = %llu", sb.st_dev);
+ ALOGV("st_dev = %llu", static_cast<uint64_t>(sb.st_dev));
ALOGV("st_mode = %u", sb.st_mode);
ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid));
ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid));
@@ -754,7 +755,6 @@
if (offset >= sb.st_size) {
ALOGE("offset error");
- ::close(fd);
return UNKNOWN_ERROR;
}
if (offset + length > sb.st_size) {
@@ -790,6 +790,19 @@
return mStatus;
}
+status_t MediaPlayerService::Client::setDataSource(
+ const sp<IDataSource> &source) {
+ sp<DataSource> dataSource = DataSource::CreateFromIDataSource(source);
+ player_type playerType = MediaPlayerFactory::getPlayerType(this, dataSource);
+ sp<MediaPlayerBase> p = setDataSource_pre(playerType);
+ if (p == NULL) {
+ return NO_INIT;
+ }
+ // now set data source
+ setDataSource_post(p, p->setDataSource(dataSource));
+ return mStatus;
+}
+
void MediaPlayerService::Client::disconnectNativeWindow() {
if (mConnectedWindow != NULL) {
status_t err = native_window_api_disconnect(mConnectedWindow.get(),
@@ -967,12 +980,52 @@
return NO_ERROR;
}
-status_t MediaPlayerService::Client::setPlaybackRate(float rate)
+status_t MediaPlayerService::Client::setPlaybackSettings(const AudioPlaybackRate& rate)
{
- ALOGV("[%d] setPlaybackRate(%f)", mConnId, rate);
+ ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
+ mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
sp<MediaPlayerBase> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
- return p->setPlaybackRate(rate);
+ return p->setPlaybackSettings(rate);
+}
+
+status_t MediaPlayerService::Client::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
+{
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ status_t ret = p->getPlaybackSettings(rate);
+ if (ret == NO_ERROR) {
+ ALOGV("[%d] getPlaybackSettings(%f, %f, %d, %d)",
+ mConnId, rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
+ } else {
+ ALOGV("[%d] getPlaybackSettings returned %d", mConnId, ret);
+ }
+ return ret;
+}
+
+status_t MediaPlayerService::Client::setSyncSettings(
+ const AVSyncSettings& sync, float videoFpsHint)
+{
+ ALOGV("[%d] setSyncSettings(%u, %u, %f, %f)",
+ mConnId, sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ return p->setSyncSettings(sync, videoFpsHint);
+}
+
+status_t MediaPlayerService::Client::getSyncSettings(
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
+{
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ status_t ret = p->getSyncSettings(sync, videoFps);
+ if (ret == NO_ERROR) {
+ ALOGV("[%d] getSyncSettings(%u, %u, %f, %f)",
+ mConnId, sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
+ } else {
+ ALOGV("[%d] getSyncSettings returned %d", mConnId, ret);
+ }
+ return ret;
}
status_t MediaPlayerService::Client::getCurrentPosition(int *msec)
@@ -1297,7 +1350,7 @@
mStreamType = AUDIO_STREAM_MUSIC;
mLeftVolume = 1.0;
mRightVolume = 1.0;
- mPlaybackRatePermille = 1000;
+ mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
mSampleRateHz = 0;
mMsecsPerFrame = 0;
mAuxEffectId = 0;
@@ -1448,8 +1501,6 @@
}
ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
format, bufferCount, mSessionId, flags);
- uint32_t afSampleRate;
- size_t afFrameCount;
size_t frameCount;
// offloading is only supported in callback mode for now.
@@ -1622,7 +1673,7 @@
mSampleRateHz = sampleRate;
mFlags = flags;
- mMsecsPerFrame = mPlaybackRatePermille / (float) sampleRate;
+ mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
uint32_t pos;
if (t->getPosition(&pos) == OK) {
mBytesWritten = uint64_t(pos) * t->frameSize();
@@ -1631,7 +1682,7 @@
status_t res = NO_ERROR;
if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) {
- res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000);
+ res = t->setPlaybackRate(mPlaybackRate);
if (res == NO_ERROR) {
t->setAuxEffectSendLevel(mSendLevel);
res = t->attachAuxEffect(mAuxEffectId);
@@ -1727,22 +1778,38 @@
}
}
-status_t MediaPlayerService::AudioOutput::setPlaybackRatePermille(int32_t ratePermille)
+status_t MediaPlayerService::AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate)
{
- ALOGV("setPlaybackRatePermille(%d)", ratePermille);
- status_t res = NO_ERROR;
- if (mTrack != 0) {
- res = mTrack->setSampleRate(ratePermille * mSampleRateHz / 1000);
- } else {
- res = NO_INIT;
+ ALOGV("setPlaybackRate(%f %f %d %d)",
+ rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+ if (mTrack == 0) {
+ // remember rate so that we can set it when the track is opened
+ mPlaybackRate = rate;
+ return OK;
}
- mPlaybackRatePermille = ratePermille;
+ status_t res = mTrack->setPlaybackRate(rate);
+ if (res != NO_ERROR) {
+ return res;
+ }
+ // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
+ CHECK_GT(rate.mSpeed, 0.f);
+ mPlaybackRate = rate;
if (mSampleRateHz != 0) {
- mMsecsPerFrame = mPlaybackRatePermille / (float) mSampleRateHz;
+ mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
}
return res;
}
+status_t MediaPlayerService::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate)
+{
+ ALOGV("setPlaybackRate");
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ *rate = mTrack->getPlaybackRate();
+ return NO_ERROR;
+}
+
status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level)
{
ALOGV("setAuxEffectSendLevel(%f)", level);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 4ce4b81..5103841 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -34,7 +34,10 @@
namespace android {
+struct AudioPlaybackRate;
class AudioTrack;
+struct AVSyncSettings;
+class IDataSource;
class IMediaRecorder;
class IMediaMetadataRetriever;
class IOMX;
@@ -108,7 +111,9 @@
void setAudioAttributes(const audio_attributes_t * attributes);
void setVolume(float left, float right);
- virtual status_t setPlaybackRatePermille(int32_t ratePermille);
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
+
status_t setAuxEffectSendLevel(float level);
status_t attachAuxEffect(int effectId);
virtual status_t dump(int fd, const Vector<String16>& args) const;
@@ -138,7 +143,7 @@
const audio_attributes_t *mAttributes;
float mLeftVolume;
float mRightVolume;
- int32_t mPlaybackRatePermille;
+ AudioPlaybackRate mPlaybackRate;
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
float mMsecsPerFrame;
int mSessionId;
@@ -187,7 +192,7 @@
static void instantiate();
// IMediaPlayerService interface
- virtual sp<IMediaRecorder> createMediaRecorder();
+ virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName);
void removeMediaRecorderClient(wp<MediaRecorderClient> client);
virtual sp<IMediaMetadataRetriever> createMetadataRetriever();
@@ -199,8 +204,8 @@
virtual sp<IDrm> makeDrm();
virtual sp<IHDCP> makeHDCP(bool createEncryptionModule);
- virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
- const String8& iface);
+ virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName,
+ const sp<IRemoteDisplayClient>& client, const String8& iface);
virtual status_t dump(int fd, const Vector<String16>& args);
void removeClient(wp<Client> client);
@@ -261,7 +266,11 @@
virtual status_t stop();
virtual status_t pause();
virtual status_t isPlaying(bool* state);
- virtual status_t setPlaybackRate(float rate);
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate);
+ virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
+ virtual status_t setSyncSettings(const AVSyncSettings& rate, float videoFpsHint);
+ virtual status_t getSyncSettings(AVSyncSettings* rate /* nonnull */,
+ float* videoFps /* nonnull */);
virtual status_t seekTo(int msec);
virtual status_t getCurrentPosition(int* msec);
virtual status_t getDuration(int* msec);
@@ -292,6 +301,8 @@
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual status_t setDataSource(const sp<IStreamSource> &source);
+ virtual status_t setDataSource(const sp<IDataSource> &source);
+
sp<MediaPlayerBase> setDataSource_pre(player_type playerType);
void setDataSource_post(const sp<MediaPlayerBase>& p,
@@ -301,7 +312,7 @@
int ext1, int ext2, const Parcel *obj);
pid_t pid() const { return mPid; }
- virtual status_t dump(int fd, const Vector<String16>& args) const;
+ virtual status_t dump(int fd, const Vector<String16>& args);
int getAudioSessionId() { return mAudioSessionId; }
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 4d4de9b..f761dec 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -55,6 +55,16 @@
return ok;
}
+status_t MediaRecorderClient::setInputSurface(const sp<IGraphicBufferConsumer>& surface)
+{
+ ALOGV("setInputSurface");
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ ALOGE("recorder is not initialized");
+ return NO_INIT;
+ }
+ return mRecorder->setInputSurface(surface);
+}
sp<IGraphicBufferProducer> MediaRecorderClient::querySurfaceMediaSource()
{
@@ -290,11 +300,12 @@
return NO_ERROR;
}
-MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid)
+MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid,
+ const String16& opPackageName)
{
ALOGV("Client constructor");
mPid = pid;
- mRecorder = new StagefrightRecorder;
+ mRecorder = new StagefrightRecorder(opPackageName);
mMediaPlayerService = service;
}
@@ -325,7 +336,7 @@
return mRecorder->setClientName(clientName);
}
-status_t MediaRecorderClient::dump(int fd, const Vector<String16>& args) const {
+status_t MediaRecorderClient::dump(int fd, const Vector<String16>& args) {
if (mRecorder != NULL) {
return mRecorder->dump(fd, args);
}
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index a444b6c..05130d4 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -54,7 +54,8 @@
virtual status_t init();
virtual status_t close();
virtual status_t release();
- virtual status_t dump(int fd, const Vector<String16>& args) const;
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface);
virtual sp<IGraphicBufferProducer> querySurfaceMediaSource();
private:
@@ -62,7 +63,8 @@
MediaRecorderClient(
const sp<MediaPlayerService>& service,
- pid_t pid);
+ pid_t pid,
+ const String16& opPackageName);
virtual ~MediaRecorderClient();
pid_t mPid;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 715cc0c..bec6203 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -34,6 +34,7 @@
#include <media/IMediaHTTPService.h>
#include <media/MediaMetadataRetrieverInterface.h>
#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/DataSource.h>
#include <private/media/VideoFrame.h>
#include "MetadataRetrieverClient.h"
#include "StagefrightMetadataRetriever.h"
@@ -56,7 +57,7 @@
disconnect();
}
-status_t MetadataRetrieverClient::dump(int fd, const Vector<String16>& /*args*/) const
+status_t MetadataRetrieverClient::dump(int fd, const Vector<String16>& /*args*/)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -140,7 +141,7 @@
ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
return BAD_VALUE;
}
- ALOGV("st_dev = %llu", sb.st_dev);
+ ALOGV("st_dev = %llu", static_cast<uint64_t>(sb.st_dev));
ALOGV("st_mode = %u", sb.st_mode);
ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid));
ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid));
@@ -148,7 +149,6 @@
if (offset >= sb.st_size) {
ALOGE("offset (%lld) bigger than file size (%llu)", offset, sb.st_size);
- ::close(fd);
return BAD_VALUE;
}
if (offset + length > sb.st_size) {
@@ -164,15 +164,30 @@
ALOGV("player type = %d", playerType);
sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
if (p == NULL) {
- ::close(fd);
return NO_INIT;
}
status_t status = p->setDataSource(fd, offset, length);
if (status == NO_ERROR) mRetriever = p;
- ::close(fd);
return status;
}
+status_t MetadataRetrieverClient::setDataSource(
+ const sp<IDataSource>& source)
+{
+ ALOGV("setDataSource(IDataSource)");
+ Mutex::Autolock lock(mLock);
+
+ sp<DataSource> dataSource = DataSource::CreateFromIDataSource(source);
+ player_type playerType =
+ MediaPlayerFactory::getPlayerType(NULL /* client */, dataSource);
+ ALOGV("player type = %d", playerType);
+ sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
+ if (p == NULL) return NO_INIT;
+ status_t ret = p->setDataSource(dataSource);
+ if (ret == NO_ERROR) mRetriever = p;
+ return ret;
+}
+
sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option)
{
ALOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option);
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index 9d3fbe9..e71a29e 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -49,11 +49,12 @@
const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual status_t setDataSource(const sp<IDataSource>& source);
virtual sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
virtual sp<IMemory> extractAlbumArt();
virtual const char* extractMetadata(int keyCode);
- virtual status_t dump(int fd, const Vector<String16>& args) const;
+ virtual status_t dump(int fd, const Vector<String16>& args);
private:
friend class MediaPlayerService;
diff --git a/media/libmediaplayerservice/RemoteDisplay.cpp b/media/libmediaplayerservice/RemoteDisplay.cpp
index eb959b4..0eb4b5d 100644
--- a/media/libmediaplayerservice/RemoteDisplay.cpp
+++ b/media/libmediaplayerservice/RemoteDisplay.cpp
@@ -26,13 +26,14 @@
namespace android {
RemoteDisplay::RemoteDisplay(
+ const String16 &opPackageName,
const sp<IRemoteDisplayClient> &client,
const char *iface)
: mLooper(new ALooper),
mNetSession(new ANetworkSession) {
mLooper->setName("wfd_looper");
- mSource = new WifiDisplaySource(mNetSession, client);
+ mSource = new WifiDisplaySource(opPackageName, mNetSession, client);
mLooper->registerHandler(mSource);
mNetSession->start();
diff --git a/media/libmediaplayerservice/RemoteDisplay.h b/media/libmediaplayerservice/RemoteDisplay.h
index 82a0116..d4573e9 100644
--- a/media/libmediaplayerservice/RemoteDisplay.h
+++ b/media/libmediaplayerservice/RemoteDisplay.h
@@ -28,11 +28,12 @@
struct ALooper;
struct ANetworkSession;
-struct IRemoteDisplayClient;
+class IRemoteDisplayClient;
struct WifiDisplaySource;
struct RemoteDisplay : public BnRemoteDisplay {
RemoteDisplay(
+ const String16 &opPackageName,
const sp<IRemoteDisplayClient> &client,
const char *iface);
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index b37aee3..8fc4b29 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -64,7 +64,7 @@
// the method returns, if you want to keep it, dup it!
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
- return mPlayer->setDataSource(dup(fd), offset, length);
+ return mPlayer->setDataSource(fd, offset, length);
}
status_t StagefrightPlayer::setDataSource(const sp<IStreamSource> &source) {
@@ -188,6 +188,14 @@
return mPlayer->getParameter(key, reply);
}
+status_t StagefrightPlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+ return mPlayer->setPlaybackSettings(rate);
+}
+
+status_t StagefrightPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+ return mPlayer->getPlaybackSettings(rate);
+}
+
status_t StagefrightPlayer::getMetadata(
const media::Metadata::Filter& /* ids */, Parcel *records) {
using media::Metadata;
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index e6c30ff..96013df 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -60,6 +60,8 @@
virtual void setAudioSink(const sp<AudioSink> &audioSink);
virtual status_t setParameter(int key, const Parcel &request);
virtual status_t getParameter(int key, Parcel *reply);
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+ virtual status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
virtual status_t getMetadata(
const media::Metadata::Filter& ids, Parcel *records);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 55763f0..e16a4b5 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -69,8 +69,9 @@
}
-StagefrightRecorder::StagefrightRecorder()
- : mWriter(NULL),
+StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName)
+ : MediaRecorderBase(opPackageName),
+ mWriter(NULL),
mOutputFd(-1),
mAudioSource(AUDIO_SOURCE_CNT),
mVideoSource(VIDEO_SOURCE_LIST_END),
@@ -242,6 +243,13 @@
return OK;
}
+status_t StagefrightRecorder::setInputSurface(
+ const sp<IGraphicBufferConsumer>& surface) {
+ mPersistentSurface = surface;
+
+ return OK;
+}
+
status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) {
ALOGV("setOutputFile: %d, %lld, %lld", fd, offset, length);
// These don't make any sense, do they?
@@ -905,6 +913,7 @@
sp<AudioSource> audioSource =
new AudioSource(
mAudioSource,
+ mOpPackageName,
mSampleRate,
mAudioChannels);
@@ -916,7 +925,6 @@
}
sp<AMessage> format = new AMessage;
- const char *mime;
switch (mAudioEncoder) {
case AUDIO_ENCODER_AMR_NB:
case AUDIO_ENCODER_DEFAULT:
@@ -954,6 +962,7 @@
if (mAudioTimeScale > 0) {
format->setInt32("time-scale", mAudioTimeScale);
}
+ format->setInt32("priority", 0 /* realtime */);
sp<MediaSource> audioEncoder =
MediaCodecSource::Create(mLooper, format, audioSource);
@@ -1544,6 +1553,11 @@
format->setInt32("level", mVideoEncoderLevel);
}
+ format->setInt32("priority", 0 /* realtime */);
+ if (mCaptureTimeLapse) {
+ format->setFloat("operating-rate", mCaptureFps);
+ }
+
uint32_t flags = 0;
if (mIsMetaDataStoredInVideoBuffers) {
flags |= MediaCodecSource::FLAG_USE_METADATA_INPUT;
@@ -1553,8 +1567,8 @@
flags |= MediaCodecSource::FLAG_USE_SURFACE_INPUT;
}
- sp<MediaCodecSource> encoder =
- MediaCodecSource::Create(mLooper, format, cameraSource, flags);
+ sp<MediaCodecSource> encoder = MediaCodecSource::Create(
+ mLooper, format, cameraSource, mPersistentSurface, flags);
if (encoder == NULL) {
ALOGE("Failed to create video encoder");
// When the encoder fails to be created, we need
@@ -1738,6 +1752,7 @@
}
mGraphicBufferProducer.clear();
+ mPersistentSurface.clear();
if (mOutputFd >= 0) {
::close(mOutputFd);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index f34c229..7473f42 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -35,12 +35,13 @@
class MetaData;
struct AudioSource;
class MediaProfiles;
+class IGraphicBufferConsumer;
class IGraphicBufferProducer;
class SurfaceMediaSource;
-class ALooper;
+struct ALooper;
struct StagefrightRecorder : public MediaRecorderBase {
- StagefrightRecorder();
+ StagefrightRecorder(const String16 &opPackageName);
virtual ~StagefrightRecorder();
virtual status_t init();
@@ -53,6 +54,7 @@
virtual status_t setVideoFrameRate(int frames_per_second);
virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy);
virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface);
+ virtual status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface);
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
virtual status_t setParameters(const String8& params);
virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
@@ -72,6 +74,7 @@
sp<ICamera> mCamera;
sp<ICameraRecordingProxy> mCameraProxy;
sp<IGraphicBufferProducer> mPreviewSurface;
+ sp<IGraphicBufferConsumer> mPersistentSurface;
sp<IMediaRecorderClient> mListener;
String16 mClientName;
uid_t mClientUid;
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.h b/media/libmediaplayerservice/VideoFrameScheduler.h
index 84b27b4..b1765c9 100644
--- a/media/libmediaplayerservice/VideoFrameScheduler.h
+++ b/media/libmediaplayerservice/VideoFrameScheduler.h
@@ -24,7 +24,7 @@
namespace android {
-struct ISurfaceComposer;
+class ISurfaceComposer;
struct VideoFrameScheduler : public RefBase {
VideoFrameScheduler();
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index 6609874..20193c3 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -16,6 +16,7 @@
StreamingSource.cpp \
LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/av/media/libstagefright/httplive \
$(TOP)/frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/av/media/libstagefright/mpeg2ts \
@@ -24,6 +25,9 @@
$(TOP)/frameworks/av/media/libmediaplayerservice \
$(TOP)/frameworks/native/include/media/openmax
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
LOCAL_MODULE:= libstagefright_nuplayer
LOCAL_MODULE_TAGS := eng
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 5a31b74..5e7b644 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -124,6 +124,12 @@
return OK;
}
+status_t NuPlayer::GenericSource::setDataSource(const sp<DataSource>& source) {
+ resetDataSource();
+ mDataSource = source;
+ return OK;
+}
+
sp<MetaData> NuPlayer::GenericSource::getFileFormatMeta() const {
return mFileMeta;
}
@@ -377,20 +383,20 @@
notifyPreparedAndCleanup(UNKNOWN_ERROR);
return;
}
-
- if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
- mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
- }
-
- // For widevine or other cached streaming cases, we need to wait for
- // enough buffering before reporting prepared.
- // Note that even when URL doesn't start with widevine://, mIsWidevine
- // could still be set to true later, if the streaming or file source
- // is sniffed to be widevine. We don't want to buffer for file source
- // in that case, so must check the flag now.
- mIsStreaming = (mIsWidevine || mCachedSource != NULL);
}
+ if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+ mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
+ }
+
+ // For widevine or other cached streaming cases, we need to wait for
+ // enough buffering before reporting prepared.
+ // Note that even when URL doesn't start with widevine://, mIsWidevine
+ // could still be set to true later, if the streaming or file source
+ // is sniffed to be widevine. We don't want to buffer for file source
+ // in that case, so must check the flag now.
+ mIsStreaming = (mIsWidevine || mCachedSource != NULL);
+
// init extractor from data source
status_t err = initFromDataSource();
@@ -697,7 +703,7 @@
stopBufferingIfNecessary();
}
} else if (cachedDataRemaining >= 0) {
- ALOGV("onPollBuffering: cachedDataRemaining %d bytes",
+ ALOGV("onPollBuffering: cachedDataRemaining %zd bytes",
cachedDataRemaining);
if (cachedDataRemaining < kLowWaterMarkBytes) {
@@ -784,7 +790,7 @@
}
readBuffer(trackType, timeUs, &actualTimeUs, formatChange);
readBuffer(counterpartType, -1, NULL, formatChange);
- ALOGV("timeUs %lld actualTimeUs %lld", timeUs, actualTimeUs);
+ ALOGV("timeUs %lld actualTimeUs %lld", (long long)timeUs, (long long)actualTimeUs);
break;
}
@@ -1059,6 +1065,7 @@
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
+ format->setString("mime", mime);
int32_t trackType;
if (!strncasecmp(mime, "video/", 6)) {
@@ -1079,8 +1086,6 @@
format->setString("language", lang);
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- format->setString("mime", mime);
-
int32_t isAutoselect = 1, isDefault = 0, isForced = 0;
meta->findInt32(kKeyTrackIsAutoselect, &isAutoselect);
meta->findInt32(kKeyTrackIsDefault, &isDefault);
@@ -1394,6 +1399,14 @@
meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
}
+ uint32_t dataType; // unused
+ const void *seiData;
+ size_t seiLength;
+ if (mb->meta_data()->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
+ sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
+ meta->setBuffer("sei", sei);
+ }
+
if (actualTimeUs) {
*actualTimeUs = timeUs;
}
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 862ee5f..7fab051 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -31,12 +31,13 @@
class DrmManagerClient;
struct AnotherPacketSource;
struct ARTSPController;
-struct DataSource;
+class DataSource;
+class IDataSource;
struct IMediaHTTPService;
struct MediaSource;
class MediaBuffer;
struct NuCachedSource2;
-struct WVMExtractor;
+class WVMExtractor;
struct NuPlayer::GenericSource : public NuPlayer::Source {
GenericSource(const sp<AMessage> ¬ify, bool uidValid, uid_t uid);
@@ -48,6 +49,8 @@
status_t setDataSource(int fd, int64_t offset, int64_t length);
+ status_t setDataSource(const sp<DataSource>& dataSource);
+
virtual void prepareAsync();
virtual void start();
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 0476c9b..39b8d09 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -22,7 +22,6 @@
#include "AnotherPacketSource.h"
#include "LiveDataSource.h"
-#include "LiveSession.h"
#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -30,6 +29,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaDefs.h>
namespace android {
@@ -44,7 +44,10 @@
mFlags(0),
mFinalResult(OK),
mOffset(0),
- mFetchSubtitleDataGeneration(0) {
+ mFetchSubtitleDataGeneration(0),
+ mFetchMetaDataGeneration(0),
+ mHasMetadata(false),
+ mMetadataSelected(false) {
if (headers) {
mExtraHeaders = *headers;
@@ -142,19 +145,49 @@
ssize_t NuPlayer::HTTPLiveSource::getSelectedTrack(media_track_type type) const {
if (mLiveSession == NULL) {
return -1;
+ } else if (type == MEDIA_TRACK_TYPE_METADATA) {
+ // MEDIA_TRACK_TYPE_METADATA is always last track
+ // mMetadataSelected can only be true when mHasMetadata is true
+ return mMetadataSelected ? (mLiveSession->getTrackCount() - 1) : -1;
} else {
return mLiveSession->getSelectedTrack(type);
}
}
status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select, int64_t /*timeUs*/) {
- status_t err = mLiveSession->selectTrack(trackIndex, select);
+ if (mLiveSession == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ status_t err = INVALID_OPERATION;
+ bool postFetchMsg = false, isSub = false;
+ if (trackIndex != mLiveSession->getTrackCount() - 1) {
+ err = mLiveSession->selectTrack(trackIndex, select);
+ postFetchMsg = select;
+ isSub = true;
+ } else {
+ // metadata track
+ if (mHasMetadata) {
+ if (mMetadataSelected && !select) {
+ err = OK;
+ } else if (!mMetadataSelected && select) {
+ postFetchMsg = true;
+ err = OK;
+ } else {
+ err = BAD_VALUE; // behave as LiveSession::selectTrack
+ }
+
+ mMetadataSelected = select;
+ }
+ }
if (err == OK) {
- mFetchSubtitleDataGeneration++;
- if (select) {
- sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
- msg->setInt32("generation", mFetchSubtitleDataGeneration);
+ int32_t &generation = isSub ? mFetchSubtitleDataGeneration : mFetchMetaDataGeneration;
+ generation++;
+ if (postFetchMsg) {
+ int32_t what = isSub ? kWhatFetchSubtitleData : kWhatFetchMetaData;
+ sp<AMessage> msg = new AMessage(what, this);
+ msg->setInt32("generation", generation);
msg->post();
}
}
@@ -169,6 +202,49 @@
return mLiveSession->seekTo(seekTimeUs);
}
+void NuPlayer::HTTPLiveSource::pollForRawData(
+ const sp<AMessage> &msg, int32_t currentGeneration,
+ LiveSession::StreamType fetchType, int32_t pushWhat) {
+
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != currentGeneration) {
+ return;
+ }
+
+ sp<ABuffer> buffer;
+ while (mLiveSession->dequeueAccessUnit(fetchType, &buffer) == OK) {
+
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", pushWhat);
+ notify->setBuffer("buffer", buffer);
+
+ int64_t timeUs, baseUs, delayUs;
+ CHECK(buffer->meta()->findInt64("baseUs", &baseUs));
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+ delayUs = baseUs + timeUs - ALooper::GetNowUs();
+
+ if (fetchType == LiveSession::STREAMTYPE_SUBTITLES) {
+ notify->post();
+ msg->post(delayUs > 0ll ? delayUs : 0ll);
+ return;
+ } else if (fetchType == LiveSession::STREAMTYPE_METADATA) {
+ if (delayUs < -1000000ll) { // 1 second
+ continue;
+ }
+ notify->post();
+ // push all currently available metadata buffers in each invocation of pollForRawData
+ // continue;
+ } else {
+ TRESPASS();
+ }
+ }
+
+ // try again in 1 second
+ msg->post(1000000ll);
+}
+
void NuPlayer::HTTPLiveSource::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSessionNotify:
@@ -179,33 +255,24 @@
case kWhatFetchSubtitleData:
{
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
+ pollForRawData(
+ msg, mFetchSubtitleDataGeneration,
+ /* fetch */ LiveSession::STREAMTYPE_SUBTITLES,
+ /* push */ kWhatSubtitleData);
- if (generation != mFetchSubtitleDataGeneration) {
- // stale
+ break;
+ }
+
+ case kWhatFetchMetaData:
+ {
+ if (!mMetadataSelected) {
break;
}
- sp<ABuffer> buffer;
- if (mLiveSession->dequeueAccessUnit(
- LiveSession::STREAMTYPE_SUBTITLES, &buffer) == OK) {
- sp<AMessage> notify = dupNotify();
- notify->setInt32("what", kWhatSubtitleData);
- notify->setBuffer("buffer", buffer);
- notify->post();
-
- int64_t timeUs, baseUs, durationUs, delayUs;
- CHECK(buffer->meta()->findInt64("baseUs", &baseUs));
- CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
- CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
- delayUs = baseUs + timeUs - ALooper::GetNowUs();
-
- msg->post(delayUs > 0ll ? delayUs : 0ll);
- } else {
- // try again in 1 second
- msg->post(1000000ll);
- }
+ pollForRawData(
+ msg, mFetchMetaDataGeneration,
+ /* fetch */ LiveSession::STREAMTYPE_METADATA,
+ /* push */ kWhatTimedMetaData);
break;
}
@@ -309,6 +376,19 @@
break;
}
+ case LiveSession::kWhatMetadataDetected:
+ {
+ if (!mHasMetadata) {
+ mHasMetadata = true;
+
+ sp<AMessage> notify = dupNotify();
+ // notification without buffer triggers MEDIA_INFO_METADATA_UPDATE
+ notify->setInt32("what", kWhatTimedMetaData);
+ notify->post();
+ }
+ break;
+ }
+
case LiveSession::kWhatError:
{
break;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index bbb8981..9e0ec2f 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -21,6 +21,8 @@
#include "NuPlayer.h"
#include "NuPlayerSource.h"
+#include "LiveSession.h"
+
namespace android {
struct LiveSession;
@@ -60,6 +62,7 @@
enum {
kWhatSessionNotify,
kWhatFetchSubtitleData,
+ kWhatFetchMetaData,
};
sp<IMediaHTTPService> mHTTPService;
@@ -71,8 +74,14 @@
sp<ALooper> mLiveLooper;
sp<LiveSession> mLiveSession;
int32_t mFetchSubtitleDataGeneration;
+ int32_t mFetchMetaDataGeneration;
+ bool mHasMetadata;
+ bool mMetadataSelected;
void onSessionNotify(const sp<AMessage> &msg);
+ void pollForRawData(
+ const sp<AMessage> &msg, int32_t currentGeneration,
+ LiveSession::StreamType fetchType, int32_t pushWhat);
DISALLOW_EVIL_CONSTRUCTORS(HTTPLiveSource);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 02d9f32..1fb4365 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -37,6 +37,9 @@
#include <cutils/properties.h>
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
+
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -45,7 +48,9 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
#include "avc_utils.h"
@@ -96,16 +101,16 @@
};
struct NuPlayer::SetSurfaceAction : public Action {
- SetSurfaceAction(const sp<NativeWindowWrapper> &wrapper)
- : mWrapper(wrapper) {
+ SetSurfaceAction(const sp<Surface> &surface)
+ : mSurface(surface) {
}
virtual void execute(NuPlayer *player) {
- player->performSetSurface(mWrapper);
+ player->performSetSurface(mSurface);
}
private:
- sp<NativeWindowWrapper> mWrapper;
+ sp<Surface> mSurface;
DISALLOW_EVIL_CONSTRUCTORS(SetSurfaceAction);
};
@@ -180,10 +185,12 @@
mFlushingVideo(NONE),
mResumePending(false),
mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
- mPlaybackRate(1.0),
+ mPlaybackSettings(AUDIO_PLAYBACK_RATE_DEFAULT),
+ mVideoFpsHint(-1.f),
mStarted(false),
mPaused(false),
- mPausedByClient(false) {
+ mPausedByClient(false),
+ mPausedForBuffering(false) {
clearFlushComplete();
}
@@ -285,21 +292,34 @@
msg->post();
}
+void NuPlayer::setDataSourceAsync(const sp<DataSource> &dataSource) {
+ sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
+ sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
+
+ sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID);
+ status_t err = source->setDataSource(dataSource);
+
+ if (err != OK) {
+ ALOGE("Failed to set data source!");
+ source = NULL;
+ }
+
+ msg->setObject("source", source);
+ msg->post();
+}
+
void NuPlayer::prepareAsync() {
(new AMessage(kWhatPrepare, this))->post();
}
void NuPlayer::setVideoSurfaceTextureAsync(
const sp<IGraphicBufferProducer> &bufferProducer) {
- sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, this);
+ sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
if (bufferProducer == NULL) {
- msg->setObject("native-window", NULL);
+ msg->setObject("surface", NULL);
} else {
- msg->setObject(
- "native-window",
- new NativeWindowWrapper(
- new Surface(bufferProducer, true /* controlledByApp */)));
+ msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
}
msg->post();
@@ -315,10 +335,61 @@
(new AMessage(kWhatStart, this))->post();
}
-void NuPlayer::setPlaybackRate(float rate) {
- sp<AMessage> msg = new AMessage(kWhatSetRate, this);
- msg->setFloat("rate", rate);
- msg->post();
+status_t NuPlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+ // do some cursory validation of the settings here. audio modes are
+ // only validated when set on the audiosink.
+ if ((rate.mSpeed != 0.f && rate.mSpeed < AUDIO_TIMESTRETCH_SPEED_MIN)
+ || rate.mSpeed > AUDIO_TIMESTRETCH_SPEED_MAX
+ || rate.mPitch < AUDIO_TIMESTRETCH_SPEED_MIN
+ || rate.mPitch > AUDIO_TIMESTRETCH_SPEED_MAX) {
+ return BAD_VALUE;
+ }
+ sp<AMessage> msg = new AMessage(kWhatConfigPlayback, this);
+ writeToAMessage(msg, rate);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ }
+ return err;
+}
+
+status_t NuPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+ sp<AMessage> msg = new AMessage(kWhatGetPlaybackSettings, this);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ if (err == OK) {
+ readFromAMessage(response, rate);
+ }
+ }
+ return err;
+}
+
+status_t NuPlayer::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
+ sp<AMessage> msg = new AMessage(kWhatConfigSync, this);
+ writeToAMessage(msg, sync, videoFpsHint);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ }
+ return err;
+}
+
+status_t NuPlayer::getSyncSettings(
+ AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */) {
+ sp<AMessage> msg = new AMessage(kWhatGetSyncSettings, this);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ if (err == OK) {
+ readFromAMessage(response, sync, videoFps);
+ }
+ }
+ return err;
}
void NuPlayer::pause() {
@@ -352,23 +423,35 @@
int32_t trackType;
CHECK(format->findInt32("type", &trackType));
+ AString mime;
+ if (!format->findString("mime", &mime)) {
+ // Java MediaPlayer only uses mimetype for subtitle and timedtext tracks.
+ // If we can't find the mimetype here it means that we wouldn't be needing
+ // the mimetype on the Java end. We still write a placeholder mime to keep the
+ // (de)serialization logic simple.
+ if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
+ mime = "audio/";
+ } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
+ mime = "video/";
+ } else {
+ TRESPASS();
+ }
+ }
+
AString lang;
CHECK(format->findString("language", &lang));
reply->writeInt32(2); // write something non-zero
reply->writeInt32(trackType);
+ reply->writeString16(String16(mime.c_str()));
reply->writeString16(String16(lang.c_str()));
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- AString mime;
- CHECK(format->findString("mime", &mime));
-
int32_t isAuto, isDefault, isForced;
CHECK(format->findInt32("auto", &isAuto));
CHECK(format->findInt32("default", &isDefault));
CHECK(format->findInt32("forced", &isForced));
- reply->writeString16(String16(mime.c_str()));
reply->writeInt32(isAuto);
reply->writeInt32(isDefault);
reply->writeInt32(isForced);
@@ -539,15 +622,15 @@
break;
}
- case kWhatSetVideoNativeWindow:
+ case kWhatSetVideoSurface:
{
- ALOGV("kWhatSetVideoNativeWindow");
+ ALOGV("kWhatSetVideoSurface");
sp<RefBase> obj;
- CHECK(msg->findObject("native-window", &obj));
-
+ CHECK(msg->findObject("surface", &obj));
+ sp<Surface> surface = static_cast<Surface *>(obj.get());
if (mSource == NULL || mSource->getFormat(false /* audio */) == NULL) {
- performSetSurface(static_cast<NativeWindowWrapper *>(obj.get()));
+ performSetSurface(surface);
break;
}
@@ -555,9 +638,7 @@
new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
FLUSH_CMD_SHUTDOWN /* video */));
- mDeferredActions.push_back(
- new SetSurfaceAction(
- static_cast<NativeWindowWrapper *>(obj.get())));
+ mDeferredActions.push_back(new SetSurfaceAction(surface));
if (obj != NULL) {
if (mStarted) {
@@ -603,7 +684,10 @@
{
ALOGV("kWhatStart");
if (mStarted) {
- onResume();
+ // do not resume yet if the source is still buffering
+ if (!mPausedForBuffering) {
+ onResume();
+ }
} else {
onStart();
}
@@ -611,13 +695,114 @@
break;
}
- case kWhatSetRate:
+ case kWhatConfigPlayback:
{
- ALOGV("kWhatSetRate");
- CHECK(msg->findFloat("rate", &mPlaybackRate));
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ AudioPlaybackRate rate /* sanitized */;
+ readFromAMessage(msg, &rate);
+ status_t err = OK;
if (mRenderer != NULL) {
- mRenderer->setPlaybackRate(mPlaybackRate);
+ err = mRenderer->setPlaybackSettings(rate);
}
+ if (err == OK) {
+ if (rate.mSpeed == 0.f) {
+ onPause();
+ // save all other settings (using non-paused speed)
+ // so we can restore them on start
+ AudioPlaybackRate newRate = rate;
+ newRate.mSpeed = mPlaybackSettings.mSpeed;
+ mPlaybackSettings = newRate;
+ } else { /* rate.mSpeed != 0.f */
+ onResume();
+ mPlaybackSettings = rate;
+ }
+ }
+
+ if (mVideoDecoder != NULL) {
+ float rate = getFrameRate();
+ if (rate > 0) {
+ sp<AMessage> params = new AMessage();
+ params->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
+ mVideoDecoder->setParameters(params);
+ }
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatGetPlaybackSettings:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ AudioPlaybackRate rate = mPlaybackSettings;
+ status_t err = OK;
+ if (mRenderer != NULL) {
+ err = mRenderer->getPlaybackSettings(&rate);
+ }
+ if (err == OK) {
+ // get playback settings used by renderer, as it may be
+ // slightly off due to audiosink not taking small changes.
+ mPlaybackSettings = rate;
+ if (mPaused) {
+ rate.mSpeed = 0.f;
+ }
+ }
+ sp<AMessage> response = new AMessage;
+ if (err == OK) {
+ writeToAMessage(response, rate);
+ }
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatConfigSync:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ ALOGV("kWhatConfigSync");
+ AVSyncSettings sync;
+ float videoFpsHint;
+ readFromAMessage(msg, &sync, &videoFpsHint);
+ status_t err = OK;
+ if (mRenderer != NULL) {
+ err = mRenderer->setSyncSettings(sync, videoFpsHint);
+ }
+ if (err == OK) {
+ mSyncSettings = sync;
+ mVideoFpsHint = videoFpsHint;
+ }
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatGetSyncSettings:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ AVSyncSettings sync = mSyncSettings;
+ float videoFps = mVideoFpsHint;
+ status_t err = OK;
+ if (mRenderer != NULL) {
+ err = mRenderer->getSyncSettings(&sync, &videoFps);
+ if (err == OK) {
+ mSyncSettings = sync;
+ mVideoFpsHint = videoFps;
+ }
+ }
+ sp<AMessage> response = new AMessage;
+ if (err == OK) {
+ writeToAMessage(response, sync, videoFps);
+ }
+ response->setInt32("err", err);
+ response->postReply(replyID);
break;
}
@@ -640,7 +825,7 @@
// initialize video before audio because successful initialization of
// video may change deep buffer mode of audio.
- if (mNativeWindow != NULL) {
+ if (mSurface != NULL) {
instantiateDecoder(false, &mVideoDecoder);
}
@@ -688,7 +873,7 @@
}
if ((mAudioDecoder == NULL && mAudioSink != NULL)
- || (mVideoDecoder == NULL && mNativeWindow != NULL)) {
+ || (mVideoDecoder == NULL && mSurface != NULL)) {
msg->post(100000ll);
mScanSourcesPending = true;
}
@@ -955,7 +1140,7 @@
CHECK(msg->findInt32("needNotify", &needNotify));
ALOGV("kWhatSeek seekTimeUs=%lld us, needNotify=%d",
- seekTimeUs, needNotify);
+ (long long)seekTimeUs, needNotify);
mDeferredActions.push_back(
new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
@@ -1034,7 +1219,7 @@
// TRICKY: We rely on mRenderer being null, so that decoder does not start requesting
// data on instantiation.
- if (mNativeWindow != NULL) {
+ if (mSurface != NULL) {
err = instantiateDecoder(false, &mVideoDecoder);
if (err != OK) {
return err;
@@ -1087,14 +1272,16 @@
mRendererLooper->setName("NuPlayerRenderer");
mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
mRendererLooper->registerHandler(mRenderer);
- if (mPlaybackRate != 1.0) {
- mRenderer->setPlaybackRate(mPlaybackRate);
+
+ status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings);
+ if (err != OK) {
+ mSource->stop();
+ notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ return;
}
- sp<MetaData> meta = getFileMeta();
- int32_t rate;
- if (meta != NULL
- && meta->findInt32(kKeyFrameRate, &rate) && rate > 0) {
+ float rate = getFrameRate();
+ if (rate > 0) {
mRenderer->setVideoFrameRate(rate);
}
@@ -1233,6 +1420,8 @@
return -EWOULDBLOCK;
}
+ format->setInt32("priority", 0 /* realtime */);
+
if (!audio) {
AString mime;
CHECK(format->findString("mime", &mime));
@@ -1249,6 +1438,11 @@
if (mSourceFlags & Source::FLAG_PROTECTED) {
format->setInt32("protected", true);
}
+
+ float rate = getFrameRate();
+ if (rate > 0) {
+ format->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
+ }
}
if (audio) {
@@ -1269,10 +1463,10 @@
notify->setInt32("generation", mVideoDecoderGeneration);
*decoder = new Decoder(
- notify, mSource, mRenderer, mNativeWindow, mCCDecoder);
+ notify, mSource, mRenderer, mSurface, mCCDecoder);
// enable FRC if high-quality AV sync is requested, even if not
- // queuing to native window, as this will even improve textureview
+ // directly queuing to display, as this will even improve textureview
// playback.
{
char value[PROPERTY_VALUE_MAX];
@@ -1320,8 +1514,6 @@
}
int32_t displayWidth, displayHeight;
- int32_t cropLeft, cropTop, cropRight, cropBottom;
-
if (outputFormat != NULL) {
int32_t width, height;
CHECK(outputFormat->findInt32("width", &width));
@@ -1403,7 +1595,11 @@
// Make sure we don't continue to scan sources until we finish flushing.
++mScanSourcesGeneration;
- mScanSourcesPending = false;
+ if (mScanSourcesPending) {
+ mDeferredActions.push_back(
+ new SimpleAction(&NuPlayer::performScanSources));
+ mScanSourcesPending = false;
+ }
decoder->signalFlush();
@@ -1442,9 +1638,8 @@
status_t NuPlayer::setVideoScalingMode(int32_t mode) {
mVideoScalingMode = mode;
- if (mNativeWindow != NULL) {
- status_t ret = native_window_set_scaling_mode(
- mNativeWindow->getNativeWindow().get(), mVideoScalingMode);
+ if (mSurface != NULL) {
+ status_t ret = native_window_set_scaling_mode(mSurface.get(), mVideoScalingMode);
if (ret != OK) {
ALOGE("Failed to set scaling mode (%d): %s",
-ret, strerror(-ret));
@@ -1519,6 +1714,28 @@
return mSource->getFileFormatMeta();
}
+float NuPlayer::getFrameRate() {
+ sp<MetaData> meta = mSource->getFormatMeta(false /* audio */);
+ if (meta == NULL) {
+ return 0;
+ }
+ int32_t rate;
+ if (!meta->findInt32(kKeyFrameRate, &rate)) {
+ // fall back to try file meta
+ sp<MetaData> fileMeta = getFileMeta();
+ if (fileMeta == NULL) {
+ ALOGW("source has video meta but not file meta");
+ return -1;
+ }
+ int32_t fileMetaRate;
+ if (!fileMeta->findInt32(kKeyFrameRate, &fileMetaRate)) {
+ return -1;
+ }
+ return fileMetaRate;
+ }
+ return rate;
+}
+
void NuPlayer::schedulePollDuration() {
sp<AMessage> msg = new AMessage(kWhatPollDuration, this);
msg->setInt32("generation", mPollDurationGeneration);
@@ -1554,7 +1771,7 @@
void NuPlayer::performSeek(int64_t seekTimeUs, bool needNotify) {
ALOGV("performSeek seekTimeUs=%lld us (%.2f secs), needNotify(%d)",
- seekTimeUs,
+ (long long)seekTimeUs,
seekTimeUs / 1E6,
needNotify);
@@ -1638,10 +1855,10 @@
}
}
-void NuPlayer::performSetSurface(const sp<NativeWindowWrapper> &wrapper) {
+void NuPlayer::performSetSurface(const sp<Surface> &surface) {
ALOGV("performSetSurface");
- mNativeWindow = wrapper;
+ mSurface = surface;
// XXX - ignore error from setVideoScalingMode for now
setVideoScalingMode(mVideoScalingMode);
@@ -1794,9 +2011,10 @@
case Source::kWhatPauseOnBufferingStart:
{
// ignore if not playing
- if (mStarted && !mPausedByClient) {
+ if (mStarted) {
ALOGI("buffer low, pausing...");
+ mPausedForBuffering = true;
onPause();
}
// fall-thru
@@ -1811,10 +2029,15 @@
case Source::kWhatResumeOnBufferingEnd:
{
// ignore if not playing
- if (mStarted && !mPausedByClient) {
+ if (mStarted) {
ALOGI("buffer ready, resuming...");
- onResume();
+ mPausedForBuffering = false;
+
+ // do not resume yet if client didn't unpause
+ if (!mPausedByClient) {
+ onResume();
+ }
}
// fall-thru
}
@@ -1843,6 +2066,17 @@
break;
}
+ case Source::kWhatTimedMetaData:
+ {
+ sp<ABuffer> buffer;
+ if (!msg->findBuffer("buffer", &buffer)) {
+ notifyListener(MEDIA_INFO, MEDIA_INFO_METADATA_UPDATE, 0);
+ } else {
+ sendTimedMetaData(buffer);
+ }
+ break;
+ }
+
case Source::kWhatTimedTextData:
{
int32_t generation;
@@ -1951,6 +2185,19 @@
notifyListener(MEDIA_SUBTITLE_DATA, 0, 0, &in);
}
+void NuPlayer::sendTimedMetaData(const sp<ABuffer> &buffer) {
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ Parcel in;
+ in.writeInt64(timeUs);
+ in.writeInt32(buffer->size());
+ in.writeInt32(buffer->size());
+ in.write(buffer->data(), buffer->size());
+
+ notifyListener(MEDIA_META_DATA, 0, 0, &in);
+}
+
void NuPlayer::sendTimedTextData(const sp<ABuffer> &buffer) {
const void *data;
size_t size = 0;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 2bc20d7..df9debc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -18,14 +18,17 @@
#define NU_PLAYER_H_
+#include <media/AudioResamplerPublic.h>
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/NativeWindowWrapper.h>
namespace android {
struct ABuffer;
struct AMessage;
+struct AudioPlaybackRate;
+struct AVSyncSettings;
+class IDataSource;
class MetaData;
struct NuPlayerDriver;
@@ -45,13 +48,19 @@
void setDataSourceAsync(int fd, int64_t offset, int64_t length);
+ void setDataSourceAsync(const sp<DataSource> &source);
+
void prepareAsync();
void setVideoSurfaceTextureAsync(
const sp<IGraphicBufferProducer> &bufferProducer);
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
- void setPlaybackRate(float rate);
+ status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+ status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+ status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
+ status_t getSyncSettings(AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
+
void start();
void pause();
@@ -71,6 +80,7 @@
void getStats(int64_t *mNumFramesTotal, int64_t *mNumFramesDropped);
sp<MetaData> getFileMeta();
+ float getFrameRate();
protected:
virtual ~NuPlayer();
@@ -102,10 +112,13 @@
enum {
kWhatSetDataSource = '=DaS',
kWhatPrepare = 'prep',
- kWhatSetVideoNativeWindow = '=NaW',
+ kWhatSetVideoSurface = '=VSu',
kWhatSetAudioSink = '=AuS',
kWhatMoreDataQueued = 'more',
- kWhatSetRate = 'setR',
+ kWhatConfigPlayback = 'cfPB',
+ kWhatConfigSync = 'cfSy',
+ kWhatGetPlaybackSettings = 'gPbS',
+ kWhatGetSyncSettings = 'gSyS',
kWhatStart = 'strt',
kWhatScanSources = 'scan',
kWhatVideoNotify = 'vidN',
@@ -128,7 +141,7 @@
uid_t mUID;
sp<Source> mSource;
uint32_t mSourceFlags;
- sp<NativeWindowWrapper> mNativeWindow;
+ sp<Surface> mSurface;
sp<MediaPlayerBase::AudioSink> mAudioSink;
sp<DecoderBase> mVideoDecoder;
bool mOffloadAudio;
@@ -177,7 +190,9 @@
int32_t mVideoScalingMode;
- float mPlaybackRate;
+ AudioPlaybackRate mPlaybackSettings;
+ AVSyncSettings mSyncSettings;
+ float mVideoFpsHint;
bool mStarted;
// Actual pause state, either as requested by client or due to buffering.
@@ -188,6 +203,9 @@
// still become true, when we pause internally due to buffering.
bool mPausedByClient;
+ // Pause state as requested by source (internally) due to buffering
+ bool mPausedForBuffering;
+
inline const sp<DecoderBase> &getDecoder(bool audio) {
return audio ? mAudioDecoder : mVideoDecoder;
}
@@ -236,7 +254,7 @@
void performDecoderFlush(FlushCommand audio, FlushCommand video);
void performReset();
void performScanSources();
- void performSetSurface(const sp<NativeWindowWrapper> &wrapper);
+ void performSetSurface(const sp<Surface> &wrapper);
void performResumeDecoders(bool needNotify);
void onSourceNotify(const sp<AMessage> &msg);
@@ -246,6 +264,7 @@
bool audio, bool video, const sp<AMessage> &reply);
void sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex);
+ void sendTimedMetaData(const sp<ABuffer> &buffer);
void sendTimedTextData(const sp<ABuffer> &buffer);
void writeTrackInfo(Parcel* reply, const sp<AMessage> format) const;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index cf3e8ad..ac3c6b6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -51,6 +51,7 @@
return cc->mData1 < 0x10 && cc->mData2 < 0x10;
}
+static void dumpBytePair(const sp<ABuffer> &ccBuf) __attribute__ ((unused));
static void dumpBytePair(const sp<ABuffer> &ccBuf) {
size_t offset = 0;
AString out;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 04ac699..376c93a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -33,6 +33,8 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
+#include <gui/Surface.h>
+
#include "avc_utils.h"
#include "ATSParser.h"
@@ -42,10 +44,10 @@
const sp<AMessage> ¬ify,
const sp<Source> &source,
const sp<Renderer> &renderer,
- const sp<NativeWindowWrapper> &nativeWindow,
+ const sp<Surface> &surface,
const sp<CCDecoder> &ccDecoder)
: DecoderBase(notify),
- mNativeWindow(nativeWindow),
+ mSurface(surface),
mSource(source),
mRenderer(renderer),
mCCDecoder(ccDecoder),
@@ -82,25 +84,71 @@
switch (msg->what()) {
case kWhatCodecNotify:
{
- if (!isStaleReply(msg)) {
- int32_t numInput, numOutput;
+ int32_t cbID;
+ CHECK(msg->findInt32("callbackID", &cbID));
- if (!msg->findInt32("input-buffers", &numInput)) {
- numInput = INT32_MAX;
- }
+ ALOGV("[%s] kWhatCodecNotify: cbID = %d, paused = %d",
+ mIsAudio ? "audio" : "video", cbID, mPaused);
- if (!msg->findInt32("output-buffers", &numOutput)) {
- numOutput = INT32_MAX;
- }
-
- if (!mPaused) {
- while (numInput-- > 0 && handleAnInputBuffer()) {}
- }
-
- while (numOutput-- > 0 && handleAnOutputBuffer()) {}
+ if (mPaused) {
+ break;
}
- requestCodecNotification();
+ switch (cbID) {
+ case MediaCodec::CB_INPUT_AVAILABLE:
+ {
+ int32_t index;
+ CHECK(msg->findInt32("index", &index));
+
+ handleAnInputBuffer(index);
+ break;
+ }
+
+ case MediaCodec::CB_OUTPUT_AVAILABLE:
+ {
+ int32_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ int32_t flags;
+
+ CHECK(msg->findInt32("index", &index));
+ CHECK(msg->findSize("offset", &offset));
+ CHECK(msg->findSize("size", &size));
+ CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("flags", &flags));
+
+ handleAnOutputBuffer(index, offset, size, timeUs, flags);
+ break;
+ }
+
+ case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
+ {
+ sp<AMessage> format;
+ CHECK(msg->findMessage("format", &format));
+
+ handleOutputFormatChange(format);
+ break;
+ }
+
+ case MediaCodec::CB_ERROR:
+ {
+ status_t err;
+ CHECK(msg->findInt32("err", &err));
+ ALOGE("Decoder (%s) reported error : 0x%x",
+ mIsAudio ? "audio" : "video", err);
+
+ handleError(err);
+ break;
+ }
+
+ default:
+ {
+ TRESPASS();
+ break;
+ }
+ }
+
break;
}
@@ -132,14 +180,9 @@
mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
- sp<Surface> surface = NULL;
- if (mNativeWindow != NULL) {
- surface = mNativeWindow->getSurfaceTextureClient();
- }
-
mComponentName = mime;
mComponentName.append(" decoder");
- ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get());
+ ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get());
mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */);
int32_t secure = 0;
@@ -164,17 +207,17 @@
mCodec->getName(&mComponentName);
status_t err;
- if (mNativeWindow != NULL) {
+ if (mSurface != NULL) {
// disconnect from surface as MediaCodec will reconnect
err = native_window_api_disconnect(
- surface.get(), NATIVE_WINDOW_API_MEDIA);
+ mSurface.get(), NATIVE_WINDOW_API_MEDIA);
// We treat this as a warning, as this is a preparatory step.
// Codec will try to connect to the surface, which is where
// any error signaling will occur.
ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err);
}
err = mCodec->configure(
- format, surface, NULL /* crypto */, 0 /* flags */);
+ format, mSurface, NULL /* crypto */, 0 /* flags */);
if (err != OK) {
ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
mCodec->release();
@@ -188,6 +231,9 @@
CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
+ sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
+ mCodec->setCallback(reply);
+
err = mCodec->start();
if (err != OK) {
ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
@@ -197,36 +243,32 @@
return;
}
- // the following should work after start
- CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
releaseAndResetMediaBuffers();
- CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
- ALOGV("[%s] got %zu input and %zu output buffers",
- mComponentName.c_str(),
- mInputBuffers.size(),
- mOutputBuffers.size());
- if (mRenderer != NULL) {
- requestCodecNotification();
- }
mPaused = false;
mResumePending = false;
}
+void NuPlayer::Decoder::onSetParameters(const sp<AMessage> ¶ms) {
+ if (mCodec == NULL) {
+ ALOGW("onSetParameters called before codec is created.");
+ return;
+ }
+ mCodec->setParameters(params);
+}
+
void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
bool hadNoRenderer = (mRenderer == NULL);
mRenderer = renderer;
if (hadNoRenderer && mRenderer != NULL) {
- requestCodecNotification();
+ // this means that the widevine legacy source is ready
+ onRequestInputBuffers();
}
}
void NuPlayer::Decoder::onGetInputBuffers(
Vector<sp<ABuffer> > *dstBuffers) {
- dstBuffers->clear();
- for (size_t i = 0; i < mInputBuffers.size(); i++) {
- dstBuffers->push(mInputBuffers[i]);
- }
+ CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers));
}
void NuPlayer::Decoder::onResume(bool notifyComplete) {
@@ -235,6 +277,7 @@
if (notifyComplete) {
mResumePending = true;
}
+ mCodec->start();
}
void NuPlayer::Decoder::doFlush(bool notifyComplete) {
@@ -261,8 +304,10 @@
// we attempt to release the buffers even if flush fails.
}
releaseAndResetMediaBuffers();
+ mPaused = true;
}
+
void NuPlayer::Decoder::onFlush() {
doFlush(true);
@@ -276,7 +321,6 @@
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFlushCompleted);
notify->post();
- mPaused = true;
}
void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -290,12 +334,10 @@
mCodec = NULL;
++mBufferGeneration;
- if (mNativeWindow != NULL) {
+ if (mSurface != NULL) {
// reconnect to surface as MediaCodec disconnected from it
status_t error =
- native_window_api_connect(
- mNativeWindow->getNativeWindow().get(),
- NATIVE_WINDOW_API_MEDIA);
+ native_window_api_connect(mSurface.get(), NATIVE_WINDOW_API_MEDIA);
ALOGW_IF(error != NO_ERROR,
"[%s] failed to connect to native window, error=%d",
mComponentName.c_str(), error);
@@ -319,9 +361,14 @@
}
}
-void NuPlayer::Decoder::doRequestBuffers() {
- if (isDiscontinuityPending()) {
- return;
+/*
+ * returns true if we should request more data
+ */
+bool NuPlayer::Decoder::doRequestBuffers() {
+ // mRenderer is only NULL if we have a legacy widevine source that
+ // is not yet ready. In this case we must not fetch input.
+ if (isDiscontinuityPending() || mRenderer == NULL) {
+ return false;
}
status_t err = OK;
while (err == OK && !mDequeuedInputBuffers.empty()) {
@@ -341,40 +388,54 @@
}
}
- if (err == -EWOULDBLOCK
- && mSource->feedMoreTSData() == OK) {
- scheduleRequestBuffers();
- }
+ return err == -EWOULDBLOCK
+ && mSource->feedMoreTSData() == OK;
}
-bool NuPlayer::Decoder::handleAnInputBuffer() {
+void NuPlayer::Decoder::handleError(int32_t err)
+{
+ // We cannot immediately release the codec due to buffers still outstanding
+ // in the renderer. We signal to the player the error so it can shutdown/release the
+ // decoder after flushing and increment the generation to discard unnecessary messages.
+
+ ++mBufferGeneration;
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) {
if (isDiscontinuityPending()) {
return false;
}
- size_t bufferIx = -1;
- status_t res = mCodec->dequeueInputBuffer(&bufferIx);
- ALOGV("[%s] dequeued input: %d",
- mComponentName.c_str(), res == OK ? (int)bufferIx : res);
- if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue input buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
+
+ sp<ABuffer> buffer;
+ mCodec->getInputBuffer(index, &buffer);
+
+ if (index >= mInputBuffers.size()) {
+ for (size_t i = mInputBuffers.size(); i <= index; ++i) {
+ mInputBuffers.add();
+ mMediaBuffers.add();
+ mInputBufferIsDequeued.add();
+ mMediaBuffers.editItemAt(i) = NULL;
+ mInputBufferIsDequeued.editItemAt(i) = false;
}
- return false;
}
+ mInputBuffers.editItemAt(index) = buffer;
- CHECK_LT(bufferIx, mInputBuffers.size());
+ //CHECK_LT(bufferIx, mInputBuffers.size());
- if (mMediaBuffers[bufferIx] != NULL) {
- mMediaBuffers[bufferIx]->release();
- mMediaBuffers.editItemAt(bufferIx) = NULL;
+ if (mMediaBuffers[index] != NULL) {
+ mMediaBuffers[index]->release();
+ mMediaBuffers.editItemAt(index) = NULL;
}
- mInputBufferIsDequeued.editItemAt(bufferIx) = true;
+ mInputBufferIsDequeued.editItemAt(index) = true;
if (!mCSDsToSubmit.isEmpty()) {
sp<AMessage> msg = new AMessage();
- msg->setSize("buffer-ix", bufferIx);
+ msg->setSize("buffer-ix", index);
sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
@@ -392,94 +453,34 @@
mPendingInputMessages.erase(mPendingInputMessages.begin());
}
- if (!mInputBufferIsDequeued.editItemAt(bufferIx)) {
+ if (!mInputBufferIsDequeued.editItemAt(index)) {
return true;
}
- mDequeuedInputBuffers.push_back(bufferIx);
+ mDequeuedInputBuffers.push_back(index);
onRequestInputBuffers();
return true;
}
-bool NuPlayer::Decoder::handleAnOutputBuffer() {
- size_t bufferIx = -1;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
- status_t res = mCodec->dequeueOutputBuffer(
- &bufferIx, &offset, &size, &timeUs, &flags);
+bool NuPlayer::Decoder::handleAnOutputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t timeUs,
+ int32_t flags) {
+// CHECK_LT(bufferIx, mOutputBuffers.size());
+ sp<ABuffer> buffer;
+ mCodec->getOutputBuffer(index, &buffer);
- if (res != OK) {
- ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res);
- } else {
- ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")",
- mComponentName.c_str(), (int)bufferIx, timeUs, flags);
+ if (index >= mOutputBuffers.size()) {
+ for (size_t i = mOutputBuffers.size(); i <= index; ++i) {
+ mOutputBuffers.add();
+ }
}
- if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
- res = mCodec->getOutputBuffers(&mOutputBuffers);
- if (res != OK) {
- ALOGE("Failed to get output buffers for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
- // NuPlayer ignores this
- return true;
- } else if (res == INFO_FORMAT_CHANGED) {
- sp<AMessage> format = new AMessage();
- res = mCodec->getOutputFormat(&format);
- if (res != OK) {
- ALOGE("Failed to get output format for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
+ mOutputBuffers.editItemAt(index) = buffer;
- if (!mIsAudio) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatVideoSizeChanged);
- notify->setMessage("format", format);
- notify->post();
- } else if (mRenderer != NULL) {
- uint32_t flags;
- int64_t durationUs;
- bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
- if (!hasVideo &&
- mSource->getDuration(&durationUs) == OK &&
- durationUs
- > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
- flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
- } else {
- flags = AUDIO_OUTPUT_FLAG_NONE;
- }
-
- res = mRenderer->openAudioSink(
- format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */);
- if (res != OK) {
- ALOGE("Failed to open AudioSink on format change for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
- }
- return true;
- } else if (res == INFO_DISCONTINUITY) {
- // nothing to do
- return true;
- } else if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue output buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- }
- return false;
- }
-
- CHECK_LT(bufferIx, mOutputBuffers.size());
- sp<ABuffer> buffer = mOutputBuffers[bufferIx];
buffer->setRange(offset, size);
buffer->meta()->clear();
buffer->meta()->setInt64("timeUs", timeUs);
@@ -488,7 +489,7 @@
// we do not expect CODECCONFIG or SYNCFRAME for decoder
sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
- reply->setSize("buffer-ix", bufferIx);
+ reply->setSize("buffer-ix", index);
reply->setInt32("generation", mBufferGeneration);
if (eos) {
@@ -522,6 +523,29 @@
return true;
}
+void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) {
+ if (!mIsAudio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatVideoSizeChanged);
+ notify->setMessage("format", format);
+ notify->post();
+ } else if (mRenderer != NULL) {
+ uint32_t flags;
+ int64_t durationUs;
+ bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
+ if (!hasVideo &&
+ mSource->getDuration(&durationUs) == OK &&
+ durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ } else {
+ flags = AUDIO_OUTPUT_FLAG_NONE;
+ }
+
+ mRenderer->openAudioSink(
+ format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */);
+ }
+}
+
void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
for (size_t i = 0; i < mMediaBuffers.size(); i++) {
if (mMediaBuffers[i] != NULL) {
@@ -637,7 +661,7 @@
#if 0
int64_t mediaTimeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
- ALOGV("[%s] feeding input buffer at media time %" PRId64,
+ ALOGV("[%s] feeding input buffer at media time %.3f",
mIsAudio ? "audio" : "video",
mediaTimeUs / 1E6);
#endif
@@ -825,11 +849,9 @@
mPaused = true;
} else if (mTimeChangePending) {
if (flushOnTimeChange) {
- doFlush(false /*notifyComplete*/);
+ doFlush(false /* notifyComplete */);
+ signalResume(false /* notifyComplete */);
}
-
- // restart fetching input
- scheduleRequestBuffers();
}
// Notify NuPlayer to either shutdown decoder, or rescan sources
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 4aab2c6..070d51a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -27,7 +27,7 @@
Decoder(const sp<AMessage> ¬ify,
const sp<Source> &source,
const sp<Renderer> &renderer = NULL,
- const sp<NativeWindowWrapper> &nativeWindow = NULL,
+ const sp<Surface> &surface = NULL,
const sp<CCDecoder> &ccDecoder = NULL);
virtual void getStats(
@@ -40,12 +40,13 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual void onConfigure(const sp<AMessage> &format);
+ virtual void onSetParameters(const sp<AMessage> ¶ms);
virtual void onSetRenderer(const sp<Renderer> &renderer);
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
- virtual void doRequestBuffers();
+ virtual bool doRequestBuffers();
private:
enum {
@@ -53,7 +54,7 @@
kWhatRenderBuffer = 'rndr',
};
- sp<NativeWindowWrapper> mNativeWindow;
+ sp<Surface> mSurface;
sp<Source> mSource;
sp<Renderer> mRenderer;
@@ -87,8 +88,15 @@
bool mResumePending;
AString mComponentName;
- bool handleAnInputBuffer();
- bool handleAnOutputBuffer();
+ void handleError(int32_t err);
+ bool handleAnInputBuffer(size_t index);
+ bool handleAnOutputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t timeUs,
+ int32_t flags);
+ void handleOutputFormatChange(const sp<AMessage> &format);
void releaseAndResetMediaBuffers();
void requestCodecNotification();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 4636f0a..9d509bf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -70,6 +70,12 @@
mDecoderLooper->registerHandler(this);
}
+void NuPlayer::DecoderBase::setParameters(const sp<AMessage> ¶ms) {
+ sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
+ msg->setMessage("params", params);
+ msg->post();
+}
+
void NuPlayer::DecoderBase::setRenderer(const sp<Renderer> &renderer) {
sp<AMessage> msg = new AMessage(kWhatSetRenderer, this);
msg->setObject("renderer", renderer);
@@ -103,16 +109,13 @@
return;
}
- doRequestBuffers();
-}
+ // doRequestBuffers() return true if we should request more data
+ if (doRequestBuffers()) {
+ mRequestInputBuffersPending = true;
-void NuPlayer::DecoderBase::scheduleRequestBuffers() {
- if (mRequestInputBuffersPending) {
- return;
+ sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this);
+ msg->post(10 * 1000ll);
}
- mRequestInputBuffersPending = true;
- sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this);
- msg->post(10 * 1000ll);
}
void NuPlayer::DecoderBase::onMessageReceived(const sp<AMessage> &msg) {
@@ -126,6 +129,14 @@
break;
}
+ case kWhatSetParameters:
+ {
+ sp<AMessage> params;
+ CHECK(msg->findMessage("params", ¶ms));
+ onSetParameters(params);
+ break;
+ }
+
case kWhatSetRenderer:
{
sp<RefBase> obj;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 97e9269..b52e7f7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -26,13 +26,14 @@
struct ABuffer;
struct MediaCodec;
-struct MediaBuffer;
+class MediaBuffer;
struct NuPlayer::DecoderBase : public AHandler {
DecoderBase(const sp<AMessage> ¬ify);
void configure(const sp<AMessage> &format);
void init();
+ void setParameters(const sp<AMessage> ¶ms);
void setRenderer(const sp<Renderer> &renderer);
@@ -62,6 +63,7 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual void onConfigure(const sp<AMessage> &format) = 0;
+ virtual void onSetParameters(const sp<AMessage> ¶ms) = 0;
virtual void onSetRenderer(const sp<Renderer> &renderer) = 0;
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0;
virtual void onResume(bool notifyComplete) = 0;
@@ -69,8 +71,7 @@
virtual void onShutdown(bool notifyComplete) = 0;
void onRequestInputBuffers();
- void scheduleRequestBuffers();
- virtual void doRequestBuffers() = 0;
+ virtual bool doRequestBuffers() = 0;
virtual void handleError(int32_t err);
sp<AMessage> mNotify;
@@ -79,6 +80,7 @@
private:
enum {
kWhatConfigure = 'conf',
+ kWhatSetParameters = 'setP',
kWhatSetRenderer = 'setR',
kWhatGetInputBuffers = 'gInB',
kWhatRequestInputBuffers = 'reqB',
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index 563de5e..d7b070e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -88,6 +88,10 @@
}
}
+void NuPlayer::DecoderPassThrough::onSetParameters(const sp<AMessage> &/*params*/) {
+ ALOGW("onSetParameters() called unexpectedly");
+}
+
void NuPlayer::DecoderPassThrough::onSetRenderer(
const sp<Renderer> &renderer) {
// renderer can't be changed during offloading
@@ -113,7 +117,10 @@
return mCachedBytes >= kMaxCachedBytes || mReachedEOS || mPaused;
}
-void NuPlayer::DecoderPassThrough::doRequestBuffers() {
+/*
+ * returns true if we should request more data
+ */
+bool NuPlayer::DecoderPassThrough::doRequestBuffers() {
status_t err = OK;
while (!isDoneFetching()) {
sp<AMessage> msg = new AMessage();
@@ -126,10 +133,8 @@
onInputBufferFetched(msg);
}
- if (err == -EWOULDBLOCK
- && mSource->feedMoreTSData() == OK) {
- scheduleRequestBuffers();
- }
+ return err == -EWOULDBLOCK
+ && mSource->feedMoreTSData() == OK;
}
status_t NuPlayer::DecoderPassThrough::dequeueAccessUnit(sp<ABuffer> *accessUnit) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index 173cfbd..2f6df2c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -40,12 +40,13 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual void onConfigure(const sp<AMessage> &format);
+ virtual void onSetParameters(const sp<AMessage> ¶ms);
virtual void onSetRenderer(const sp<Renderer> &renderer);
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
- virtual void doRequestBuffers();
+ virtual bool doRequestBuffers();
private:
enum {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 1fa9cef..231f2e1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -135,6 +135,25 @@
return mAsyncResult;
}
+status_t NuPlayerDriver::setDataSource(const sp<DataSource> &source) {
+ ALOGV("setDataSource(%p) callback source", this);
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != STATE_IDLE) {
+ return INVALID_OPERATION;
+ }
+
+ mState = STATE_SET_DATASOURCE_PENDING;
+
+ mPlayer->setDataSourceAsync(source);
+
+ while (mState == STATE_SET_DATASOURCE_PENDING) {
+ mCondition.wait(mLock);
+ }
+
+ return mAsyncResult;
+}
+
status_t NuPlayerDriver::setVideoSurfaceTexture(
const sp<IGraphicBufferProducer> &bufferProducer) {
ALOGV("setVideoSurfaceTexture(%p)", this);
@@ -341,9 +360,32 @@
return mState == STATE_RUNNING && !mAtEOS;
}
-status_t NuPlayerDriver::setPlaybackRate(float rate) {
- mPlayer->setPlaybackRate(rate);
- return OK;
+status_t NuPlayerDriver::setPlaybackSettings(const AudioPlaybackRate &rate) {
+ Mutex::Autolock autoLock(mLock);
+ status_t err = mPlayer->setPlaybackSettings(rate);
+ if (err == OK) {
+ if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
+ mState = STATE_PAUSED;
+ // try to update position
+ (void)mPlayer->getCurrentPosition(&mPositionUs);
+ notifyListener_l(MEDIA_PAUSED);
+ } else if (rate.mSpeed != 0.f && mState == STATE_PAUSED) {
+ mState = STATE_RUNNING;
+ }
+ }
+ return err;
+}
+
+status_t NuPlayerDriver::getPlaybackSettings(AudioPlaybackRate *rate) {
+ return mPlayer->getPlaybackSettings(rate);
+}
+
+status_t NuPlayerDriver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
+ return mPlayer->setSyncSettings(sync, videoFpsHint);
+}
+
+status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
+ return mPlayer->getSyncSettings(sync, videoFps);
}
status_t NuPlayerDriver::seekTo(int msec) {
@@ -369,6 +411,9 @@
{
mAtEOS = false;
mSeekInProgress = true;
+ if (mState == STATE_PAUSED) {
+ mStartupSeekTimeUs = seekTimeUs;
+ }
// seeks can take a while, so we essentially paused
notifyListener_l(MEDIA_PAUSED);
mPlayer->seekToAsync(seekTimeUs, true /* needNotify */);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index e53abcd..9da7fc1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -39,6 +39,8 @@
virtual status_t setDataSource(const sp<IStreamSource> &source);
+ virtual status_t setDataSource(const sp<DataSource>& dataSource);
+
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer> &bufferProducer);
virtual status_t prepare();
@@ -47,7 +49,10 @@
virtual status_t stop();
virtual status_t pause();
virtual bool isPlaying();
- virtual status_t setPlaybackRate(float rate);
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+ virtual status_t getPlaybackSettings(AudioPlaybackRate *rate);
+ virtual status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
+ virtual status_t getSyncSettings(AVSyncSettings *sync, float *videoFps);
virtual status_t seekTo(int msec);
virtual status_t getCurrentPosition(int *msec);
virtual status_t getDuration(int *msec);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index a2ec51c..007a335 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -66,7 +66,7 @@
mVideoQueueGeneration(0),
mAudioDrainGeneration(0),
mVideoDrainGeneration(0),
- mPlaybackRate(1.0),
+ mPlaybackSettings(AUDIO_PLAYBACK_RATE_DEFAULT),
mAudioFirstAnchorTimeMediaUs(-1),
mAnchorTimeMediaUs(-1),
mAnchorNumFramesWritten(-1),
@@ -89,6 +89,8 @@
mLastAudioBufferDrained(0),
mWakeLock(new AWakeLock()) {
mMediaClock = new MediaClock;
+ mPlaybackRate = mPlaybackSettings.mSpeed;
+ mMediaClock->setPlaybackRate(mPlaybackRate);
}
NuPlayer::Renderer::~Renderer() {
@@ -121,10 +123,111 @@
msg->post();
}
-void NuPlayer::Renderer::setPlaybackRate(float rate) {
- sp<AMessage> msg = new AMessage(kWhatSetRate, this);
- msg->setFloat("rate", rate);
- msg->post();
+status_t NuPlayer::Renderer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+ sp<AMessage> msg = new AMessage(kWhatConfigPlayback, this);
+ writeToAMessage(msg, rate);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ }
+ return err;
+}
+
+status_t NuPlayer::Renderer::onConfigPlayback(const AudioPlaybackRate &rate /* sanitized */) {
+ if (rate.mSpeed == 0.f) {
+ onPause();
+ // don't call audiosink's setPlaybackRate if pausing, as pitch does not
+ // have to correspond to the any non-0 speed (e.g old speed). Keep
+ // settings nonetheless, using the old speed, in case audiosink changes.
+ AudioPlaybackRate newRate = rate;
+ newRate.mSpeed = mPlaybackSettings.mSpeed;
+ mPlaybackSettings = newRate;
+ return OK;
+ }
+
+ if (mAudioSink != NULL && mAudioSink->ready()) {
+ status_t err = mAudioSink->setPlaybackRate(rate);
+ if (err != OK) {
+ return err;
+ }
+ }
+ mPlaybackSettings = rate;
+ mPlaybackRate = rate.mSpeed;
+ mMediaClock->setPlaybackRate(mPlaybackRate);
+ return OK;
+}
+
+status_t NuPlayer::Renderer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+ sp<AMessage> msg = new AMessage(kWhatGetPlaybackSettings, this);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ if (err == OK) {
+ readFromAMessage(response, rate);
+ }
+ }
+ return err;
+}
+
+status_t NuPlayer::Renderer::onGetPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+ if (mAudioSink != NULL && mAudioSink->ready()) {
+ status_t err = mAudioSink->getPlaybackRate(rate);
+ if (err == OK) {
+ if (!isAudioPlaybackRateEqual(*rate, mPlaybackSettings)) {
+ ALOGW("correcting mismatch in internal/external playback rate");
+ }
+ // get playback settings used by audiosink, as it may be
+ // slightly off due to audiosink not taking small changes.
+ mPlaybackSettings = *rate;
+ if (mPaused) {
+ rate->mSpeed = 0.f;
+ }
+ }
+ return err;
+ }
+ *rate = mPlaybackSettings;
+ return OK;
+}
+
+status_t NuPlayer::Renderer::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
+ sp<AMessage> msg = new AMessage(kWhatConfigSync, this);
+ writeToAMessage(msg, sync, videoFpsHint);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ }
+ return err;
+}
+
+status_t NuPlayer::Renderer::onConfigSync(const AVSyncSettings &sync, float videoFpsHint __unused) {
+ if (sync.mSource != AVSYNC_SOURCE_DEFAULT) {
+ return BAD_VALUE;
+ }
+ // TODO: support sync sources
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayer::Renderer::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
+ sp<AMessage> msg = new AMessage(kWhatGetSyncSettings, this);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ if (err == OK) {
+ readFromAMessage(response, sync, videoFps);
+ }
+ }
+ return err;
+}
+
+status_t NuPlayer::Renderer::onGetSyncSettings(
+ AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */) {
+ *sync = mSyncSettings;
+ *videoFps = -1.f;
+ return OK;
}
void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) {
@@ -312,6 +415,9 @@
int64_t delayUs =
mAudioSink->msecsPerFrame()
* numFramesPendingPlayout * 1000ll;
+ if (mPlaybackRate > 1.0f) {
+ delayUs /= mPlaybackRate;
+ }
// Let's give it more data after about half that time
// has elapsed.
@@ -362,13 +468,63 @@
break;
}
- case kWhatSetRate:
+ case kWhatConfigPlayback:
{
- CHECK(msg->findFloat("rate", &mPlaybackRate));
- int32_t ratePermille = (int32_t)(0.5f + 1000 * mPlaybackRate);
- mPlaybackRate = ratePermille / 1000.0f;
- mMediaClock->setPlaybackRate(mPlaybackRate);
- mAudioSink->setPlaybackRatePermille(ratePermille);
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ AudioPlaybackRate rate;
+ readFromAMessage(msg, &rate);
+ status_t err = onConfigPlayback(rate);
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatGetPlaybackSettings:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+ status_t err = onGetPlaybackSettings(&rate);
+ sp<AMessage> response = new AMessage;
+ if (err == OK) {
+ writeToAMessage(response, rate);
+ }
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatConfigSync:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ AVSyncSettings sync;
+ float videoFpsHint;
+ readFromAMessage(msg, &sync, &videoFpsHint);
+ status_t err = onConfigSync(sync, videoFpsHint);
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatGetSyncSettings:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ ALOGV("kWhatGetSyncSettings");
+ AVSyncSettings sync;
+ float videoFps = -1.f;
+ status_t err = onGetSyncSettings(&sync, &videoFps);
+ sp<AMessage> response = new AMessage;
+ if (err == OK) {
+ writeToAMessage(response, sync, videoFps);
+ }
+ response->setInt32("err", err);
+ response->postReply(replyID);
break;
}
@@ -618,7 +774,8 @@
return false;
}
- if (entry->mOffset == 0) {
+ // ignore 0-sized buffer which could be EOS marker with no data
+ if (entry->mOffset == 0 && entry->mBuffer->size() > 0) {
int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
@@ -853,7 +1010,7 @@
if (tooLate) {
ALOGV("video late by %lld us (%.2f secs)",
- mVideoLateByUs, mVideoLateByUs / 1E6);
+ (long long)mVideoLateByUs, mVideoLateByUs / 1E6);
} else {
int64_t mediaUs = 0;
mMediaClock->getMediaTime(realTimeUs, &mediaUs);
@@ -1083,6 +1240,16 @@
mAudioSink->pause();
mAudioSink->flush();
mAudioSink->start();
+ } else {
+ mAudioSink->pause();
+ mAudioSink->flush();
+ // Call stop() to signal to the AudioSink to completely fill the
+ // internal buffer before resuming playback.
+ mAudioSink->stop();
+ if (!mPaused) {
+ mAudioSink->start();
+ }
+ mNumFramesWritten = 0;
}
} else {
flushQueue(&mVideoQueue);
@@ -1172,10 +1339,9 @@
void NuPlayer::Renderer::onPause() {
if (mPaused) {
- ALOGW("Renderer::onPause() called while already paused!");
return;
}
- int64_t currentPositionUs;
+
{
Mutex::Autolock autoLock(mLock);
++mAudioDrainGeneration;
@@ -1193,7 +1359,7 @@
startAudioOffloadPauseTimeout();
}
- ALOGV("now paused audio queue has %d entries, video has %d entries",
+ ALOGV("now paused audio queue has %zu entries, video has %zu entries",
mAudioQueue.size(), mVideoQueue.size());
}
@@ -1210,6 +1376,12 @@
{
Mutex::Autolock autoLock(mLock);
mPaused = false;
+
+ // configure audiosink as we did not do it when pausing
+ if (mAudioSink != NULL && mAudioSink->ready()) {
+ mAudioSink->setPlaybackRate(mPlaybackSettings);
+ }
+
mMediaClock->setPlaybackRate(mPlaybackRate);
if (!mAudioQueue.empty()) {
@@ -1286,7 +1458,7 @@
CHECK_EQ(res, (status_t)OK);
numFramesPlayedAt = nowUs;
numFramesPlayedAt += 1000LL * mAudioSink->latency() / 2; /* XXX */
- //ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt);
+ //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAt);
}
//CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
@@ -1429,10 +1601,10 @@
&offloadInfo);
if (err == OK) {
- if (mPlaybackRate != 1.0) {
- mAudioSink->setPlaybackRatePermille(
- (int32_t)(mPlaybackRate * 1000 + 0.5f));
- }
+ err = mAudioSink->setPlaybackRate(mPlaybackSettings);
+ }
+
+ if (err == OK) {
// If the playback is offloaded to h/w, we pass
// the HAL some metadata information.
// We don't want to do this for PCM because it
@@ -1482,16 +1654,15 @@
NULL,
NULL,
(audio_output_flags_t)pcmFlags);
+ if (err == OK) {
+ err = mAudioSink->setPlaybackRate(mPlaybackSettings);
+ }
if (err != OK) {
ALOGW("openAudioSink: non offloaded open failed status: %d", err);
mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
return err;
}
mCurrentPcmInfo = info;
- if (mPlaybackRate != 1.0) {
- mAudioSink->setPlaybackRatePermille(
- (int32_t)(mPlaybackRate * 1000 + 0.5f));
- }
mAudioSink->start();
}
if (audioSinkChanged) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 38843d5..928b71b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -18,6 +18,9 @@
#define NUPLAYER_RENDERER_H_
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
+
#include "NuPlayer.h"
namespace android {
@@ -48,7 +51,10 @@
void queueEOS(bool audio, status_t finalResult);
- void setPlaybackRate(float rate);
+ status_t setPlaybackSettings(const AudioPlaybackRate &rate /* sanitized */);
+ status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+ status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
+ status_t getSyncSettings(AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
void flush(bool audio, bool notifyComplete);
@@ -102,7 +108,10 @@
kWhatPostDrainVideoQueue = 'pDVQ',
kWhatQueueBuffer = 'queB',
kWhatQueueEOS = 'qEOS',
- kWhatSetRate = 'setR',
+ kWhatConfigPlayback = 'cfPB',
+ kWhatConfigSync = 'cfSy',
+ kWhatGetPlaybackSettings = 'gPbS',
+ kWhatGetSyncSettings = 'gSyS',
kWhatFlush = 'flus',
kWhatPause = 'paus',
kWhatResume = 'resm',
@@ -141,7 +150,12 @@
int32_t mVideoDrainGeneration;
sp<MediaClock> mMediaClock;
- float mPlaybackRate;
+ float mPlaybackRate; // audio track rate
+
+ AudioPlaybackRate mPlaybackSettings;
+ AVSyncSettings mSyncSettings;
+ float mVideoFpsHint;
+
int64_t mAudioFirstAnchorTimeMediaUs;
int64_t mAnchorTimeMediaUs;
int64_t mAnchorNumFramesWritten;
@@ -217,6 +231,11 @@
void onAudioSinkChanged();
void onDisableOffloadAudio();
void onEnableOffloadAudio();
+ status_t onConfigPlayback(const AudioPlaybackRate &rate /* sanitized */);
+ status_t onGetPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+ status_t onConfigSync(const AVSyncSettings &sync, float videoFpsHint);
+ status_t onGetSyncSettings(AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
+
void onPause();
void onResume();
void onSetVideoFrameRate(float fps);
@@ -252,6 +271,6 @@
DISALLOW_EVIL_CONSTRUCTORS(Renderer);
};
-} // namespace android
+} // namespace android
#endif // NUPLAYER_RENDERER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index d9f14a2..ef1ba13 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -28,7 +28,7 @@
namespace android {
struct ABuffer;
-struct MediaBuffer;
+class MediaBuffer;
struct NuPlayer::Source : public AHandler {
enum Flags {
@@ -53,6 +53,7 @@
kWhatCacheStats,
kWhatSubtitleData,
kWhatTimedTextData,
+ kWhatTimedMetaData,
kWhatQueueDecoderShutdown,
kWhatDrmNoLicense,
kWhatInstantiateSecureDecoders,
diff --git a/media/libmediaplayerservice/tests/Android.mk b/media/libmediaplayerservice/tests/Android.mk
index 7bc78ff..8cbf782 100644
--- a/media/libmediaplayerservice/tests/Android.mk
+++ b/media/libmediaplayerservice/tests/Android.mk
@@ -18,6 +18,9 @@
frameworks/av/include \
frameworks/av/media/libmediaplayerservice \
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
LOCAL_32_BIT_ONLY := true
include $(BUILD_NATIVE_TEST)
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index d3e760b..de350a1 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -98,17 +98,17 @@
mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId2);
mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId3);
const PidSessionInfosMap& map = sessionMap();
- EXPECT_EQ(2, map.size());
+ EXPECT_EQ(2u, map.size());
ssize_t index1 = map.indexOfKey(kTestPid1);
ASSERT_GE(index1, 0);
const SessionInfos& infos1 = map[index1];
- EXPECT_EQ(1, infos1.size());
+ EXPECT_EQ(1u, infos1.size());
ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 0);
ssize_t index2 = map.indexOfKey(kTestPid2);
ASSERT_GE(index2, 0);
const SessionInfos& infos2 = map[index2];
- EXPECT_EQ(2, infos2.size());
+ EXPECT_EQ(2u, infos2.size());
ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId2, 1);
ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 2);
}
@@ -185,11 +185,11 @@
mDrmSessionManager->removeSession(mSessionId2);
const PidSessionInfosMap& map = sessionMap();
- EXPECT_EQ(2, map.size());
+ EXPECT_EQ(2u, map.size());
const SessionInfos& infos1 = map.valueFor(kTestPid1);
const SessionInfos& infos2 = map.valueFor(kTestPid2);
- EXPECT_EQ(1, infos1.size());
- EXPECT_EQ(1, infos2.size());
+ EXPECT_EQ(1u, infos1.size());
+ EXPECT_EQ(1u, infos2.size());
// mSessionId2 has been removed.
ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId3, 2);
}
@@ -207,7 +207,7 @@
const PidSessionInfosMap& map = sessionMap();
const SessionInfos& infos2 = map.valueFor(kTestPid2);
- EXPECT_EQ(1, infos2.size());
+ EXPECT_EQ(1u, infos2.size());
// mTestDrm2 has been removed.
ExpectEqSessionInfo(infos2[0], drm, sessionId, 3);
}
@@ -220,7 +220,7 @@
EXPECT_FALSE(mDrmSessionManager->reclaimSession(50));
EXPECT_TRUE(mDrmSessionManager->reclaimSession(10));
- EXPECT_EQ(1, mTestDrm1->reclaimedSessions().size());
+ EXPECT_EQ(1u, mTestDrm1->reclaimedSessions().size());
EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0]));
mDrmSessionManager->removeSession(mSessionId1);
@@ -233,7 +233,7 @@
mDrmSessionManager->addSession(15, drm, sessionId);
EXPECT_TRUE(mDrmSessionManager->reclaimSession(18));
- EXPECT_EQ(1, mTestDrm2->reclaimedSessions().size());
+ EXPECT_EQ(1u, mTestDrm2->reclaimedSessions().size());
// mSessionId2 is reclaimed.
EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0]));
}
diff --git a/media/libnbaio/SourceAudioBufferProvider.cpp b/media/libnbaio/SourceAudioBufferProvider.cpp
index e21ef48..04c42c9 100644
--- a/media/libnbaio/SourceAudioBufferProvider.cpp
+++ b/media/libnbaio/SourceAudioBufferProvider.cpp
@@ -61,20 +61,30 @@
// do we need to reallocate?
if (buffer->frameCount > mSize) {
free(mAllocated);
- mAllocated = malloc(buffer->frameCount * mFrameSize);
+ // Android convention is to _not_ check the return value of malloc and friends.
+ // But in this case the calloc() can also fail due to integer overflow,
+ // so we check and recover.
+ mAllocated = calloc(buffer->frameCount, mFrameSize);
+ if (mAllocated == NULL) {
+ mSize = 0;
+ goto fail;
+ }
mSize = buffer->frameCount;
}
- // read from source
- ssize_t actual = mSource->read(mAllocated, buffer->frameCount, pts);
- if (actual > 0) {
- ALOG_ASSERT((size_t) actual <= buffer->frameCount);
- mOffset = 0;
- mRemaining = actual;
- buffer->raw = mAllocated;
- buffer->frameCount = actual;
- mGetCount = actual;
- return OK;
+ {
+ // read from source
+ ssize_t actual = mSource->read(mAllocated, buffer->frameCount, pts);
+ if (actual > 0) {
+ ALOG_ASSERT((size_t) actual <= buffer->frameCount);
+ mOffset = 0;
+ mRemaining = actual;
+ buffer->raw = mAllocated;
+ buffer->frameCount = actual;
+ mGetCount = actual;
+ return OK;
+ }
}
+fail:
buffer->raw = NULL;
buffer->frameCount = 0;
mGetCount = 0;
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index 196f6ee..45e8a30 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -360,7 +360,7 @@
pos += len;
ALOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)",
- pos, pos);
+ (long long)pos, (long long)pos);
}
uint8_t header[2];
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 97f3e20..7cba450 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -24,6 +24,8 @@
#include <inttypes.h>
#include <utils/Trace.h>
+#include <gui/Surface.h>
+
#include <media/stagefright/ACodec.h>
#include <binder/MemoryDealer.h>
@@ -37,16 +39,17 @@
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/NativeWindowWrapper.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
-
+#include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/SurfaceUtils.h>
#include <media/hardware/HardwareAPI.h>
#include <OMX_AudioExt.h>
#include <OMX_VideoExt.h>
#include <OMX_Component.h>
#include <OMX_IndexExt.h>
+#include <OMX_AsString.h>
#include "include/avc_utils.h"
@@ -152,7 +155,7 @@
}
default:
- TRESPASS();
+ ALOGE("Unrecognized message type: %d", omx_msg.type);
break;
}
@@ -259,9 +262,12 @@
bool onConfigureComponent(const sp<AMessage> &msg);
void onCreateInputSurface(const sp<AMessage> &msg);
+ void onSetInputSurface(const sp<AMessage> &msg);
void onStart();
void onShutdown(bool keepComponentAllocated);
+ status_t setupInputSurface();
+
DISALLOW_EVIL_CONSTRUCTORS(LoadedState);
};
@@ -405,6 +411,7 @@
: mQuirks(0),
mNode(0),
mSentFormat(false),
+ mIsVideo(false),
mIsEncoder(false),
mUseMetadataOnEncoderOutput(false),
mShutdownInProgress(false),
@@ -416,6 +423,7 @@
mChannelMask(0),
mDequeueCounter(0),
mStoreMetaDataInOutputBuffers(false),
+ mLegacyAdaptiveExperiment(false),
mMetaDataBuffersToSubmit(0),
mRepeatFrameDelayUs(-1ll),
mMaxPtsGapUs(-1ll),
@@ -474,10 +482,30 @@
msg->post();
}
+status_t ACodec::setSurface(const sp<Surface> &surface) {
+ sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
+ msg->setObject("surface", surface);
+
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+
+ if (err == OK) {
+ (void)response->findInt32("err", &err);
+ }
+ return err;
+}
+
void ACodec::initiateCreateInputSurface() {
(new AMessage(kWhatCreateInputSurface, this))->post();
}
+void ACodec::initiateSetInputSurface(
+ const sp<PersistentSurface> &surface) {
+ sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
+ msg->setObject("input-surface", surface);
+ msg->post();
+}
+
void ACodec::signalEndOfInputStream() {
(new AMessage(kWhatSignalEndOfInputStream, this))->post();
}
@@ -521,6 +549,123 @@
}
}
+status_t ACodec::handleSetSurface(const sp<Surface> &surface) {
+ // allow keeping unset surface
+ if (surface == NULL) {
+ if (mNativeWindow != NULL) {
+ ALOGW("cannot unset a surface");
+ return INVALID_OPERATION;
+ }
+ return OK;
+ }
+
+ // allow keeping unset surface
+ if (mNativeWindow == NULL) {
+ ALOGW("component was not configured with a surface");
+ return INVALID_OPERATION;
+ }
+
+ ANativeWindow *nativeWindow = surface.get();
+ // if we have not yet started the codec, we can simply set the native window
+ if (mBuffers[kPortIndexInput].size() == 0) {
+ mNativeWindow = surface;
+ return OK;
+ }
+
+ // we do not support changing a tunneled surface after start
+ if (mTunneled) {
+ ALOGW("cannot change tunneled surface");
+ return INVALID_OPERATION;
+ }
+
+ status_t err = setupNativeWindowSizeFormatAndUsage(nativeWindow);
+ if (err != OK) {
+ return err;
+ }
+
+ // get min undequeued count. We cannot switch to a surface that has a higher
+ // undequeued count than we allocated.
+ int minUndequeuedBuffers = 0;
+ err = nativeWindow->query(
+ nativeWindow, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers);
+ if (err != 0) {
+ ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+ if (minUndequeuedBuffers > (int)mNumUndequeuedBuffers) {
+ ALOGE("new surface holds onto more buffers (%d) than planned for (%zu)",
+ minUndequeuedBuffers, mNumUndequeuedBuffers);
+ return BAD_VALUE;
+ }
+
+ // we cannot change the number of output buffers while OMX is running
+ // set up surface to the same count
+ Vector<BufferInfo> &buffers = mBuffers[kPortIndexOutput];
+ ALOGV("setting up surface for %zu buffers", buffers.size());
+
+ err = native_window_set_buffer_count(nativeWindow, buffers.size());
+ if (err != 0) {
+ ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ // need to enable allocation when attaching
+ surface->getIGraphicBufferProducer()->allowAllocation(true);
+
+ // for meta data mode, we move dequeud buffers to the new surface.
+ // for non-meta mode, we must move all registered buffers
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ const BufferInfo &info = buffers[i];
+ // skip undequeued buffers for meta data mode
+ if (mStoreMetaDataInOutputBuffers
+ && !mLegacyAdaptiveExperiment
+ && info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ ALOGV("skipping buffer %p", info.mGraphicBuffer->getNativeBuffer());
+ continue;
+ }
+ ALOGV("attaching buffer %p", info.mGraphicBuffer->getNativeBuffer());
+
+ err = surface->attachBuffer(info.mGraphicBuffer->getNativeBuffer());
+ if (err != OK) {
+ ALOGE("failed to attach buffer %p to the new surface: %s (%d)",
+ info.mGraphicBuffer->getNativeBuffer(),
+ strerror(-err), -err);
+ return err;
+ }
+ }
+
+ // cancel undequeued buffers to new surface
+ if (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment) {
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ const BufferInfo &info = buffers[i];
+ if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ ALOGV("canceling buffer %p", info.mGraphicBuffer->getNativeBuffer());
+ err = nativeWindow->cancelBuffer(
+ nativeWindow, info.mGraphicBuffer->getNativeBuffer(), -1);
+ if (err != OK) {
+ ALOGE("failed to cancel buffer %p to the new surface: %s (%d)",
+ info.mGraphicBuffer->getNativeBuffer(),
+ strerror(-err), -err);
+ return err;
+ }
+ }
+ }
+ // disallow further allocation
+ (void)surface->getIGraphicBufferProducer()->allowAllocation(false);
+ }
+
+ // push blank buffers to previous window if requested
+ if (mFlags & kFlagPushBlankBuffersToNativeWindowOnShutdown) {
+ pushBlankBuffersToNativeWindow(mNativeWindow.get());
+ }
+
+ mNativeWindow = nativeWindow;
+ return OK;
+}
+
status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
@@ -553,7 +698,9 @@
for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
- CHECK(mem.get() != NULL);
+ if (mem.get() == NULL) {
+ return NO_MEMORY;
+ }
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_US;
@@ -615,9 +762,7 @@
return OK;
}
-status_t ACodec::configureOutputBuffersFromNativeWindow(
- OMX_U32 *bufferCount, OMX_U32 *bufferSize,
- OMX_U32 *minUndequeuedBuffers) {
+status_t ACodec::setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
@@ -629,40 +774,6 @@
return err;
}
- err = native_window_set_buffers_geometry(
- mNativeWindow.get(),
- def.format.video.nFrameWidth,
- def.format.video.nFrameHeight,
- def.format.video.eColorFormat);
-
- if (err != 0) {
- ALOGE("native_window_set_buffers_geometry failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- if (mRotationDegrees != 0) {
- uint32_t transform = 0;
- switch (mRotationDegrees) {
- case 0: transform = 0; break;
- case 90: transform = HAL_TRANSFORM_ROT_90; break;
- case 180: transform = HAL_TRANSFORM_ROT_180; break;
- case 270: transform = HAL_TRANSFORM_ROT_270; break;
- default: transform = 0; break;
- }
-
- if (transform > 0) {
- err = native_window_set_buffers_transform(
- mNativeWindow.get(), transform);
- if (err != 0) {
- ALOGE("native_window_set_buffers_transform failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
- }
- }
-
- // Set up the native window.
OMX_U32 usage = 0;
err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
if (err != 0) {
@@ -676,43 +787,32 @@
usage |= GRALLOC_USAGE_PROTECTED;
}
- // Make sure to check whether either Stagefright or the video decoder
- // requested protected buffers.
- if (usage & GRALLOC_USAGE_PROTECTED) {
- // Verify that the ANativeWindow sends images directly to
- // SurfaceFlinger.
- int queuesToNativeWindow = 0;
- err = mNativeWindow->query(
- mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
- &queuesToNativeWindow);
- if (err != 0) {
- ALOGE("error authenticating native window: %d", err);
- return err;
- }
- if (queuesToNativeWindow != 1) {
- ALOGE("native window could not be authenticated");
- return PERMISSION_DENIED;
- }
+ usage |= GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP;
+
+ ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec)", omxUsage, usage);
+ return setNativeWindowSizeFormatAndUsage(
+ nativeWindow,
+ def.format.video.nFrameWidth,
+ def.format.video.nFrameHeight,
+ def.format.video.eColorFormat,
+ mRotationDegrees,
+ usage);
+}
+
+status_t ACodec::configureOutputBuffersFromNativeWindow(
+ OMX_U32 *bufferCount, OMX_U32 *bufferSize,
+ OMX_U32 *minUndequeuedBuffers) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err == OK) {
+ err = setupNativeWindowSizeFormatAndUsage(mNativeWindow.get());
}
-
- int consumerUsage = 0;
- err = mNativeWindow->query(
- mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS,
- &consumerUsage);
- if (err != 0) {
- ALOGW("failed to get consumer usage bits. ignoring");
- err = 0;
- }
-
- ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec) + %#x(Consumer) = %#x",
- omxUsage, usage, consumerUsage, usage | consumerUsage);
- usage |= consumerUsage;
- err = native_window_set_usage(
- mNativeWindow.get(),
- usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
-
- if (err != 0) {
- ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+ if (err != OK) {
return err;
}
@@ -796,6 +896,11 @@
return err;
mNumUndequeuedBuffers = minUndequeuedBuffers;
+ if (!mStoreMetaDataInOutputBuffers) {
+ static_cast<Surface*>(mNativeWindow.get())
+ ->getIGraphicBufferProducer()->allowAllocation(true);
+ }
+
ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
"output port",
mComponentName.c_str(), bufferCount, bufferSize);
@@ -854,6 +959,11 @@
}
}
+ if (!mStoreMetaDataInOutputBuffers) {
+ static_cast<Surface*>(mNativeWindow.get())
+ ->getIGraphicBufferProducer()->allowAllocation(false);
+ }
+
return err;
}
@@ -880,7 +990,9 @@
sp<IMemory> mem = mDealer[kPortIndexOutput]->allocate(
sizeof(struct VideoDecoderOutputMetaData));
- CHECK(mem.get() != NULL);
+ if (mem.get() == NULL) {
+ return NO_MEMORY;
+ }
info.mData = new ABuffer(mem->pointer(), mem->size());
// we use useBuffer for metadata regardless of quirks
@@ -893,6 +1005,45 @@
mComponentName.c_str(), info.mBufferID, mem->pointer());
}
+ if (mLegacyAdaptiveExperiment) {
+ // preallocate and preregister buffers
+ static_cast<Surface *>(mNativeWindow.get())
+ ->getIGraphicBufferProducer()->allowAllocation(true);
+
+ ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
+ "output port",
+ mComponentName.c_str(), bufferCount, bufferSize);
+
+ // Dequeue buffers then cancel them all
+ for (OMX_U32 i = 0; i < bufferCount; i++) {
+ BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+
+ ANativeWindowBuffer *buf;
+ err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf);
+ if (err != 0) {
+ ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
+ break;
+ }
+
+ sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
+ mOMX->updateGraphicBufferInMeta(
+ mNode, kPortIndexOutput, graphicBuffer, info->mBufferID);
+ info->mStatus = BufferInfo::OWNED_BY_US;
+ info->mGraphicBuffer = graphicBuffer;
+ }
+
+ for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) {
+ BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+ status_t error = cancelBufferToNativeWindow(info);
+ if (err == OK) {
+ err = error;
+ }
+ }
+
+ static_cast<Surface*>(mNativeWindow.get())
+ ->getIGraphicBufferProducer()->allowAllocation(false);
+ }
+
mMetaDataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
return err;
}
@@ -910,11 +1061,12 @@
mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get());
--mMetaDataBuffersToSubmit;
- CHECK_EQ(mOMX->fillBuffer(mNode, info->mBufferID),
- (status_t)OK);
+ status_t err = mOMX->fillBuffer(mNode, info->mBufferID);
+ if (err == OK) {
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ }
- info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
- return OK;
+ return err;
}
status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
@@ -944,26 +1096,57 @@
return NULL;
}
- if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) {
- ALOGE("dequeueBuffer failed.");
- return NULL;
- }
+ do {
+ if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) {
+ ALOGE("dequeueBuffer failed.");
+ return NULL;
+ }
+ bool stale = false;
+ for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+ BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+
+ if (info->mGraphicBuffer != NULL &&
+ info->mGraphicBuffer->handle == buf->handle) {
+ // Since consumers can attach buffers to BufferQueues, it is possible
+ // that a known yet stale buffer can return from a surface that we
+ // once used. We can simply ignore this as we have already dequeued
+ // this buffer properly. NOTE: this does not eliminate all cases,
+ // e.g. it is possible that we have queued the valid buffer to the
+ // NW, and a stale copy of the same buffer gets dequeued - which will
+ // be treated as the valid buffer by ACodec.
+ if (info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ ALOGI("dequeued stale buffer %p. discarding", buf);
+ stale = true;
+ break;
+ }
+ ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer());
+ info->mStatus = BufferInfo::OWNED_BY_US;
+
+ return info;
+ }
+ }
+
+ // It is also possible to receive a previously unregistered buffer
+ // in non-meta mode. These should be treated as stale buffers. The
+ // same is possible in meta mode, in which case, it will be treated
+ // as a normal buffer, which is not desirable.
+ // TODO: fix this.
+ if (!stale && (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment)) {
+ ALOGI("dequeued unrecognized (stale) buffer %p. discarding", buf);
+ stale = true;
+ }
+ if (stale) {
+ // TODO: detach stale buffer, but there is no API yet to do it.
+ buf = NULL;
+ }
+ } while (buf == NULL);
+
+ // get oldest undequeued buffer
BufferInfo *oldest = NULL;
for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
BufferInfo *info =
&mBuffers[kPortIndexOutput].editItemAt(i);
-
- if (info->mGraphicBuffer != NULL &&
- info->mGraphicBuffer->handle == buf->handle) {
- CHECK_EQ((int)info->mStatus,
- (int)BufferInfo::OWNED_BY_NATIVE_WINDOW);
-
- info->mStatus = BufferInfo::OWNED_BY_US;
-
- return info;
- }
-
if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW &&
(oldest == NULL ||
// avoid potential issues from counter rolling over
@@ -990,7 +1173,7 @@
CHECK_EQ(metaData->eType, kMetadataBufferTypeGrallocSource);
ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)",
- oldest - &mBuffers[kPortIndexOutput][0],
+ (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
mDequeueCounter - oldest->mDequeuedAt,
metaData->pHandle,
oldest->mGraphicBuffer->handle, oldest->mData->base());
@@ -1004,16 +1187,21 @@
}
status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
+ status_t err = OK;
for (size_t i = mBuffers[portIndex].size(); i-- > 0;) {
- CHECK_EQ((status_t)OK, freeBuffer(portIndex, i));
+ status_t err2 = freeBuffer(portIndex, i);
+ if (err == OK) {
+ err = err2;
+ }
}
mDealer[portIndex].clear();
- return OK;
+ return err;
}
status_t ACodec::freeOutputBuffersNotOwnedByComponent() {
+ status_t err = OK;
for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
BufferInfo *info =
&mBuffers[kPortIndexOutput].editItemAt(i);
@@ -1022,11 +1210,14 @@
// or being drained.
if (info->mStatus != BufferInfo::OWNED_BY_COMPONENT &&
info->mStatus != BufferInfo::OWNED_BY_DOWNSTREAM) {
- CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i));
+ status_t err2 = freeBuffer(kPortIndexOutput, i);
+ if (err == OK) {
+ err = err2;
+ }
}
}
- return OK;
+ return err;
}
status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
@@ -1040,13 +1231,11 @@
cancelBufferToNativeWindow(info);
}
- CHECK_EQ(mOMX->freeBuffer(
- mNode, portIndex, info->mBufferID),
- (status_t)OK);
-
+ status_t err = mOMX->freeBuffer(mNode, portIndex, info->mBufferID);
+ // remove buffer even if mOMX->freeBuffer fails
mBuffers[portIndex].removeAt(i);
- return OK;
+ return err;
}
ACodec::BufferInfo *ACodec::findBufferByID(
@@ -1063,8 +1252,7 @@
}
}
- TRESPASS();
-
+ ALOGE("Could not find buffer with ID %u", bufferID);
return NULL;
}
@@ -1177,6 +1365,7 @@
mIsEncoder = encoder;
+
status_t err = setComponentRole(encoder /* isEncoder */, mime);
if (err != OK) {
@@ -1235,6 +1424,7 @@
// sps/pps to idr frames, since in metadata mode the bitstream is in an
// opaque handle, to which we don't have access.
int32_t video = !strncasecmp(mime, "video/", 6);
+ mIsVideo = video;
if (encoder && video) {
OMX_BOOL enable = (OMX_BOOL) (prependSPSPPS
&& msg->findInt32("store-metadata-in-buffers-output", &storeMeta)
@@ -1280,6 +1470,7 @@
bool haveNativeWindow = msg->findObject("native-window", &obj)
&& obj != NULL && video && !encoder;
mStoreMetaDataInOutputBuffers = false;
+ mLegacyAdaptiveExperiment = false;
if (video && !encoder) {
inputFormat->setInt32("adaptive-playback", false);
@@ -1294,9 +1485,8 @@
}
}
if (haveNativeWindow) {
- sp<NativeWindowWrapper> windowWrapper(
- static_cast<NativeWindowWrapper *>(obj.get()));
- sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow();
+ sp<ANativeWindow> nativeWindow =
+ static_cast<ANativeWindow *>(static_cast<Surface *>(obj.get()));
// START of temporary support for automatic FRC - THIS WILL BE REMOVED
int32_t autoFrc;
@@ -1418,6 +1608,9 @@
ALOGV("[%s] storeMetaDataInBuffers succeeded",
mComponentName.c_str());
mStoreMetaDataInOutputBuffers = true;
+ mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled(
+ "legacy-adaptive", !msg->contains("no-experiments"));
+
inputFormat->setInt32("adaptive-playback", true);
}
@@ -1455,28 +1648,31 @@
}
if (haveNativeWindow) {
- sp<NativeWindowWrapper> nativeWindow(
- static_cast<NativeWindowWrapper *>(obj.get()));
- CHECK(nativeWindow != NULL);
- mNativeWindow = nativeWindow->getNativeWindow();
-
- native_window_set_scaling_mode(
- mNativeWindow.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ mNativeWindow = static_cast<Surface *>(obj.get());
}
// initialize native window now to get actual output format
// TODO: this is needed for some encoders even though they don't use native window
- CHECK_EQ((status_t)OK, initNativeWindow());
+ err = initNativeWindow();
+ if (err != OK) {
+ return err;
+ }
// fallback for devices that do not handle flex-YUV for native buffers
if (haveNativeWindow) {
int32_t requestedColorFormat = OMX_COLOR_FormatUnused;
if (msg->findInt32("color-format", &requestedColorFormat) &&
requestedColorFormat == OMX_COLOR_FormatYUV420Flexible) {
- CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK);
+ status_t err = getPortFormat(kPortIndexOutput, outputFormat);
+ if (err != OK) {
+ return err;
+ }
int32_t colorFormat = OMX_COLOR_FormatUnused;
OMX_U32 flexibleEquivalent = OMX_COLOR_FormatUnused;
- CHECK(outputFormat->findInt32("color-format", &colorFormat));
+ if (!outputFormat->findInt32("color-format", &colorFormat)) {
+ ALOGE("ouptut port did not have a color format (wrong domain?)");
+ return BAD_VALUE;
+ }
ALOGD("[%s] Requested output format %#x and got %#x.",
mComponentName.c_str(), requestedColorFormat, colorFormat);
if (!isFlexibleColorFormat(
@@ -1598,7 +1794,7 @@
err = setupG711Codec(encoder, sampleRate, numChannels);
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {
- int32_t numChannels, sampleRate, compressionLevel = -1;
+ int32_t numChannels = 0, sampleRate = 0, compressionLevel = -1;
if (encoder &&
(!msg->findInt32("channel-count", &numChannels)
|| !msg->findInt32("sample-rate", &sampleRate))) {
@@ -1685,13 +1881,26 @@
err = setPriority(priority);
}
+ int32_t rateInt = -1;
+ float rateFloat = -1;
+ if (!msg->findFloat("operating-rate", &rateFloat)) {
+ msg->findInt32("operating-rate", &rateInt);
+ rateFloat = (float)rateInt; // 16MHz (FLINTMAX) is OK for upper bound.
+ }
+ if (rateFloat > 0) {
+ err = setOperatingRate(rateFloat, video);
+ }
+
mBaseOutputFormat = outputFormat;
- CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK);
- CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK);
- mInputFormat = inputFormat;
- mOutputFormat = outputFormat;
-
+ err = getPortFormat(kPortIndexInput, inputFormat);
+ if (err == OK) {
+ err = getPortFormat(kPortIndexOutput, outputFormat);
+ if (err == OK) {
+ mInputFormat = inputFormat;
+ mOutputFormat = outputFormat;
+ }
+ }
return err;
}
@@ -1711,6 +1920,34 @@
return OK;
}
+status_t ACodec::setOperatingRate(float rateFloat, bool isVideo) {
+ if (rateFloat < 0) {
+ return BAD_VALUE;
+ }
+ OMX_U32 rate;
+ if (isVideo) {
+ if (rateFloat > 65535) {
+ return BAD_VALUE;
+ }
+ rate = (OMX_U32)(rateFloat * 65536.0f + 0.5f);
+ } else {
+ if (rateFloat > UINT_MAX) {
+ return BAD_VALUE;
+ }
+ rate = (OMX_U32)(rateFloat);
+ }
+ OMX_PARAM_U32TYPE config;
+ InitOMXParams(&config);
+ config.nU32 = rate;
+ status_t err = mOMX->setConfig(
+ mNode, (OMX_INDEXTYPE)OMX_IndexConfigOperatingRate,
+ &config, sizeof(config));
+ if (err != OK) {
+ ALOGI("codec does not support config operating rate (err %d)", err);
+ }
+ return OK;
+}
+
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
@@ -1743,7 +1980,10 @@
return err;
}
- CHECK(def.nBufferSize >= size);
+ if (def.nBufferSize < size) {
+ ALOGE("failed to set min buffer size to %zu (is still %u)", size, def.nBufferSize);
+ return FAILED_TRANSACTION;
+ }
return OK;
}
@@ -2071,7 +2311,9 @@
}
status_t ACodec::setupG711Codec(bool encoder, int32_t sampleRate, int32_t numChannels) {
- CHECK(!encoder); // XXX TODO
+ if (encoder) {
+ return INVALID_OPERATION;
+ }
return setupRawAudioFormat(
kPortIndexInput, sampleRate, numChannels);
@@ -3180,8 +3422,9 @@
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
- CHECK_EQ(err, (status_t)OK);
+ if (err != OK) {
+ return err;
+ }
if (portIndex == kPortIndexInput) {
// XXX Need a (much) better heuristic to compute input buffer sizes.
@@ -3191,7 +3434,10 @@
}
}
- CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
+ if (def.eDomain != OMX_PortDomainVideo) {
+ ALOGE("expected video port, got %s(%d)", asString(def.eDomain), def.eDomain);
+ return FAILED_TRANSACTION;
+ }
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
@@ -3468,17 +3714,20 @@
}
status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> ¬ify) {
- // TODO: catch errors an return them instead of using CHECK
+ const char *niceIndex = portIndex == kPortIndexInput ? "input" : "output";
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)),
- (status_t)OK);
+ status_t err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != OK) {
+ return err;
+ }
- CHECK_EQ((int)def.eDir,
- (int)(portIndex == kPortIndexOutput ? OMX_DirOutput : OMX_DirInput));
+ if (def.eDir != (portIndex == kPortIndexOutput ? OMX_DirOutput : OMX_DirInput)) {
+ ALOGE("unexpected dir: %s(%d) on %s port", asString(def.eDir), def.eDir, niceIndex);
+ return BAD_VALUE;
+ }
switch (def.eDomain) {
case OMX_PortDomainVideo:
@@ -3541,12 +3790,16 @@
rect.nHeight = videoDef->nFrameHeight;
}
- CHECK_GE(rect.nLeft, 0);
- CHECK_GE(rect.nTop, 0);
- CHECK_GE(rect.nWidth, 0u);
- CHECK_GE(rect.nHeight, 0u);
- CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth);
- CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight);
+ if (rect.nLeft < 0 ||
+ rect.nTop < 0 ||
+ rect.nLeft + rect.nWidth > videoDef->nFrameWidth ||
+ rect.nTop + rect.nHeight > videoDef->nFrameHeight) {
+ ALOGE("Wrong cropped rect (%d, %d) - (%u, %u) vs. frame (%u, %u)",
+ rect.nLeft, rect.nTop,
+ rect.nLeft + rect.nWidth, rect.nTop + rect.nHeight,
+ videoDef->nFrameWidth, videoDef->nFrameHeight);
+ return BAD_VALUE;
+ }
notify->setRect(
"crop",
@@ -3603,7 +3856,13 @@
default:
{
- CHECK(mIsEncoder ^ (portIndex == kPortIndexInput));
+ if (mIsEncoder ^ (portIndex == kPortIndexOutput)) {
+ // should be CodingUnused
+ ALOGE("Raw port video compression format is %s(%d)",
+ asString(videoDef->eCompressionFormat),
+ videoDef->eCompressionFormat);
+ return BAD_VALUE;
+ }
AString mime;
if (GetMimeTypeForVideoCoding(
videoDef->eCompressionFormat, &mime) != OK) {
@@ -3634,20 +3893,25 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioPcm,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
- CHECK_GT(params.nChannels, 0);
- CHECK(params.nChannels == 1 || params.bInterleaved);
- CHECK_EQ(params.nBitPerSample, 16u);
-
- CHECK_EQ((int)params.eNumData,
- (int)OMX_NumericalDataSigned);
-
- CHECK_EQ((int)params.ePCMMode,
- (int)OMX_AUDIO_PCMModeLinear);
+ if (params.nChannels <= 0
+ || (params.nChannels != 1 && !params.bInterleaved)
+ || params.nBitPerSample != 16u
+ || params.eNumData != OMX_NumericalDataSigned
+ || params.ePCMMode != OMX_AUDIO_PCMModeLinear) {
+ ALOGE("unsupported PCM port: %u channels%s, %u-bit, %s(%d), %s(%d) mode ",
+ params.nChannels,
+ params.bInterleaved ? " interleaved" : "",
+ params.nBitPerSample,
+ asString(params.eNumData), params.eNumData,
+ asString(params.ePCMMode), params.ePCMMode);
+ return FAILED_TRANSACTION;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
notify->setInt32("channel-count", params.nChannels);
@@ -3665,10 +3929,11 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioAac,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioAac, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
notify->setInt32("channel-count", params.nChannels);
@@ -3682,10 +3947,11 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioAmr,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioAmr, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setInt32("channel-count", 1);
if (params.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0) {
@@ -3708,10 +3974,11 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioFlac,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioFlac, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_FLAC);
notify->setInt32("channel-count", params.nChannels);
@@ -3725,10 +3992,11 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioMp3,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioMp3, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_MPEG);
notify->setInt32("channel-count", params.nChannels);
@@ -3742,10 +4010,11 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioVorbis,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioVorbis, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_VORBIS);
notify->setInt32("channel-count", params.nChannels);
@@ -3759,11 +4028,12 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ((status_t)OK, mOMX->getParameter(
- mNode,
- (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
- ¶ms,
- sizeof(params)));
+ err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+ ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AC3);
notify->setInt32("channel-count", params.nChannels);
@@ -3777,11 +4047,12 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ((status_t)OK, mOMX->getParameter(
- mNode,
- (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3,
- ¶ms,
- sizeof(params)));
+ err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3,
+ ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_EAC3);
notify->setInt32("channel-count", params.nChannels);
@@ -3795,11 +4066,12 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ((status_t)OK, mOMX->getParameter(
- mNode,
- (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus,
- ¶ms,
- sizeof(params)));
+ err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus,
+ ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_OPUS);
notify->setInt32("channel-count", params.nChannels);
@@ -3813,11 +4085,11 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ((status_t)OK, mOMX->getParameter(
- mNode,
- (OMX_INDEXTYPE)OMX_IndexParamAudioPcm,
- ¶ms,
- sizeof(params)));
+ err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
const char *mime = NULL;
if (params.ePCMMode == OMX_AUDIO_PCMModeMULaw) {
@@ -3839,10 +4111,11 @@
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioPcm,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params));
+ if (err != OK) {
+ return err;
+ }
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_MSGSM);
notify->setInt32("channel-count", params.nChannels);
@@ -3851,14 +4124,16 @@
}
default:
- ALOGE("UNKNOWN AUDIO CODING: %d\n", audioDef->eEncoding);
- TRESPASS();
+ ALOGE("Unsupported audio coding: %s(%d)\n",
+ asString(audioDef->eEncoding), audioDef->eEncoding);
+ return BAD_TYPE;
}
break;
}
default:
- TRESPASS();
+ ALOGE("Unsupported domain: %s(%d)", asString(def.eDomain), def.eDomain);
+ return BAD_TYPE;
}
return OK;
@@ -3868,7 +4143,10 @@
sp<AMessage> notify = mBaseOutputFormat->dup();
notify->setInt32("what", kWhatOutputFormatChanged);
- CHECK_EQ(getPortFormat(kPortIndexOutput, notify), (status_t)OK);
+ if (getPortFormat(kPortIndexOutput, notify) != OK) {
+ ALOGE("[%s] Failed to get port format to send format change", mComponentName.c_str());
+ return;
+ }
AString mime;
CHECK(notify->findString("mime", &mime));
@@ -3888,9 +4166,7 @@
if (mSkipCutBuffer != NULL) {
size_t prevbufsize = mSkipCutBuffer->size();
if (prevbufsize != 0) {
- ALOGW("Replacing SkipCutBuffer holding %d "
- "bytes",
- prevbufsize);
+ ALOGW("Replacing SkipCutBuffer holding %zu bytes", prevbufsize);
}
}
mSkipCutBuffer = new SkipCutBuffer(
@@ -3921,150 +4197,6 @@
notify->post();
}
-status_t ACodec::pushBlankBuffersToNativeWindow() {
- status_t err = NO_ERROR;
- ANativeWindowBuffer* anb = NULL;
- int numBufs = 0;
- int minUndequeuedBufs = 0;
-
- // We need to reconnect to the ANativeWindow as a CPU client to ensure that
- // no frames get dropped by SurfaceFlinger assuming that these are video
- // frames.
- err = native_window_api_disconnect(mNativeWindow.get(),
- NATIVE_WINDOW_API_MEDIA);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = native_window_api_connect(mNativeWindow.get(),
- NATIVE_WINDOW_API_CPU);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = native_window_set_buffers_geometry(mNativeWindow.get(), 1, 1,
- HAL_PIXEL_FORMAT_RGBX_8888);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: set_buffers_geometry failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = native_window_set_scaling_mode(mNativeWindow.get(),
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank_frames: set_scaling_mode failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = native_window_set_usage(mNativeWindow.get(),
- GRALLOC_USAGE_SW_WRITE_OFTEN);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: set_usage failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = mNativeWindow->query(mNativeWindow.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query "
- "failed: %s (%d)", strerror(-err), -err);
- goto error;
- }
-
- numBufs = minUndequeuedBufs + 1;
- err = native_window_set_buffer_count(mNativeWindow.get(), numBufs);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- // We push numBufs + 1 buffers to ensure that we've drawn into the same
- // buffer twice. This should guarantee that the buffer has been displayed
- // on the screen and then been replaced, so an previous video frames are
- // guaranteed NOT to be currently displayed.
- for (int i = 0; i < numBufs + 1; i++) {
- err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
-
- // Fill the buffer with the a 1x1 checkerboard pattern ;)
- uint32_t* img = NULL;
- err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: lock failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- *img = 0;
-
- err = buf->unlock();
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: unlock failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = mNativeWindow->queueBuffer(mNativeWindow.get(),
- buf->getNativeBuffer(), -1);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- anb = NULL;
- }
-
-error:
-
- if (err != NO_ERROR) {
- // Clean up after an error.
- if (anb != NULL) {
- mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1);
- }
-
- native_window_api_disconnect(mNativeWindow.get(),
- NATIVE_WINDOW_API_CPU);
- native_window_api_connect(mNativeWindow.get(),
- NATIVE_WINDOW_API_MEDIA);
-
- return err;
- } else {
- // Clean up after success.
- err = native_window_api_disconnect(mNativeWindow.get(),
- NATIVE_WINDOW_API_CPU);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = native_window_api_connect(mNativeWindow.get(),
- NATIVE_WINDOW_API_MEDIA);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- return NO_ERROR;
- }
-}
-
////////////////////////////////////////////////////////////////////////////////
ACodec::PortDescription::PortDescription() {
@@ -4137,7 +4269,26 @@
return onOMXMessage(msg);
}
+ case ACodec::kWhatSetSurface:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("surface", &obj));
+
+ status_t err =
+ ADebug::isExperimentEnabled("legacy-setsurface") ? BAD_VALUE :
+ mCodec->handleSetSurface(static_cast<Surface *>(obj.get()));
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
case ACodec::kWhatCreateInputSurface:
+ case ACodec::kWhatSetInputSurface:
case ACodec::kWhatSignalEndOfInputStream:
{
// This may result in an app illegal state exception.
@@ -4180,7 +4331,7 @@
// there is a possibility that this is an outstanding message for a
// codec that we have already destroyed
- if (mCodec->mNode == NULL) {
+ if (mCodec->mNode == 0) {
ALOGI("ignoring message as already freed component: %s",
msg->debugString().c_str());
return true;
@@ -4188,7 +4339,10 @@
IOMX::node_id nodeID;
CHECK(msg->findInt32("node", (int32_t*)&nodeID));
- CHECK_EQ(nodeID, mCodec->mNode);
+ if (nodeID != mCodec->mNode) {
+ ALOGE("Unexpected message for nodeID: %u, should have been %u", nodeID, mCodec->mNode);
+ return false;
+ }
switch (type) {
case omx_message::EVENT:
@@ -4244,21 +4398,21 @@
}
default:
- TRESPASS();
- break;
+ ALOGE("Unexpected message type: %d", type);
+ return false;
}
}
bool ACodec::BaseState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
if (event != OMX_EventError) {
- ALOGV("[%s] EVENT(%d, 0x%08lx, 0x%08lx)",
+ ALOGV("[%s] EVENT(%d, 0x%08x, 0x%08x)",
mCodec->mComponentName.c_str(), event, data1, data2);
return false;
}
- ALOGE("[%s] ERROR(0x%08lx)", mCodec->mComponentName.c_str(), data1);
+ ALOGE("[%s] ERROR(0x%08x)", mCodec->mComponentName.c_str(), data1);
// verify OMX component sends back an error we expect.
OMX_ERRORTYPE omxError = (OMX_ERRORTYPE)data1;
@@ -4272,7 +4426,7 @@
}
bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
- ALOGV("[%s] onOMXEmptyBufferDone %p",
+ ALOGV("[%s] onOMXEmptyBufferDone %u",
mCodec->mComponentName.c_str(), bufferID);
BufferInfo *info =
@@ -4298,12 +4452,10 @@
postFillThisBuffer(info);
break;
+ case FREE_BUFFERS:
default:
- {
- CHECK_EQ((int)mode, (int)FREE_BUFFERS);
- TRESPASS(); // Not currently used
- break;
- }
+ ALOGE("SHOULD NOT REACH HERE: cannot free empty output buffers");
+ return false;
}
return true;
@@ -4398,7 +4550,7 @@
}
if (buffer != info->mData) {
- ALOGV("[%s] Needs to copy input data for buffer %p. (%p != %p)",
+ ALOGV("[%s] Needs to copy input data for buffer %u. (%p != %p)",
mCodec->mComponentName.c_str(),
bufferID,
buffer.get(), info->mData.get());
@@ -4408,18 +4560,18 @@
}
if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
- ALOGV("[%s] calling emptyBuffer %p w/ codec specific data",
+ ALOGV("[%s] calling emptyBuffer %u w/ codec specific data",
mCodec->mComponentName.c_str(), bufferID);
} else if (flags & OMX_BUFFERFLAG_EOS) {
- ALOGV("[%s] calling emptyBuffer %p w/ EOS",
+ ALOGV("[%s] calling emptyBuffer %u w/ EOS",
mCodec->mComponentName.c_str(), bufferID);
} else {
#if TRACK_BUFFER_TIMING
- ALOGI("[%s] calling emptyBuffer %p w/ time %lld us",
- mCodec->mComponentName.c_str(), bufferID, timeUs);
+ ALOGI("[%s] calling emptyBuffer %u w/ time %lld us",
+ mCodec->mComponentName.c_str(), bufferID, (long long)timeUs);
#else
- ALOGV("[%s] calling emptyBuffer %p w/ time %lld us",
- mCodec->mComponentName.c_str(), bufferID, timeUs);
+ ALOGV("[%s] calling emptyBuffer %u w/ time %lld us",
+ mCodec->mComponentName.c_str(), bufferID, (long long)timeUs);
#endif
}
@@ -4473,7 +4625,7 @@
mCodec->mComponentName.c_str());
}
- ALOGV("[%s] calling emptyBuffer %p signalling EOS",
+ ALOGV("[%s] calling emptyBuffer %u signalling EOS",
mCodec->mComponentName.c_str(), bufferID);
CHECK_EQ(mCodec->mOMX->emptyBuffer(
@@ -4493,8 +4645,11 @@
break;
}
+ case FREE_BUFFERS:
+ break;
+
default:
- CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+ ALOGE("invalid port mode: %d", mode);
break;
}
}
@@ -4638,14 +4793,14 @@
break;
}
- default:
- {
- CHECK_EQ((int)mode, (int)FREE_BUFFERS);
-
+ case FREE_BUFFERS:
CHECK_EQ((status_t)OK,
mCodec->freeBuffer(kPortIndexOutput, index));
break;
- }
+
+ default:
+ ALOGE("Invalid port mode: %d", mode);
+ return false;
}
return true;
@@ -4660,10 +4815,9 @@
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
android_native_rect_t crop;
- if (msg->findRect("crop",
- &crop.left, &crop.top, &crop.right, &crop.bottom)) {
- CHECK_EQ(0, native_window_set_crop(
- mCodec->mNativeWindow.get(), &crop));
+ if (msg->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)) {
+ status_t err = native_window_set_crop(mCodec->mNativeWindow.get(), &crop);
+ ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err);
}
int32_t render;
@@ -4748,14 +4902,16 @@
break;
}
- default:
+ case FREE_BUFFERS:
{
- CHECK_EQ((int)mode, (int)FREE_BUFFERS);
-
CHECK_EQ((status_t)OK,
mCodec->freeBuffer(kPortIndexOutput, index));
break;
}
+
+ default:
+ ALOGE("Invalid port mode: %d", mode);
+ return;
}
}
@@ -4774,7 +4930,7 @@
}
mCodec->mNativeWindow.clear();
- mCodec->mNode = NULL;
+ mCodec->mNode = 0;
mCodec->mOMX.clear();
mCodec->mQuirks = 0;
mCodec->mFlags = 0;
@@ -4852,10 +5008,13 @@
bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
ALOGV("onAllocateComponent");
- CHECK(mCodec->mNode == NULL);
+ CHECK(mCodec->mNode == 0);
OMXClient client;
- CHECK_EQ(client.connect(), (status_t)OK);
+ if (client.connect() != OK) {
+ mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
+ return false;
+ }
sp<IOMX> omx = client.interface();
@@ -4900,8 +5059,9 @@
}
sp<CodecObserver> observer = new CodecObserver;
- IOMX::node_id node = NULL;
+ IOMX::node_id node = 0;
+ status_t err = OMX_ErrorComponentNotFound;
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).mName.string();
@@ -4910,7 +5070,7 @@
pid_t tid = gettid();
int prevPriority = androidGetThreadPriority(tid);
androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
- status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+ err = omx->allocateNode(componentName.c_str(), observer, &node);
androidSetThreadPriority(tid, prevPriority);
if (err == OK) {
@@ -4919,18 +5079,18 @@
ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
}
- node = NULL;
+ node = 0;
}
- if (node == NULL) {
+ if (node == 0) {
if (!mime.empty()) {
- ALOGE("Unable to instantiate a %scoder for type '%s'.",
- encoder ? "en" : "de", mime.c_str());
+ ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.",
+ encoder ? "en" : "de", mime.c_str(), err);
} else {
- ALOGE("Unable to instantiate codec '%s'.", componentName.c_str());
+ ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);
}
- mCodec->signalError(OMX_ErrorComponentNotFound);
+ mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err));
return false;
}
@@ -5029,6 +5189,13 @@
break;
}
+ case ACodec::kWhatSetInputSurface:
+ {
+ onSetInputSurface(msg);
+ handled = true;
+ break;
+ }
+
case ACodec::kWhatStart:
{
onStart();
@@ -5070,7 +5237,7 @@
const sp<AMessage> &msg) {
ALOGV("onConfigureComponent");
- CHECK(mCodec->mNode != NULL);
+ CHECK(mCodec->mNode != 0);
AString mime;
CHECK(msg->findString("mime", &mime));
@@ -5096,20 +5263,10 @@
return true;
}
-void ACodec::LoadedState::onCreateInputSurface(
- const sp<AMessage> & /* msg */) {
- ALOGV("onCreateInputSurface");
+status_t ACodec::LoadedState::setupInputSurface() {
+ status_t err = OK;
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatInputSurfaceCreated);
-
- sp<IGraphicBufferProducer> bufferProducer;
- status_t err;
-
- err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput,
- &bufferProducer);
-
- if (err == OK && mCodec->mRepeatFrameDelayUs > 0ll) {
+ if (mCodec->mRepeatFrameDelayUs > 0ll) {
err = mCodec->mOMX->setInternalOption(
mCodec->mNode,
kPortIndexInput,
@@ -5122,10 +5279,11 @@
"frames (err %d)",
mCodec->mComponentName.c_str(),
err);
+ return err;
}
}
- if (err == OK && mCodec->mMaxPtsGapUs > 0ll) {
+ if (mCodec->mMaxPtsGapUs > 0ll) {
err = mCodec->mOMX->setInternalOption(
mCodec->mNode,
kPortIndexInput,
@@ -5137,10 +5295,11 @@
ALOGE("[%s] Unable to configure max timestamp gap (err %d)",
mCodec->mComponentName.c_str(),
err);
+ return err;
}
}
- if (err == OK && mCodec->mMaxFps > 0) {
+ if (mCodec->mMaxFps > 0) {
err = mCodec->mOMX->setInternalOption(
mCodec->mNode,
kPortIndexInput,
@@ -5152,10 +5311,11 @@
ALOGE("[%s] Unable to configure max fps (err %d)",
mCodec->mComponentName.c_str(),
err);
+ return err;
}
}
- if (err == OK && mCodec->mTimePerCaptureUs > 0ll
+ if (mCodec->mTimePerCaptureUs > 0ll
&& mCodec->mTimePerFrameUs > 0ll) {
int64_t timeLapse[2];
timeLapse[0] = mCodec->mTimePerFrameUs;
@@ -5171,10 +5331,11 @@
ALOGE("[%s] Unable to configure time lapse (err %d)",
mCodec->mComponentName.c_str(),
err);
+ return err;
}
}
- if (err == OK && mCodec->mCreateInputBuffersSuspended) {
+ if (mCodec->mCreateInputBuffersSuspended) {
bool suspend = true;
err = mCodec->mOMX->setInternalOption(
mCodec->mNode,
@@ -5187,9 +5348,28 @@
ALOGE("[%s] Unable to configure option to suspend (err %d)",
mCodec->mComponentName.c_str(),
err);
+ return err;
}
}
+ return OK;
+}
+
+void ACodec::LoadedState::onCreateInputSurface(
+ const sp<AMessage> & /* msg */) {
+ ALOGV("onCreateInputSurface");
+
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", CodecBase::kWhatInputSurfaceCreated);
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ status_t err = mCodec->mOMX->createInputSurface(
+ mCodec->mNode, kPortIndexInput, &bufferProducer);
+
+ if (err == OK) {
+ err = setupInputSurface();
+ }
+
if (err == OK) {
notify->setObject("input-surface",
new BufferProducerWrapper(bufferProducer));
@@ -5204,6 +5384,35 @@
notify->post();
}
+void ACodec::LoadedState::onSetInputSurface(
+ const sp<AMessage> &msg) {
+ ALOGV("onSetInputSurface");
+
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", CodecBase::kWhatInputSurfaceAccepted);
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("input-surface", &obj));
+ sp<PersistentSurface> surface = static_cast<PersistentSurface *>(obj.get());
+
+ status_t err = mCodec->mOMX->setInputSurface(
+ mCodec->mNode, kPortIndexInput, surface->getBufferConsumer());
+
+ if (err == OK) {
+ err = setupInputSurface();
+ }
+
+ if (err != OK) {
+ // Can't use mCodec->signalError() here -- MediaCodec won't forward
+ // the error through because it's in the "configured" state. We
+ // send a kWhatInputSurfaceAccepted with an error value instead.
+ ALOGE("[%s] onSetInputSurface returning error %d",
+ mCodec->mComponentName.c_str(), err);
+ notify->setInt32("err", err);
+ }
+ notify->post();
+}
+
void ACodec::LoadedState::onStart() {
ALOGV("onStart");
@@ -5410,7 +5619,7 @@
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
}
- ALOGV("[%s] calling fillBuffer %p",
+ ALOGV("[%s] calling fillBuffer %u",
mCodec->mComponentName.c_str(), info->mBufferID);
CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
@@ -5438,7 +5647,10 @@
submitOutputBuffers();
// Post all available input buffers
- CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
+ if (mCodec->mBuffers[kPortIndexInput].size() == 0u) {
+ ALOGW("[%s] we don't have any input buffers to resume", mCodec->mComponentName.c_str());
+ }
+
for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); i++) {
BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);
if (info->mStatus == BufferInfo::OWNED_BY_US) {
@@ -5484,7 +5696,7 @@
case kWhatFlush:
{
ALOGV("[%s] ExecutingState flushing now "
- "(codec owns %d/%d input, %d/%d output).",
+ "(codec owns %zu/%zu input, %zu/%zu output).",
mCodec->mComponentName.c_str(),
mCodec->countBuffersOwnedByComponent(kPortIndexInput),
mCodec->mBuffers[kPortIndexInput].size(),
@@ -5632,6 +5844,15 @@
}
}
+ float rate;
+ if (params->findFloat("operating-rate", &rate) && rate > 0) {
+ status_t err = setOperatingRate(rate, mIsVideo);
+ if (err != OK) {
+ ALOGE("Failed to set parameter 'operating-rate' (err %d)", err);
+ return err;
+ }
+ }
+
return OK;
}
@@ -5666,7 +5887,7 @@
} else if (data2 == OMX_IndexConfigCommonOutputCrop) {
mCodec->mSentFormat = false;
} else {
- ALOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
+ ALOGV("[%s] OMX_EventPortSettingsChanged 0x%08x",
mCodec->mComponentName.c_str(), data2);
}
@@ -5772,7 +5993,10 @@
return true;
} else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
- CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+ if (data2 != (OMX_U32)kPortIndexOutput) {
+ ALOGW("ignoring EventCmdComplete OMX_CommandPortEnable for port %u", data2);
+ return false;
+ }
mCodec->mSentFormat = false;
@@ -5811,7 +6035,7 @@
{
// Don't send me a flush request if you previously wanted me
// to shutdown.
- TRESPASS();
+ ALOGE("Got flush request in IdleToLoadedState");
break;
}
@@ -5880,7 +6104,7 @@
// them has made it to the display. This allows the OMX
// component teardown to zero out any protected buffers
// without the risk of scanning out one of those buffers.
- mCodec->pushBlankBuffersToNativeWindow();
+ pushBlankBuffersToNativeWindow(mCodec->mNativeWindow.get());
}
mCodec->changeState(mCodec->mIdleToLoadedState);
@@ -5923,7 +6147,7 @@
{
// Don't send me a flush request if you previously wanted me
// to shutdown.
- TRESPASS();
+ ALOGW("Ignoring flush request in ExecutingToIdleState");
break;
}
@@ -5996,8 +6220,8 @@
bool ACodec::FlushingState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
- ALOGV("[%s] FlushingState onOMXEvent(%d,%ld)",
- mCodec->mComponentName.c_str(), event, data1);
+ ALOGV("[%s] FlushingState onOMXEvent(%u,%d)",
+ mCodec->mComponentName.c_str(), event, (OMX_S32)data1);
switch (event) {
case OMX_EventCmdComplete:
@@ -6005,19 +6229,28 @@
CHECK_EQ(data1, (OMX_U32)OMX_CommandFlush);
if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
- CHECK(!mFlushComplete[data2]);
+ if (mFlushComplete[data2]) {
+ ALOGW("Flush already completed for %s port",
+ data2 == kPortIndexInput ? "input" : "output");
+ return true;
+ }
mFlushComplete[data2] = true;
if (mFlushComplete[kPortIndexInput]
&& mFlushComplete[kPortIndexOutput]) {
changeStateIfWeOwnAllBuffers();
}
- } else {
- CHECK_EQ(data2, OMX_ALL);
- CHECK(mFlushComplete[kPortIndexInput]);
- CHECK(mFlushComplete[kPortIndexOutput]);
+ } else if (data2 == OMX_ALL) {
+ if (!mFlushComplete[kPortIndexInput] || !mFlushComplete[kPortIndexOutput]) {
+ ALOGW("received flush complete event for OMX_ALL before ports have been"
+ "flushed (%d/%d)",
+ mFlushComplete[kPortIndexInput], mFlushComplete[kPortIndexOutput]);
+ return false;
+ }
changeStateIfWeOwnAllBuffers();
+ } else {
+ ALOGW("data2 not OMX_ALL but %u in EventCmdComplete CommandFlush", data2);
}
return true;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index a2cbdaf..6010558 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -12,6 +12,7 @@
AudioPlayer.cpp \
AudioSource.cpp \
AwesomePlayer.cpp \
+ CallbackDataSource.cpp \
CameraSource.cpp \
CameraSourceTimeLapse.cpp \
ClockEstimator.cpp \
@@ -34,6 +35,7 @@
MediaClock.cpp \
MediaCodec.cpp \
MediaCodecList.cpp \
+ MediaCodecListOverrides.cpp \
MediaCodecSource.cpp \
MediaDefs.cpp \
MediaExtractor.cpp \
@@ -55,6 +57,7 @@
StagefrightMediaScanner.cpp \
StagefrightMetadataRetriever.cpp \
SurfaceMediaSource.cpp \
+ SurfaceUtils.cpp \
ThrottledSource.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
@@ -121,7 +124,8 @@
libdl \
libRScpp \
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall -DENABLE_STAGEFRIGHT_EXPERIMENTS
+LOCAL_CLANG := true
LOCAL_MODULE:= libstagefright
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index e24824b..dd9d393 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -408,11 +408,22 @@
}
}
-status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) {
+status_t AudioPlayer::setPlaybackRate(const AudioPlaybackRate &rate) {
if (mAudioSink.get() != NULL) {
- return mAudioSink->setPlaybackRatePermille(ratePermille);
+ return mAudioSink->setPlaybackRate(rate);
} else if (mAudioTrack != 0){
- return mAudioTrack->setSampleRate(ratePermille * mSampleRate / 1000);
+ return mAudioTrack->setPlaybackRate(rate);
+ } else {
+ return NO_INIT;
+ }
+}
+
+status_t AudioPlayer::getPlaybackRate(AudioPlaybackRate *rate /* nonnull */) {
+ if (mAudioSink.get() != NULL) {
+ return mAudioSink->getPlaybackRate(rate);
+ } else if (mAudioTrack != 0) {
+ *rate = mAudioTrack->getPlaybackRate();
+ return OK;
} else {
return NO_INIT;
}
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 804f131..e5a6a9b 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -50,7 +50,8 @@
}
AudioSource::AudioSource(
- audio_source_t inputSource, uint32_t sampleRate, uint32_t channelCount)
+ audio_source_t inputSource, const String16 &opPackageName, uint32_t sampleRate,
+ uint32_t channelCount)
: mStarted(false),
mSampleRate(sampleRate),
mPrevSampleTimeUs(0),
@@ -78,6 +79,7 @@
mRecord = new AudioRecord(
inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount),
+ opPackageName,
(size_t) (bufCount * frameCount),
AudioRecordCallbackFunction,
this,
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 87eef1e..c178091 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -241,6 +241,8 @@
mClockEstimator = new WindowedLinearFitEstimator();
+ mPlaybackSettings = AUDIO_PLAYBACK_RATE_DEFAULT;
+
reset();
}
@@ -341,6 +343,7 @@
reset_l();
+ fd = dup(fd);
sp<DataSource> dataSource = new FileSource(fd, offset, length);
status_t err = dataSource->initCheck();
@@ -360,7 +363,7 @@
return setDataSource_l(dataSource);
}
-status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source) {
+status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source __unused) {
return INVALID_OPERATION;
}
@@ -422,7 +425,7 @@
mBitrate = totalBitRate;
- ALOGV("mBitrate = %lld bits/sec", mBitrate);
+ ALOGV("mBitrate = %lld bits/sec", (long long)mBitrate);
{
Mutex::Autolock autoLock(mStatsLock);
@@ -1009,6 +1012,10 @@
return err;
}
}
+
+ if (mAudioPlayer != NULL) {
+ mAudioPlayer->setPlaybackRate(mPlaybackSettings);
+ }
}
if (mTimeSource == NULL && mAudioPlayer == NULL) {
@@ -1131,6 +1138,10 @@
}
if (err == OK) {
+ err = mAudioPlayer->setPlaybackRate(mPlaybackSettings);
+ }
+
+ if (err == OK) {
modifyFlags(AUDIO_RUNNING, SET);
mWatchForAudioEOS = true;
@@ -1722,7 +1733,7 @@
// we are now resuming. Signal new position to media time provider.
// Cannot signal another SEEK_COMPLETE, as existing clients may not expect
// multiple SEEK_COMPLETE responses to a single seek() request.
- if (mSeekNotificationSent && abs(mSeekTimeUs - videoTimeUs) > 10000) {
+ if (mSeekNotificationSent && llabs((long long)(mSeekTimeUs - videoTimeUs)) > 10000) {
// notify if we are resuming more than 10ms away from desired seek time
notifyListener_l(MEDIA_SKIPPED);
}
@@ -2358,7 +2369,7 @@
}
CHECK_GE(metaDataSize, 0ll);
- ALOGV("metaDataSize = %lld bytes", metaDataSize);
+ ALOGV("metaDataSize = %lld bytes", (long long)metaDataSize);
}
usleep(200000);
@@ -2553,14 +2564,6 @@
{
return setCacheStatCollectFreq(request);
}
- case KEY_PARAMETER_PLAYBACK_RATE_PERMILLE:
- {
- if (mAudioPlayer != NULL) {
- return mAudioPlayer->setPlaybackRatePermille(request.readInt32());
- } else {
- return NO_INIT;
- }
- }
default:
{
return ERROR_UNSUPPORTED;
@@ -2597,6 +2600,58 @@
}
}
+status_t AwesomePlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+ Mutex::Autolock autoLock(mLock);
+ // cursory sanity check for non-audio and paused cases
+ if ((rate.mSpeed != 0.f && rate.mSpeed < AUDIO_TIMESTRETCH_SPEED_MIN)
+ || rate.mSpeed > AUDIO_TIMESTRETCH_SPEED_MAX
+ || rate.mPitch < AUDIO_TIMESTRETCH_SPEED_MIN
+ || rate.mPitch > AUDIO_TIMESTRETCH_SPEED_MAX) {
+ return BAD_VALUE;
+ }
+
+ status_t err = OK;
+ if (rate.mSpeed == 0.f) {
+ if (mFlags & PLAYING) {
+ modifyFlags(CACHE_UNDERRUN, CLEAR); // same as pause
+ err = pause_l();
+ }
+ if (err == OK) {
+ // save settings (using old speed) in case player is resumed
+ AudioPlaybackRate newRate = rate;
+ newRate.mSpeed = mPlaybackSettings.mSpeed;
+ mPlaybackSettings = newRate;
+ }
+ return err;
+ }
+ if (mAudioPlayer != NULL) {
+ err = mAudioPlayer->setPlaybackRate(rate);
+ }
+ if (err == OK) {
+ mPlaybackSettings = rate;
+ if (!(mFlags & PLAYING)) {
+ play_l();
+ }
+ }
+ return err;
+}
+
+status_t AwesomePlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+ if (mAudioPlayer != NULL) {
+ status_t err = mAudioPlayer->getPlaybackRate(rate);
+ if (err == OK) {
+ mPlaybackSettings = *rate;
+ Mutex::Autolock autoLock(mLock);
+ if (!(mFlags & PLAYING)) {
+ rate->mSpeed = 0.f;
+ }
+ }
+ return err;
+ }
+ *rate = mPlaybackSettings;
+ return OK;
+}
+
status_t AwesomePlayer::getTrackInfo(Parcel *reply) const {
Mutex::Autolock autoLock(mLock);
size_t trackCount = mExtractor->countTracks();
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
new file mode 100644
index 0000000..41f0175
--- /dev/null
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CallbackDataSource"
+#include <utils/Log.h>
+
+#include "include/CallbackDataSource.h"
+
+#include <binder/IMemory.h>
+#include <media/IDataSource.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <algorithm>
+
+namespace android {
+
+CallbackDataSource::CallbackDataSource(
+ const sp<IDataSource>& binderDataSource)
+ : mIDataSource(binderDataSource) {
+ // Set up the buffer to read into.
+ mMemory = mIDataSource->getIMemory();
+}
+
+CallbackDataSource::~CallbackDataSource() {
+ ALOGV("~CallbackDataSource");
+ mIDataSource->close();
+}
+
+status_t CallbackDataSource::initCheck() const {
+ if (mMemory == NULL) {
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+ssize_t CallbackDataSource::readAt(off64_t offset, void* data, size_t size) {
+ if (mMemory == NULL) {
+ return -1;
+ }
+
+ // IDataSource can only read up to mMemory->size() bytes at a time, but this
+ // method should be able to read any number of bytes, so read in a loop.
+ size_t totalNumRead = 0;
+ size_t numLeft = size;
+ const size_t bufferSize = mMemory->size();
+
+ while (numLeft > 0) {
+ size_t numToRead = std::min(numLeft, bufferSize);
+ ssize_t numRead =
+ mIDataSource->readAt(offset + totalNumRead, numToRead);
+ // A negative return value represents an error. Pass it on.
+ if (numRead < 0) {
+ return numRead;
+ }
+ // A zero return value signals EOS. Return the bytes read so far.
+ if (numRead == 0) {
+ return totalNumRead;
+ }
+ if ((size_t)numRead > numToRead) {
+ return ERROR_OUT_OF_RANGE;
+ }
+ CHECK(numRead >= 0 && (size_t)numRead <= bufferSize);
+ memcpy(((uint8_t*)data) + totalNumRead, mMemory->pointer(), numRead);
+ numLeft -= numRead;
+ totalNumRead += numRead;
+ }
+
+ return totalNumRead;
+}
+
+status_t CallbackDataSource::getSize(off64_t *size) {
+ status_t err = mIDataSource->getSize(size);
+ if (err != OK) {
+ return err;
+ }
+ if (*size < 0) {
+ // IDataSource will set size to -1 to indicate unknown size, but
+ // DataSource returns ERROR_UNSUPPORTED for that.
+ return ERROR_UNSUPPORTED;
+ }
+ return OK;
+}
+
+TinyCacheSource::TinyCacheSource(const sp<DataSource>& source)
+ : mSource(source), mCachedOffset(0), mCachedSize(0) {
+}
+
+status_t TinyCacheSource::initCheck() const {
+ return mSource->initCheck();
+}
+
+ssize_t TinyCacheSource::readAt(off64_t offset, void* data, size_t size) {
+ if (size >= kCacheSize) {
+ return mSource->readAt(offset, data, size);
+ }
+
+ // Check if the cache satisfies the read.
+ if (offset >= mCachedOffset && offset + size <= mCachedOffset + mCachedSize) {
+ memcpy(data, &mCache[offset - mCachedOffset], size);
+ return size;
+ }
+
+ // Fill the cache and copy to the caller.
+ const ssize_t numRead = mSource->readAt(offset, mCache, kCacheSize);
+ if (numRead <= 0) {
+ return numRead;
+ }
+ if ((size_t)numRead > kCacheSize) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mCachedSize = numRead;
+ mCachedOffset = offset;
+ CHECK(mCachedSize <= kCacheSize && mCachedOffset >= 0);
+ const size_t numToReturn = std::min(size, (size_t)numRead);
+ memcpy(data, mCache, numToReturn);
+
+ return numToReturn;
+}
+
+status_t TinyCacheSource::getSize(off64_t *size) {
+ return mSource->getSize(size);
+}
+
+uint32_t TinyCacheSource::flags() {
+ return mSource->flags();
+}
+
+} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index ad12bdd..1b788f3 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -851,11 +851,11 @@
}
void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
- int32_t msgType, const sp<IMemory> &data) {
- ALOGV("dataCallbackTimestamp: timestamp %" PRId64 " us", timestampUs);
+ int32_t msgType __unused, const sp<IMemory> &data) {
+ ALOGV("dataCallbackTimestamp: timestamp %lld us", (long long)timestampUs);
Mutex::Autolock autoLock(mLock);
if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
- ALOGV("Drop frame at %" PRId64 "/%" PRId64 " us", timestampUs, mStartTimeUs);
+ ALOGV("Drop frame at %lld/%lld us", (long long)timestampUs, (long long)mStartTimeUs);
releaseOneRecordingFrame(data);
return;
}
@@ -913,7 +913,7 @@
mSource->dataCallbackTimestamp(timestamp / 1000, msgType, dataPtr);
}
-void CameraSource::DeathNotifier::binderDied(const wp<IBinder>& who) {
+void CameraSource::DeathNotifier::binderDied(const wp<IBinder>& who __unused) {
ALOGI("Camera recording proxy died");
}
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index f7dcf35..75ef288 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -19,6 +19,7 @@
#include "include/AMRExtractor.h"
#include "include/AACExtractor.h"
+#include "include/CallbackDataSource.h"
#include "include/DRMExtractor.h"
#include "include/FLACExtractor.h"
#include "include/HTTPBase.h"
@@ -281,6 +282,10 @@
}
}
+sp<DataSource> DataSource::CreateFromIDataSource(const sp<IDataSource> &source) {
+ return new TinyCacheSource(new CallbackDataSource(source));
+}
+
String8 DataSource::getMIMEType() const {
return String8("application/octet-stream");
}
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
index 427bf7b..8fbb57c 100644
--- a/media/libstagefright/ESDS.cpp
+++ b/media/libstagefright/ESDS.cpp
@@ -136,6 +136,8 @@
--size;
if (streamDependenceFlag) {
+ if (size < 2)
+ return ERROR_MALFORMED;
offset += 2;
size -= 2;
}
@@ -145,11 +147,15 @@
return ERROR_MALFORMED;
}
unsigned URLlength = mData[offset];
+ if (URLlength >= size)
+ return ERROR_MALFORMED;
offset += URLlength + 1;
size -= URLlength + 1;
}
if (OCRstreamFlag) {
+ if (size < 2)
+ return ERROR_MALFORMED;
offset += 2;
size -= 2;
diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp
index fa7251c..8b972b7 100644
--- a/media/libstagefright/FLACExtractor.cpp
+++ b/media/libstagefright/FLACExtractor.cpp
@@ -651,10 +651,10 @@
if (doSeek) {
// We implement the seek callback, so this works without explicit flush
if (!FLAC__stream_decoder_seek_absolute(mDecoder, sample)) {
- ALOGE("FLACParser::readBuffer seek to sample %llu failed", sample);
+ ALOGE("FLACParser::readBuffer seek to sample %lld failed", (long long)sample);
return NULL;
}
- ALOGV("FLACParser::readBuffer seek to sample %llu succeeded", sample);
+ ALOGV("FLACParser::readBuffer seek to sample %lld succeeded", (long long)sample);
} else {
if (!FLAC__stream_decoder_process_single(mDecoder)) {
ALOGE("FLACParser::readBuffer process_single failed");
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index f0db76b..565f156 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -111,7 +111,7 @@
} else {
off64_t result = lseek64(mFd, offset + mOffset, SEEK_SET);
if (result == -1) {
- ALOGE("seek to %lld failed", offset + mOffset);
+ ALOGE("seek to %lld failed", (long long)(offset + mOffset));
return UNKNOWN_ERROR;
}
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index 77a652a..068a77f 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -34,10 +34,10 @@
: mNumBandwidthHistoryItems(0),
mTotalTransferTimeUs(0),
mTotalTransferBytes(0),
+ mMaxBandwidthHistoryItems(100),
mPrevBandwidthMeasureTimeUs(0),
mPrevEstimatedBandWidthKbps(0),
- mBandWidthCollectFreqMs(5000),
- mMaxBandwidthHistoryItems(100) {
+ mBandWidthCollectFreqMs(5000) {
}
void HTTPBase::addBandwidthMeasurement(
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 4a63152..2e54e8c 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -82,7 +82,7 @@
*inout_pos += len;
ALOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)",
- *inout_pos, *inout_pos);
+ (long long)*inout_pos, (long long)*inout_pos);
}
if (post_id3_pos != NULL) {
@@ -103,9 +103,9 @@
uint8_t *tmp = buf;
do {
- if (pos >= *inout_pos + kMaxBytesChecked) {
+ if (pos >= (off64_t)(*inout_pos + kMaxBytesChecked)) {
// Don't scan forever.
- ALOGV("giving up at offset %lld", pos);
+ ALOGV("giving up at offset %lld", (long long)pos);
break;
}
@@ -155,7 +155,7 @@
continue;
}
- ALOGV("found possible 1st frame at %lld (header = 0x%08x)", pos, header);
+ ALOGV("found possible 1st frame at %lld (header = 0x%08x)", (long long)pos, header);
// We found what looks like a valid frame,
// now find its successors.
@@ -186,7 +186,7 @@
break;
}
- ALOGV("found subsequent frame #%d at %lld", j + 2, test_pos);
+ ALOGV("found subsequent frame #%d at %lld", j + 2, (long long)test_pos);
test_pos += test_frame_size;
}
@@ -282,6 +282,41 @@
mFirstFramePos = pos;
mFixedHeader = header;
+ mMeta = new MetaData;
+ sp<XINGSeeker> seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
+
+ if (seeker == NULL) {
+ mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
+ } else {
+ mSeeker = seeker;
+ int encd = seeker->getEncoderDelay();
+ int encp = seeker->getEncoderPadding();
+ if (encd != 0 || encp != 0) {
+ mMeta->setInt32(kKeyEncoderDelay, encd);
+ mMeta->setInt32(kKeyEncoderPadding, encp);
+ }
+ }
+
+ if (mSeeker != NULL) {
+ // While it is safe to send the XING/VBRI frame to the decoder, this will
+ // result in an extra 1152 samples being output. In addition, the bitrate
+ // of the Xing header might not match the rest of the file, which could
+ // lead to problems when seeking. The real first frame to decode is after
+ // the XING/VBRI frame, so skip there.
+ size_t frame_size;
+ int sample_rate;
+ int num_channels;
+ int bitrate;
+ GetMPEGAudioFrameSize(
+ header, &frame_size, &sample_rate, &num_channels, &bitrate);
+ pos += frame_size;
+ if (!Resync(mDataSource, 0, &pos, &post_id3_pos, &header)) {
+ // mInitCheck will remain NO_INIT
+ return;
+ }
+ mFirstFramePos = pos;
+ mFixedHeader = header;
+ }
size_t frame_size;
int sample_rate;
@@ -292,8 +327,6 @@
unsigned layer = 4 - ((header >> 17) & 3);
- mMeta = new MetaData;
-
switch (layer) {
case 1:
mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
@@ -312,27 +345,6 @@
mMeta->setInt32(kKeyBitRate, bitrate * 1000);
mMeta->setInt32(kKeyChannelCount, num_channels);
- sp<XINGSeeker> seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
-
- if (seeker == NULL) {
- mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
- } else {
- mSeeker = seeker;
- int encd = seeker->getEncoderDelay();
- int encp = seeker->getEncoderPadding();
- if (encd != 0 || encp != 0) {
- mMeta->setInt32(kKeyEncoderDelay, encd);
- mMeta->setInt32(kKeyEncoderPadding, encp);
- }
- }
-
- if (mSeeker != NULL) {
- // While it is safe to send the XING/VBRI frame to the decoder, this will
- // result in an extra 1152 samples being output. The real first frame to
- // decode is after the XING/VBRI frame, so skip there.
- mFirstFramePos += frame_size;
- }
-
int64_t durationUs;
if (mSeeker == NULL || !mSeeker->getDuration(&durationUs)) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index d0f42cc..65d8a04 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -33,6 +33,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
@@ -221,8 +222,7 @@
ssize_t MPEG4DataSource::readAt(off64_t offset, void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
- if (offset >= mCachedOffset
- && offset + size <= mCachedOffset + mCachedSize) {
+ if (isInRange(mCachedOffset, mCachedSize, offset, size)) {
memcpy(data, &mCache[offset - mCachedOffset], size);
return size;
}
@@ -503,7 +503,7 @@
} else if (offset <= orig_offset) {
// only continue parsing if the offset was advanced,
// otherwise we might end up in an infinite loop
- ALOGE("did not advance: 0x%lld->0x%lld", orig_offset, offset);
+ ALOGE("did not advance: %lld->%lld", (long long)orig_offset, (long long)offset);
err = ERROR_MALFORMED;
break;
} else if (err == UNKNOWN_ERROR) {
@@ -739,6 +739,17 @@
&& path[3] == FOURCC('i', 'l', 's', 't');
}
+static bool underQTMetaPath(const Vector<uint32_t> &path, int32_t depth) {
+ return path.size() >= 2
+ && path[0] == FOURCC('m', 'o', 'o', 'v')
+ && path[1] == FOURCC('m', 'e', 't', 'a')
+ && (depth == 2
+ || (depth == 3
+ && (path[2] == FOURCC('h', 'd', 'l', 'r')
+ || path[2] == FOURCC('i', 'l', 's', 't')
+ || path[2] == FOURCC('k', 'e', 'y', 's'))));
+}
+
// Given a time in seconds since Jan 1 1904, produce a human-readable string.
static void convertTimeToDate(int64_t time_1904, String8 *s) {
time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600);
@@ -750,7 +761,7 @@
}
status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
- ALOGV("entering parseChunk %lld/%d", *offset, depth);
+ ALOGV("entering parseChunk %lld/%d", (long long)*offset, depth);
uint32_t hdr[2];
if (mDataSource->readAt(*offset, hdr, 8) < 8) {
return ERROR_IO;
@@ -794,7 +805,7 @@
char chunk[5];
MakeFourCCString(chunk_type, chunk);
- ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth);
+ ALOGV("chunk: %s @ %lld, %d", chunk, (long long)*offset, depth);
if (kUseHexDump) {
static const char kWhitespace[] = " ";
@@ -874,6 +885,9 @@
}
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->sampleTable = new SampleTable(mDataSource);
}
@@ -1028,6 +1042,10 @@
}
original_fourcc = ntohl(original_fourcc);
ALOGV("read original format: %d", original_fourcc);
+
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
uint32_t num_channels = 0;
uint32_t sample_rate = 0;
@@ -1083,6 +1101,9 @@
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
@@ -1168,6 +1189,11 @@
return ERROR_IO;
}
+ if (!timescale) {
+ ALOGE("timescale should not be ZERO.");
+ return ERROR_MALFORMED;
+ }
+
mLastTrack->timescale = ntohl(timescale);
// 14496-12 says all ones means indeterminate, but some files seem to use
@@ -1193,7 +1219,7 @@
duration = ntohl(duration32);
}
}
- if (duration != 0) {
+ if (duration != 0 && mLastTrack->timescale != 0) {
mLastTrack->meta->setInt64(
kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
}
@@ -1257,6 +1283,10 @@
// display the timed text.
// For encrypted files, there may also be more than one entry.
const char *mime;
+
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
strcasecmp(mime, "application/octet-stream")) {
@@ -1303,6 +1333,9 @@
uint16_t sample_size = U16_AT(&buffer[18]);
uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
// if the chunk type is enca, we'll get the type from the sinf/frma box later
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1364,6 +1397,9 @@
// printf("*** coding='%s' width=%d height=%d\n",
// chunk, width, height);
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
// if the chunk type is encv, we'll get the type from the sinf/frma box later
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1389,6 +1425,9 @@
case FOURCC('s', 't', 'c', 'o'):
case FOURCC('c', 'o', '6', '4'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
status_t err =
mLastTrack->sampleTable->setChunkOffsetParams(
chunk_type, data_offset, chunk_data_size);
@@ -1404,6 +1443,9 @@
case FOURCC('s', 't', 's', 'c'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
status_t err =
mLastTrack->sampleTable->setSampleToChunkParams(
data_offset, chunk_data_size);
@@ -1420,6 +1462,9 @@
case FOURCC('s', 't', 's', 'z'):
case FOURCC('s', 't', 'z', '2'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
status_t err =
mLastTrack->sampleTable->setSampleSizeParams(
chunk_type, data_offset, chunk_data_size);
@@ -1489,6 +1534,9 @@
case FOURCC('s', 't', 't', 's'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
*offset += chunk_size;
status_t err =
@@ -1504,6 +1552,9 @@
case FOURCC('c', 't', 't', 's'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
*offset += chunk_size;
status_t err =
@@ -1519,6 +1570,9 @@
case FOURCC('s', 't', 's', 's'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
*offset += chunk_size;
status_t err =
@@ -1591,6 +1645,9 @@
return ERROR_MALFORMED;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(
kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
@@ -1623,6 +1680,9 @@
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(
kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
@@ -1637,6 +1697,9 @@
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(
kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size);
@@ -1661,7 +1724,7 @@
char buffer[23];
if (chunk_data_size != 7 &&
chunk_data_size != 23) {
- ALOGE("Incorrect D263 box size %lld", chunk_data_size);
+ ALOGE("Incorrect D263 box size %lld", (long long)chunk_data_size);
return ERROR_MALFORMED;
}
@@ -1670,6 +1733,9 @@
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
break;
@@ -1677,31 +1743,35 @@
case FOURCC('m', 'e', 't', 'a'):
{
- uint8_t buffer[4];
- if (chunk_data_size < (off64_t)sizeof(buffer)) {
- *offset += chunk_size;
- return ERROR_MALFORMED;
- }
-
- if (mDataSource->readAt(
- data_offset, buffer, 4) < 4) {
- *offset += chunk_size;
- return ERROR_IO;
- }
-
- if (U32_AT(buffer) != 0) {
- // Should be version 0, flags 0.
-
- // If it's not, let's assume this is one of those
- // apparently malformed chunks that don't have flags
- // and completely different semantics than what's
- // in the MPEG4 specs and skip it.
- *offset += chunk_size;
- return OK;
- }
-
off64_t stop_offset = *offset + chunk_size;
- *offset = data_offset + sizeof(buffer);
+ *offset = data_offset;
+ bool isParsingMetaKeys = underQTMetaPath(mPath, 2);
+ if (!isParsingMetaKeys) {
+ uint8_t buffer[4];
+ if (chunk_data_size < (off64_t)sizeof(buffer)) {
+ *offset += chunk_size;
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, 4) < 4) {
+ *offset += chunk_size;
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+
+ // If it's not, let's assume this is one of those
+ // apparently malformed chunks that don't have flags
+ // and completely different semantics than what's
+ // in the MPEG4 specs and skip it.
+ *offset += chunk_size;
+ return OK;
+ }
+ *offset += sizeof(buffer);
+ }
+
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
@@ -1767,7 +1837,7 @@
}
duration = d32;
}
- if (duration != 0) {
+ if (duration != 0 && mHeaderTimescale != 0) {
mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
}
@@ -1816,7 +1886,7 @@
return ERROR_MALFORMED;
}
- if (duration != 0) {
+ if (duration != 0 && mHeaderTimescale != 0) {
mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
}
@@ -1845,12 +1915,19 @@
{
*offset += chunk_size;
+ if (underQTMetaPath(mPath, 3)) {
+ break;
+ }
+
uint32_t buffer;
if (mDataSource->readAt(
data_offset + 8, &buffer, 4) < 4) {
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
uint32_t type = ntohl(buffer);
// For the 3GPP file format, the handler-type within the 'hdlr' box
// shall be 'text'. We also want to support 'sbtl' handler type
@@ -1862,6 +1939,16 @@
break;
}
+ case FOURCC('k', 'e', 'y', 's'):
+ {
+ *offset += chunk_size;
+
+ if (underQTMetaPath(mPath, 3)) {
+ parseQTMetaKey(data_offset, chunk_data_size);
+ }
+ break;
+ }
+
case FOURCC('t', 'r', 'e', 'x'):
{
*offset += chunk_size;
@@ -1883,6 +1970,9 @@
case FOURCC('t', 'x', '3', 'g'):
{
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
uint32_t type;
const void *data;
size_t size = 0;
@@ -1891,6 +1981,10 @@
size = 0;
}
+ if (SIZE_MAX - chunk_size <= size) {
+ return ERROR_MALFORMED;
+ }
+
uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
if (buffer == NULL) {
return ERROR_MALFORMED;
@@ -1924,14 +2018,22 @@
*offset += chunk_size;
if (mFileMetaData != NULL) {
- ALOGV("chunk_data_size = %lld and data_offset = %lld",
- chunk_data_size, data_offset);
+ ALOGV("chunk_data_size = %" PRId64 " and data_offset = %" PRId64,
+ chunk_data_size, data_offset);
+
+ if (chunk_data_size < 0 || static_cast<uint64_t>(chunk_data_size) >= SIZE_MAX - 1) {
+ return ERROR_MALFORMED;
+ }
sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
if (mDataSource->readAt(
data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
return ERROR_IO;
}
const int kSkipBytesOfDataBox = 16;
+ if (chunk_data_size <= kSkipBytesOfDataBox) {
+ return ERROR_MALFORMED;
+ }
+
mFileMetaData->setData(
kKeyAlbumArt, MetaData::TYPE_NONE,
buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
@@ -1989,6 +2091,12 @@
default:
{
+ // check if we're parsing 'ilst' for meta keys
+ // if so, treat type as a number (key-id).
+ if (underQTMetaPath(mPath, 3)) {
+ parseQTMetaVal(chunk_type, data_offset, chunk_data_size);
+ }
+
*offset += chunk_size;
break;
}
@@ -2024,6 +2132,8 @@
return ERROR_MALFORMED;
}
ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale);
+ if (timeScale == 0)
+ return ERROR_MALFORMED;
uint64_t earliestPresentationTime;
uint64_t firstOffset;
@@ -2107,6 +2217,9 @@
uint64_t sidxDuration = total_duration * 1000000 / timeScale;
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
int64_t metaDuration;
if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
mLastTrack->meta->setInt64(kKeyDuration, sidxDuration);
@@ -2114,7 +2227,108 @@
return OK;
}
+status_t MPEG4Extractor::parseQTMetaKey(off64_t offset, size_t size) {
+ if (size < 8) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t count;
+ if (!mDataSource->getUInt32(offset + 4, &count)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mMetaKeyMap.size() > 0) {
+ ALOGW("'keys' atom seen again, discarding existing entries");
+ mMetaKeyMap.clear();
+ }
+
+ off64_t keyOffset = offset + 8;
+ off64_t stopOffset = offset + size;
+ for (size_t i = 1; i <= count; i++) {
+ if (keyOffset + 8 > stopOffset) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t keySize;
+ if (!mDataSource->getUInt32(keyOffset, &keySize)
+ || keySize < 8
+ || keyOffset + keySize > stopOffset) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t type;
+ if (!mDataSource->getUInt32(keyOffset + 4, &type)
+ || type != FOURCC('m', 'd', 't', 'a')) {
+ return ERROR_MALFORMED;
+ }
+
+ keySize -= 8;
+ keyOffset += 8;
+
+ sp<ABuffer> keyData = new ABuffer(keySize);
+ if (keyData->data() == NULL) {
+ return ERROR_MALFORMED;
+ }
+ if (mDataSource->readAt(
+ keyOffset, keyData->data(), keySize) < (ssize_t) keySize) {
+ return ERROR_MALFORMED;
+ }
+
+ AString key((const char *)keyData->data(), keySize);
+ mMetaKeyMap.add(i, key);
+
+ keyOffset += keySize;
+ }
+ return OK;
+}
+
+status_t MPEG4Extractor::parseQTMetaVal(
+ int32_t keyId, off64_t offset, size_t size) {
+ ssize_t index = mMetaKeyMap.indexOfKey(keyId);
+ if (index < 0) {
+ // corresponding key is not present, ignore
+ return ERROR_MALFORMED;
+ }
+
+ if (size <= 16) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t dataSize;
+ if (!mDataSource->getUInt32(offset, &dataSize)
+ || dataSize > size || dataSize <= 16) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t atomFourCC;
+ if (!mDataSource->getUInt32(offset + 4, &atomFourCC)
+ || atomFourCC != FOURCC('d', 'a', 't', 'a')) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t dataType;
+ if (!mDataSource->getUInt32(offset + 8, &dataType)
+ || ((dataType & 0xff000000) != 0)) {
+ // not well-known type
+ return ERROR_MALFORMED;
+ }
+
+ dataSize -= 16;
+ offset += 16;
+
+ if (dataType == 23 && dataSize >= 4) {
+ // BE Float32
+ uint32_t val;
+ if (!mDataSource->getUInt32(offset, &val)) {
+ return ERROR_MALFORMED;
+ }
+ if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) {
+ mFileMetaData->setFloat(kKeyCaptureFramerate, *(float *)&val);
+ }
+ } else {
+ // add more keys if needed
+ ALOGV("ignoring key: type %d, size %d", dataType, dataSize);
+ }
+
+ return OK;
+}
status_t MPEG4Extractor::parseTrackHeader(
off64_t data_offset, off64_t data_size) {
@@ -2157,6 +2371,9 @@
return ERROR_UNSUPPORTED;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setInt32(kKeyTrackID, id);
size_t matrixOffset = dynSize + 16;
@@ -2228,7 +2445,7 @@
uint32_t metadataKey = 0;
char chunk[5];
MakeFourCCString(mPath[4], chunk);
- ALOGV("meta: %s @ %lld", chunk, offset);
+ ALOGV("meta: %s @ %lld", chunk, (long long)offset);
switch ((int32_t)mPath[4]) {
case FOURCC(0xa9, 'a', 'l', 'b'):
{
@@ -2339,6 +2556,9 @@
int32_t delay, padding;
if (sscanf(mLastCommentData,
" %*x %x %x %*x", &delay, &padding) == 2) {
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
}
@@ -2397,11 +2617,11 @@
}
status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) {
- if (size < 4) {
+ if (size < 4 || size == SIZE_MAX) {
return ERROR_MALFORMED;
}
- uint8_t *buffer = new (std::nothrow) uint8_t[size];
+ uint8_t *buffer = new (std::nothrow) uint8_t[size + 1];
if (buffer == NULL) {
return ERROR_MALFORMED;
}
@@ -2470,6 +2690,10 @@
int len16 = 0; // Number of UTF-16 characters
// smallest possible valid UTF-16 string w BOM: 0xfe 0xff 0x00 0x00
+ if (size < 6) {
+ return ERROR_MALFORMED;
+ }
+
if (size - 6 >= 4) {
len16 = ((size - 6) / 2) - 1; // don't include 0x0000 terminator
framedata = (char16_t *)(buffer + 6);
@@ -2493,6 +2717,7 @@
}
if (isUTF8) {
+ buffer[size] = 0;
mFileMetaData->setCString(metadataKey, (const char *)buffer + 6);
} else {
// Convert from UTF-16 string to UTF-8 string.
@@ -2635,6 +2860,11 @@
return ERROR_MALFORMED;
}
+ if (track->timescale == 0) {
+ ALOGE("timescale invalid.");
+ return ERROR_MALFORMED;
+ }
+
return OK;
}
@@ -2701,6 +2931,9 @@
if (objectTypeIndication == 0xe1) {
// This isn't MPEG4 audio at all, it's QCELP 14k...
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
return OK;
}
@@ -2721,7 +2954,7 @@
}
if (kUseHexDump) {
- printf("ESD of size %d\n", csd_size);
+ printf("ESD of size %zu\n", csd_size);
hexdump(csd, csd_size);
}
@@ -2749,6 +2982,9 @@
objectType = 32 + br.getBits(6);
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
//keep AOT type
mLastTrack->meta->setInt32(kKeyAACAOT, objectType);
@@ -2919,6 +3155,9 @@
return ERROR_UNSUPPORTED;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
int32_t prevSampleRate;
CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
@@ -3122,7 +3361,7 @@
char chunk[5];
MakeFourCCString(chunk_type, chunk);
- ALOGV("MPEG4Source chunk %s @ %llx", chunk, *offset);
+ ALOGV("MPEG4Source chunk %s @ %#llx", chunk, (long long)*offset);
off64_t chunk_data_size = *offset + chunk_size - data_offset;
@@ -3579,7 +3818,7 @@
sampleCtsOffset = 0;
}
- if (size < (off64_t)sampleCount * bytesPerSample) {
+ if (size < (off64_t)(sampleCount * bytesPerSample)) {
return -EINVAL;
}
@@ -3881,12 +4120,12 @@
size_t dstOffset = 0;
while (srcOffset < size) {
- bool isMalFormed = (srcOffset + mNALLengthSize > size);
+ bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
size_t nalLength = 0;
if (!isMalFormed) {
nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
srcOffset += mNALLengthSize;
- isMalFormed = srcOffset + nalLength > size;
+ isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength);
}
if (isMalFormed) {
@@ -4158,12 +4397,12 @@
size_t dstOffset = 0;
while (srcOffset < size) {
- bool isMalFormed = (srcOffset + mNALLengthSize > size);
+ bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
size_t nalLength = 0;
if (!isMalFormed) {
nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
srcOffset += mNALLengthSize;
- isMalFormed = srcOffset + nalLength > size;
+ isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength);
}
if (isMalFormed) {
@@ -4331,7 +4570,7 @@
char chunkstring[5];
MakeFourCCString(chunkType, chunkstring);
- ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, offset);
+ ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, (long long)offset);
switch (chunkType) {
case FOURCC('f', 't', 'y', 'p'):
{
@@ -4394,7 +4633,7 @@
*meta = new AMessage;
(*meta)->setInt64("meta-data-size", moovAtomEndOffset);
- ALOGV("found metadata size: %lld", moovAtomEndOffset);
+ ALOGV("found metadata size: %lld", (long long)moovAtomEndOffset);
}
return true;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 6f6e362..3bc22f2 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -63,9 +63,11 @@
static const uint8_t kNalUnitTypePicParamSet = 0x08;
static const int64_t kInitialDelayTimeUs = 700000LL;
-static const char kMetaKey_Model[] = "com.android.model";
static const char kMetaKey_Version[] = "com.android.version";
+#ifdef SHOW_MODEL_BUILD
+static const char kMetaKey_Model[] = "com.android.model";
static const char kMetaKey_Build[] = "com.android.build";
+#endif
static const char kMetaKey_CaptureFps[] = "com.android.capture.fps";
/* uncomment to include model and build in meta */
@@ -92,6 +94,7 @@
void addChunkOffset(off64_t offset);
int32_t getTrackId() const { return mTrackId; }
status_t dump(int fd, const Vector<String16>& args) const;
+ static const char *getFourCCForMime(const char *mime);
private:
enum {
@@ -372,8 +375,8 @@
mLatitudex10000(0),
mLongitudex10000(0),
mAreGeoTagsAvailable(false),
- mMetaKeys(new AMessage()),
- mStartTimeOffsetMs(-1) {
+ mStartTimeOffsetMs(-1),
+ mMetaKeys(new AMessage()) {
addDeviceMeta();
}
@@ -424,6 +427,33 @@
return OK;
}
+// static
+const char *MPEG4Writer::Track::getFourCCForMime(const char *mime) {
+ if (mime == NULL) {
+ return NULL;
+ }
+ if (!strncasecmp(mime, "audio/", 6)) {
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
+ return "samr";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+ return "sawb";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ return "mp4a";
+ }
+ } else if (!strncasecmp(mime, "video/", 6)) {
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ return "mp4v";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ return "s263";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ return "avc1";
+ }
+ } else {
+ ALOGE("Track (%s) other than video or audio is not supported", mime);
+ }
+ return NULL;
+}
+
status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
Mutex::Autolock l(mLock);
if (mStarted) {
@@ -439,14 +469,11 @@
CHECK(source.get() != NULL);
- // A track of type other than video or audio is not supported.
const char *mime;
source->getFormat()->findCString(kKeyMIMEType, &mime);
bool isAudio = !strncasecmp(mime, "audio/", 6);
- bool isVideo = !strncasecmp(mime, "video/", 6);
- if (!isAudio && !isVideo) {
- ALOGE("Track (%s) other than video or audio is not supported",
- mime);
+ if (Track::getFourCCForMime(mime) == NULL) {
+ ALOGE("Unsupported mime '%s'", mime);
return ERROR_UNSUPPORTED;
}
@@ -841,6 +868,8 @@
mFd = -1;
mInitCheck = NO_INIT;
mStarted = false;
+ free(mMoovBoxBuffer);
+ mMoovBoxBuffer = NULL;
}
status_t MPEG4Writer::reset() {
@@ -2726,17 +2755,13 @@
const char *mime;
bool success = mMeta->findCString(kKeyMIMEType, &mime);
CHECK(success);
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- mOwner->beginBox("mp4v");
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- mOwner->beginBox("s263");
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- mOwner->beginBox("avc1");
- } else {
+ const char *fourcc = getFourCCForMime(mime);
+ if (fourcc == NULL) {
ALOGE("Unknown mime type '%s'.", mime);
CHECK(!"should not be here, unknown mime type.");
}
+ mOwner->beginBox(fourcc); // video format
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(0); // reserved
mOwner->writeInt16(1); // data ref index
@@ -2780,14 +2805,8 @@
const char *mime;
bool success = mMeta->findCString(kKeyMIMEType, &mime);
CHECK(success);
- const char *fourcc = NULL;
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
- fourcc = "samr";
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
- fourcc = "sawb";
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
- fourcc = "mp4a";
- } else {
+ const char *fourcc = getFourCCForMime(mime);
+ if (fourcc == NULL) {
ALOGE("Unknown mime type '%s'.", mime);
CHECK(!"should not be here, unknown mime type.");
}
@@ -2839,8 +2858,10 @@
mOwner->writeInt16(0x03); // XXX
mOwner->writeInt8(0x00); // buffer size 24-bit
- mOwner->writeInt32(96000); // max bit rate
- mOwner->writeInt32(96000); // avg bit rate
+ int32_t bitRate;
+ bool success = mMeta->findInt32(kKeyBitRate, &bitRate);
+ mOwner->writeInt32(success ? bitRate : 96000); // max bit rate
+ mOwner->writeInt32(success ? bitRate : 96000); // avg bit rate
mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
mOwner->writeInt8(mCodecSpecificDataSize);
diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
index 433f555..2641e4e 100644
--- a/media/libstagefright/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -92,6 +92,11 @@
mPlaybackRate = rate;
}
+float MediaClock::getPlaybackRate() const {
+ Mutex::Autolock autoLock(mLock);
+ return mPlaybackRate;
+}
+
status_t MediaClock::getMediaTime(
int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
if (outMediaUs == NULL) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 0597f1d..46c154d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -22,9 +22,14 @@
#include "include/SoftwareRenderer.h"
#include <binder/IBatteryStats.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
#include <gui/Surface.h>
#include <media/ICrypto.h>
+#include <media/IOMX.h>
+#include <media/IResourceManagerService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -38,25 +43,97 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaFilter.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/NativeWindowWrapper.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/SurfaceUtils.h>
#include <private/android_filesystem_config.h>
#include <utils/Log.h>
#include <utils/Singleton.h>
namespace android {
+static inline int getCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+static int64_t getId(sp<IResourceManagerClient> client) {
+ return (int64_t) client.get();
+}
+
+static bool isResourceError(status_t err) {
+ return (err == NO_MEMORY);
+}
+
+static const int kMaxRetry = 2;
+
+struct ResourceManagerClient : public BnResourceManagerClient {
+ ResourceManagerClient(MediaCodec* codec) : mMediaCodec(codec) {}
+
+ virtual bool reclaimResource() {
+ sp<MediaCodec> codec = mMediaCodec.promote();
+ if (codec == NULL) {
+ // codec is already gone.
+ return true;
+ }
+ status_t err = codec->reclaim();
+ if (err != OK) {
+ ALOGW("ResourceManagerClient failed to release codec with err %d", err);
+ }
+ return (err == OK);
+ }
+
+ virtual String8 getName() {
+ String8 ret;
+ sp<MediaCodec> codec = mMediaCodec.promote();
+ if (codec == NULL) {
+ // codec is already gone.
+ return ret;
+ }
+
+ AString name;
+ if (codec->getName(&name) == OK) {
+ ret.setTo(name.c_str());
+ }
+ return ret;
+ }
+
+protected:
+ virtual ~ResourceManagerClient() {}
+
+private:
+ wp<MediaCodec> mMediaCodec;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ResourceManagerClient);
+};
+
struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> {
BatteryNotifier();
+ virtual ~BatteryNotifier();
void noteStartVideo();
void noteStopVideo();
void noteStartAudio();
void noteStopAudio();
+ void onBatteryStatServiceDied();
private:
+ struct DeathNotifier : public IBinder::DeathRecipient {
+ DeathNotifier() {}
+ virtual void binderDied(const wp<IBinder>& /*who*/) {
+ BatteryNotifier::getInstance().onBatteryStatServiceDied();
+ }
+ };
+
+ Mutex mLock;
int32_t mVideoRefCount;
int32_t mAudioRefCount;
sp<IBatteryStats> mBatteryStatService;
+ sp<DeathNotifier> mDeathNotifier;
+
+ sp<IBatteryStats> getBatteryService_l();
+
+ DISALLOW_EVIL_CONSTRUCTORS(BatteryNotifier);
};
ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier)
@@ -64,54 +141,155 @@
MediaCodec::BatteryNotifier::BatteryNotifier() :
mVideoRefCount(0),
mAudioRefCount(0) {
- // get battery service
+}
+
+sp<IBatteryStats> MediaCodec::BatteryNotifier::getBatteryService_l() {
+ if (mBatteryStatService != NULL) {
+ return mBatteryStatService;
+ }
+ // get battery service from service manager
const sp<IServiceManager> sm(defaultServiceManager());
if (sm != NULL) {
const String16 name("batterystats");
- mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
+ mBatteryStatService =
+ interface_cast<IBatteryStats>(sm->getService(name));
if (mBatteryStatService == NULL) {
ALOGE("batterystats service unavailable!");
+ return NULL;
}
+ mDeathNotifier = new DeathNotifier();
+ IInterface::asBinder(mBatteryStatService)->linkToDeath(mDeathNotifier);
+ // notify start now if media already started
+ if (mVideoRefCount > 0) {
+ mBatteryStatService->noteStartVideo(AID_MEDIA);
+ }
+ if (mAudioRefCount > 0) {
+ mBatteryStatService->noteStartAudio(AID_MEDIA);
+ }
+ }
+ return mBatteryStatService;
+}
+
+MediaCodec::BatteryNotifier::~BatteryNotifier() {
+ if (mDeathNotifier != NULL) {
+ IInterface::asBinder(mBatteryStatService)->
+ unlinkToDeath(mDeathNotifier);
}
}
void MediaCodec::BatteryNotifier::noteStartVideo() {
- if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStartVideo(AID_MEDIA);
+ Mutex::Autolock _l(mLock);
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+ if (mVideoRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStartVideo(AID_MEDIA);
}
mVideoRefCount++;
}
void MediaCodec::BatteryNotifier::noteStopVideo() {
+ Mutex::Autolock _l(mLock);
if (mVideoRefCount == 0) {
ALOGW("BatteryNotifier::noteStop(): video refcount is broken!");
return;
}
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+
mVideoRefCount--;
- if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStopVideo(AID_MEDIA);
+ if (mVideoRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStopVideo(AID_MEDIA);
}
}
void MediaCodec::BatteryNotifier::noteStartAudio() {
- if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStartAudio(AID_MEDIA);
+ Mutex::Autolock _l(mLock);
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+ if (mAudioRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStartAudio(AID_MEDIA);
}
mAudioRefCount++;
}
void MediaCodec::BatteryNotifier::noteStopAudio() {
+ Mutex::Autolock _l(mLock);
if (mAudioRefCount == 0) {
ALOGW("BatteryNotifier::noteStop(): audio refcount is broken!");
return;
}
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+
mAudioRefCount--;
- if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStopAudio(AID_MEDIA);
+ if (mAudioRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStopAudio(AID_MEDIA);
}
}
+
+void MediaCodec::BatteryNotifier::onBatteryStatServiceDied() {
+ Mutex::Autolock _l(mLock);
+ mBatteryStatService.clear();
+ mDeathNotifier.clear();
+ // Do not reset mVideoRefCount and mAudioRefCount here. The ref
+ // counting is independent of the battery service availability.
+ // We need this if battery service becomes available after media
+ // started.
+}
+
+MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy() {
+}
+
+MediaCodec::ResourceManagerServiceProxy::~ResourceManagerServiceProxy() {
+ if (mService != NULL) {
+ IInterface::asBinder(mService)->unlinkToDeath(this);
+ }
+}
+
+void MediaCodec::ResourceManagerServiceProxy::init() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
+ mService = interface_cast<IResourceManagerService>(binder);
+ if (mService == NULL) {
+ ALOGE("Failed to get ResourceManagerService");
+ return;
+ }
+ IInterface::asBinder(mService)->linkToDeath(this);
+}
+
+void MediaCodec::ResourceManagerServiceProxy::binderDied(const wp<IBinder>& /*who*/) {
+ ALOGW("ResourceManagerService died.");
+ Mutex::Autolock _l(mLock);
+ mService.clear();
+}
+
+void MediaCodec::ResourceManagerServiceProxy::addResource(
+ int pid,
+ int64_t clientId,
+ const sp<IResourceManagerClient> client,
+ const Vector<MediaResource> &resources) {
+ Mutex::Autolock _l(mLock);
+ if (mService == NULL) {
+ return;
+ }
+ mService->addResource(pid, clientId, client, resources);
+}
+
+void MediaCodec::ResourceManagerServiceProxy::removeResource(int64_t clientId) {
+ Mutex::Autolock _l(mLock);
+ if (mService == NULL) {
+ return;
+ }
+ mService->removeResource(clientId);
+}
+
+bool MediaCodec::ResourceManagerServiceProxy::reclaimResource(
+ int callingPid, const Vector<MediaResource> &resources) {
+ Mutex::Autolock _l(mLock);
+ if (mService == NULL) {
+ return false;
+ }
+ return mService->reclaimResource(callingPid, resources);
+}
+
// static
sp<MediaCodec> MediaCodec::CreateByType(
const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) {
@@ -136,25 +314,52 @@
return ret == OK ? codec : NULL; // NULL deallocates codec.
}
+// static
+sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
+ OMXClient client;
+ CHECK_EQ(client.connect(), (status_t)OK);
+ sp<IOMX> omx = client.interface();
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ sp<IGraphicBufferConsumer> bufferConsumer;
+
+ status_t err = omx->createPersistentInputSurface(
+ &bufferProducer, &bufferConsumer);
+
+ if (err != OK) {
+ ALOGE("Failed to create persistent input surface.");
+ return NULL;
+ }
+
+ return new PersistentSurface(bufferProducer, bufferConsumer);
+}
+
MediaCodec::MediaCodec(const sp<ALooper> &looper)
: mState(UNINITIALIZED),
+ mReleasedByResourceManager(false),
mLooper(looper),
mCodec(NULL),
mReplyID(0),
mFlags(0),
mStickyError(OK),
mSoftRenderer(NULL),
+ mResourceManagerClient(new ResourceManagerClient(this)),
+ mResourceManagerService(new ResourceManagerServiceProxy()),
mBatteryStatNotified(false),
mIsVideo(false),
+ mVideoWidth(0),
+ mVideoHeight(0),
mDequeueInputTimeoutGeneration(0),
mDequeueInputReplyID(0),
mDequeueOutputTimeoutGeneration(0),
mDequeueOutputReplyID(0),
- mHaveInputSurface(false) {
+ mHaveInputSurface(false),
+ mHavePendingInputBuffers(false) {
}
MediaCodec::~MediaCodec() {
CHECK_EQ(mState, UNINITIALIZED);
+ mResourceManagerService->removeResource(getId(mResourceManagerClient));
}
// static
@@ -173,14 +378,21 @@
return err;
}
-// static
void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) {
+ int32_t finalErr = err;
+ if (mReleasedByResourceManager) {
+ // override the err code if MediaCodec has been released by ResourceManager.
+ finalErr = DEAD_OBJECT;
+ }
+
sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
+ response->setInt32("err", finalErr);
response->postReply(replyID);
}
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
+ mResourceManagerService->init();
+
// save init parameters for reset
mInitName = name;
mInitNameIsType = nameIsType;
@@ -200,15 +412,20 @@
return NAME_NOT_FOUND;
}
- bool needDedicatedLooper = false;
+ bool secureCodec = false;
if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) {
- needDedicatedLooper = true;
+ mIsVideo = true;
} else {
AString tmp = name;
if (tmp.endsWith(".secure")) {
+ secureCodec = true;
tmp.erase(tmp.size() - 7, 7);
}
const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+ if (mcl == NULL) {
+ mCodec = NULL; // remove the codec.
+ return NO_INIT; // if called from Java should raise IOException
+ }
ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
if (codecIdx >= 0) {
const sp<MediaCodecInfo> info = mcl->getCodecInfo(codecIdx);
@@ -216,14 +433,15 @@
info->getSupportedMimes(&mimes);
for (size_t i = 0; i < mimes.size(); i++) {
if (mimes[i].startsWith("video/")) {
- needDedicatedLooper = true;
+ mIsVideo = true;
break;
}
}
}
}
- if (needDedicatedLooper) {
+ if (mIsVideo) {
+ // video codec needs dedicated looper
if (mCodecLooper == NULL) {
mCodecLooper = new ALooper;
mCodecLooper->setName("CodecLooper");
@@ -247,8 +465,26 @@
msg->setInt32("encoder", encoder);
}
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
+ status_t err;
+ Vector<MediaResource> resources;
+ const char *type = secureCodec ? kResourceSecureCodec : kResourceNonSecureCodec;
+ const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+ resources.push_back(MediaResource(String8(type), String8(subtype), 1));
+ for (int i = 0; i <= kMaxRetry; ++i) {
+ if (i > 0) {
+ // Don't try to reclaim resource for the first time.
+ if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) {
+ break;
+ }
+ }
+
+ sp<AMessage> response;
+ err = PostAndAwaitResponse(msg, &response);
+ if (!isResourceError(err)) {
+ break;
+ }
+ }
+ return err;
}
status_t MediaCodec::setCallback(const sp<AMessage> &callback) {
@@ -261,41 +497,80 @@
status_t MediaCodec::configure(
const sp<AMessage> &format,
- const sp<Surface> &nativeWindow,
+ const sp<Surface> &surface,
const sp<ICrypto> &crypto,
uint32_t flags) {
sp<AMessage> msg = new AMessage(kWhatConfigure, this);
+ if (mIsVideo) {
+ format->findInt32("width", &mVideoWidth);
+ format->findInt32("height", &mVideoHeight);
+ }
+
msg->setMessage("format", format);
msg->setInt32("flags", flags);
-
- if (nativeWindow != NULL) {
- msg->setObject(
- "native-window",
- new NativeWindowWrapper(nativeWindow));
- }
+ msg->setObject("surface", surface);
if (crypto != NULL) {
msg->setPointer("crypto", crypto.get());
}
- sp<AMessage> response;
- status_t err = PostAndAwaitResponse(msg, &response);
+ // save msg for reset
+ mConfigureMsg = msg;
- if (err != OK && err != INVALID_OPERATION) {
- // MediaCodec now set state to UNINITIALIZED upon any fatal error.
- // To maintain backward-compatibility, do a reset() to put codec
- // back into INITIALIZED state.
- // But don't reset if the err is INVALID_OPERATION, which means
- // the configure failure is due to wrong state.
+ status_t err;
+ Vector<MediaResource> resources;
+ const char *type = (mFlags & kFlagIsSecure) ?
+ kResourceSecureCodec : kResourceNonSecureCodec;
+ const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+ resources.push_back(MediaResource(String8(type), String8(subtype), 1));
+ // Don't know the buffer size at this point, but it's fine to use 1 because
+ // the reclaimResource call doesn't consider the requester's buffer size for now.
+ resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1));
+ for (int i = 0; i <= kMaxRetry; ++i) {
+ if (i > 0) {
+ // Don't try to reclaim resource for the first time.
+ if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) {
+ break;
+ }
+ }
- ALOGE("configure failed with err 0x%08x, resetting...", err);
- reset();
+ sp<AMessage> response;
+ err = PostAndAwaitResponse(msg, &response);
+ if (err != OK && err != INVALID_OPERATION) {
+ // MediaCodec now set state to UNINITIALIZED upon any fatal error.
+ // To maintain backward-compatibility, do a reset() to put codec
+ // back into INITIALIZED state.
+ // But don't reset if the err is INVALID_OPERATION, which means
+ // the configure failure is due to wrong state.
+
+ ALOGE("configure failed with err 0x%08x, resetting...", err);
+ reset();
+ }
+ if (!isResourceError(err)) {
+ break;
+ }
}
-
return err;
}
+status_t MediaCodec::setInputSurface(
+ const sp<PersistentSurface> &surface) {
+ sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
+ msg->setObject("input-surface", surface.get());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::setSurface(const sp<Surface> &surface) {
+ sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
+ msg->setObject("surface", surface);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::createInputSurface(
sp<IGraphicBufferProducer>* bufferProducer) {
sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, this);
@@ -316,15 +591,78 @@
return err;
}
+uint64_t MediaCodec::getGraphicBufferSize() {
+ if (!mIsVideo) {
+ return 0;
+ }
+
+ uint64_t size = 0;
+ size_t portNum = sizeof(mPortBuffers) / sizeof((mPortBuffers)[0]);
+ for (size_t i = 0; i < portNum; ++i) {
+ // TODO: this is just an estimation, we should get the real buffer size from ACodec.
+ size += mPortBuffers[i].size() * mVideoWidth * mVideoHeight * 3 / 2;
+ }
+ return size;
+}
+
+void MediaCodec::addResource(const String8 &type, const String8 &subtype, uint64_t value) {
+ Vector<MediaResource> resources;
+ resources.push_back(MediaResource(type, subtype, value));
+ mResourceManagerService->addResource(
+ getCallingPid(), getId(mResourceManagerClient), mResourceManagerClient, resources);
+}
+
status_t MediaCodec::start() {
sp<AMessage> msg = new AMessage(kWhatStart, this);
+ status_t err;
+ Vector<MediaResource> resources;
+ const char *type = (mFlags & kFlagIsSecure) ?
+ kResourceSecureCodec : kResourceNonSecureCodec;
+ const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+ resources.push_back(MediaResource(String8(type), String8(subtype), 1));
+ // Don't know the buffer size at this point, but it's fine to use 1 because
+ // the reclaimResource call doesn't consider the requester's buffer size for now.
+ resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1));
+ for (int i = 0; i <= kMaxRetry; ++i) {
+ if (i > 0) {
+ // Don't try to reclaim resource for the first time.
+ if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) {
+ break;
+ }
+ // Recover codec from previous error before retry start.
+ err = reset();
+ if (err != OK) {
+ ALOGE("retrying start: failed to reset codec");
+ break;
+ }
+ sp<AMessage> response;
+ err = PostAndAwaitResponse(mConfigureMsg, &response);
+ if (err != OK) {
+ ALOGE("retrying start: failed to configure codec");
+ break;
+ }
+ }
+
+ sp<AMessage> response;
+ err = PostAndAwaitResponse(msg, &response);
+ if (!isResourceError(err)) {
+ break;
+ }
+ }
+ return err;
+}
+
+status_t MediaCodec::stop() {
+ sp<AMessage> msg = new AMessage(kWhatStop, this);
+
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
-status_t MediaCodec::stop() {
- sp<AMessage> msg = new AMessage(kWhatStop, this);
+status_t MediaCodec::reclaim() {
+ sp<AMessage> msg = new AMessage(kWhatRelease, this);
+ msg->setInt32("reclaimed", 1);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
@@ -544,6 +882,16 @@
return OK;
}
+status_t MediaCodec::getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
+ msg->setInt32("portIndex", kPortIndexInput);
+ msg->setPointer("buffers", buffers);
+ msg->setInt32("widevine", true);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
msg->setInt32("portIndex", kPortIndexInput);
@@ -586,6 +934,10 @@
sp<ABuffer> *buffer, sp<AMessage> *format) {
// use mutex instead of a context switch
+ if (mReleasedByResourceManager) {
+ return DEAD_OBJECT;
+ }
+
buffer->clear();
format->clear();
if (!isExecuting()) {
@@ -675,20 +1027,19 @@
}
bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
- sp<AMessage> response = new AMessage;
-
if (!isExecuting() || (mFlags & kFlagIsAsync)
|| (newRequest && (mFlags & kFlagDequeueOutputPending))) {
- response->setInt32("err", INVALID_OPERATION);
+ PostReplyWithError(replyID, INVALID_OPERATION);
} else if (mFlags & kFlagStickyError) {
- response->setInt32("err", getStickyError());
+ PostReplyWithError(replyID, getStickyError());
} else if (mFlags & kFlagOutputBuffersChanged) {
- response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED);
+ PostReplyWithError(replyID, INFO_OUTPUT_BUFFERS_CHANGED);
mFlags &= ~kFlagOutputBuffersChanged;
} else if (mFlags & kFlagOutputFormatChanged) {
- response->setInt32("err", INFO_FORMAT_CHANGED);
+ PostReplyWithError(replyID, INFO_FORMAT_CHANGED);
mFlags &= ~kFlagOutputFormatChanged;
} else {
+ sp<AMessage> response = new AMessage;
ssize_t index = dequeuePortBuffer(kPortIndexOutput);
if (index < 0) {
@@ -723,10 +1074,9 @@
}
response->setInt32("flags", flags);
+ response->postReply(replyID);
}
- response->postReply(replyID);
-
return true;
}
@@ -884,12 +1234,18 @@
mFlags &= ~kFlagUsesSoftwareRenderer;
}
+ String8 resourceType;
if (mComponentName.endsWith(".secure")) {
mFlags |= kFlagIsSecure;
+ resourceType = String8(kResourceSecureCodec);
} else {
mFlags &= ~kFlagIsSecure;
+ resourceType = String8(kResourceNonSecureCodec);
}
+ const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+ addResource(resourceType, String8(subtype), 1);
+
(new AMessage)->postReply(mReplyID);
break;
}
@@ -918,7 +1274,7 @@
{
// response to initiateCreateInputSurface()
status_t err = NO_ERROR;
- sp<AMessage> response = new AMessage();
+ sp<AMessage> response = new AMessage;
if (!msg->findInt32("err", &err)) {
sp<RefBase> obj;
msg->findObject("input-surface", &obj);
@@ -932,10 +1288,24 @@
break;
}
+ case CodecBase::kWhatInputSurfaceAccepted:
+ {
+ // response to initiateSetInputSurface()
+ status_t err = NO_ERROR;
+ sp<AMessage> response = new AMessage();
+ if (!msg->findInt32("err", &err)) {
+ mHaveInputSurface = true;
+ } else {
+ response->setInt32("err", err);
+ }
+ response->postReply(mReplyID);
+ break;
+ }
+
case CodecBase::kWhatSignaledInputEOS:
{
// response to signalEndOfInputStream()
- sp<AMessage> response = new AMessage();
+ sp<AMessage> response = new AMessage;
status_t err;
if (msg->findInt32("err", &err)) {
response->setInt32("err", err);
@@ -969,6 +1339,17 @@
size_t numBuffers = portDesc->countBuffers();
+ size_t totalSize = 0;
+ for (size_t i = 0; i < numBuffers; ++i) {
+ if (portIndex == kPortIndexInput && mCrypto != NULL) {
+ totalSize += portDesc->bufferAt(i)->capacity();
+ }
+ }
+
+ if (totalSize) {
+ mDealer = new MemoryDealer(totalSize, "MediaCodec");
+ }
+
for (size_t i = 0; i < numBuffers; ++i) {
BufferInfo info;
info.mBufferID = portDesc->bufferIDAt(i);
@@ -976,8 +1357,10 @@
info.mData = portDesc->bufferAt(i);
if (portIndex == kPortIndexInput && mCrypto != NULL) {
+ sp<IMemory> mem = mDealer->allocate(info.mData->capacity());
info.mEncryptedData =
- new ABuffer(info.mData->capacity());
+ new ABuffer(mem->pointer(), info.mData->capacity());
+ info.mSharedEncryptedBuffer = mem;
}
buffers->push_back(info);
@@ -988,6 +1371,13 @@
// We're always allocating output buffers after
// allocating input buffers, so this is a good
// indication that now all buffers are allocated.
+ if (mIsVideo) {
+ String8 subtype;
+ addResource(
+ String8(kResourceGraphicMemory),
+ subtype,
+ getGraphicBufferSize());
+ }
setState(STARTED);
(new AMessage)->postReply(mReplyID);
} else {
@@ -1003,13 +1393,13 @@
ALOGV("codec output format changed");
if (mSoftRenderer == NULL &&
- mNativeWindow != NULL &&
+ mSurface != NULL &&
(mFlags & kFlagUsesSoftwareRenderer)) {
AString mime;
CHECK(msg->findString("mime", &mime));
if (mime.startsWithIgnoreCase("video/")) {
- mSoftRenderer = new SoftwareRenderer(mNativeWindow);
+ mSoftRenderer = new SoftwareRenderer(mSurface);
}
}
@@ -1078,7 +1468,11 @@
if (mFlags & kFlagIsAsync) {
if (!mHaveInputSurface) {
- onInputBufferAvailable();
+ if (mState == FLUSHED) {
+ mHavePendingInputBuffers = true;
+ } else {
+ onInputBufferAvailable();
+ }
}
} else if (mFlags & kFlagDequeueInputPending) {
CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));
@@ -1167,6 +1561,8 @@
}
mFlags &= ~kFlagIsComponentAllocated;
+ mResourceManagerService->removeResource(getId(mResourceManagerClient));
+
(new AMessage)->postReply(mReplyID);
break;
}
@@ -1275,26 +1671,25 @@
}
sp<RefBase> obj;
- if (!msg->findObject("native-window", &obj)) {
- obj.clear();
- }
+ CHECK(msg->findObject("surface", &obj));
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
+ int32_t push;
+ if (msg->findInt32("push-blank-buffers-on-shutdown", &push) && push != 0) {
+ mFlags |= kFlagPushBlankBuffersOnShutdown;
+ }
+
if (obj != NULL) {
format->setObject("native-window", obj);
-
- status_t err = setNativeWindow(
- static_cast<NativeWindowWrapper *>(obj.get())
- ->getSurfaceTextureClient());
-
+ status_t err = handleSetSurface(static_cast<Surface *>(obj.get()));
if (err != OK) {
PostReplyWithError(replyID, err);
break;
}
} else {
- setNativeWindow(NULL);
+ handleSetSurface(NULL);
}
mReplyID = replyID;
@@ -1321,7 +1716,67 @@
break;
}
+ case kWhatSetSurface:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ status_t err = OK;
+ sp<Surface> surface;
+
+ switch (mState) {
+ case CONFIGURED:
+ case STARTED:
+ case FLUSHED:
+ {
+ sp<RefBase> obj;
+ (void)msg->findObject("surface", &obj);
+ sp<Surface> surface = static_cast<Surface *>(obj.get());
+ if (mSurface == NULL) {
+ // do not support setting surface if it was not set
+ err = INVALID_OPERATION;
+ } else if (obj == NULL) {
+ // do not support unsetting surface
+ err = BAD_VALUE;
+ } else {
+ err = connectToSurface(surface);
+ if (err == BAD_VALUE) {
+ // assuming reconnecting to same surface
+ // TODO: check if it is the same surface
+ err = OK;
+ } else {
+ if (err == OK) {
+ if (mFlags & kFlagUsesSoftwareRenderer) {
+ if (mSoftRenderer != NULL
+ && (mFlags & kFlagPushBlankBuffersOnShutdown)) {
+ pushBlankBuffersToNativeWindow(mSurface.get());
+ }
+ mSoftRenderer = new SoftwareRenderer(surface);
+ // TODO: check if this was successful
+ } else {
+ err = mCodec->setSurface(surface);
+ }
+ }
+ if (err == OK) {
+ (void)disconnectFromSurface();
+ mSurface = surface;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ err = INVALID_OPERATION;
+ break;
+ }
+
+ PostReplyWithError(replyID, err);
+ break;
+ }
+
case kWhatCreateInputSurface:
+ case kWhatSetInputSurface:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
@@ -1333,10 +1788,17 @@
}
mReplyID = replyID;
- mCodec->initiateCreateInputSurface();
+ if (msg->what() == kWhatCreateInputSurface) {
+ mCodec->initiateCreateInputSurface();
+ } else {
+ sp<RefBase> obj;
+ CHECK(msg->findObject("input-surface", &obj));
+
+ mCodec->initiateSetInputSurface(
+ static_cast<PersistentSurface *>(obj.get()));
+ }
break;
}
-
case kWhatStart:
{
sp<AReplyToken> replyID;
@@ -1344,6 +1806,10 @@
if (mState == FLUSHED) {
setState(STARTED);
+ if (mHavePendingInputBuffers) {
+ onInputBufferAvailable();
+ mHavePendingInputBuffers = false;
+ }
mCodec->signalResume();
PostReplyWithError(replyID, OK);
break;
@@ -1368,6 +1834,20 @@
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ // already stopped/released
+ if (mState == UNINITIALIZED && mReleasedByResourceManager) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", OK);
+ response->postReply(replyID);
+ break;
+ }
+
+ int32_t reclaimed = 0;
+ msg->findInt32("reclaimed", &reclaimed);
+ if (reclaimed) {
+ mReleasedByResourceManager = true;
+ }
+
if (!((mFlags & kFlagIsComponentAllocated) && targetState == UNINITIALIZED) // See 1
&& mState != INITIALIZED
&& mState != CONFIGURED && !isExecuting()) {
@@ -1381,6 +1861,8 @@
// and it should be in this case, no harm to allow a release()
// if we're already uninitialized.
sp<AMessage> response = new AMessage;
+ // TODO: we shouldn't throw an exception for stop/release. Change this to wait until
+ // the previous stop/release completes and then reply with OK.
status_t err = mState == targetState ? OK : INVALID_OPERATION;
response->setInt32("err", err);
if (err == OK && targetState == UNINITIALIZED) {
@@ -1408,6 +1890,10 @@
msg->what() == kWhatStop /* keepComponentAllocated */);
returnBuffersToCodec();
+
+ if (mSoftRenderer != NULL && (mFlags & kFlagPushBlankBuffersOnShutdown)) {
+ pushBlankBuffersToNativeWindow(mSurface.get());
+ }
break;
}
@@ -1587,8 +2073,12 @@
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ // Unfortunately widevine legacy source requires knowing all of the
+ // codec input buffers, so we have to provide them even in async mode.
+ int32_t widevine = 0;
+ msg->findInt32("widevine", &widevine);
- if (!isExecuting() || (mFlags & kFlagIsAsync)) {
+ if (!isExecuting() || ((mFlags & kFlagIsAsync) && !widevine)) {
PostReplyWithError(replyID, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
@@ -1769,7 +2259,7 @@
mSoftRenderer = NULL;
mCrypto.clear();
- setNativeWindow(NULL);
+ handleSetSurface(NULL);
mInputFormat.clear();
mOutputFormat.clear();
@@ -1953,7 +2443,8 @@
key,
iv,
mode,
- info->mEncryptedData->base() + offset,
+ info->mSharedEncryptedBuffer,
+ offset,
subSamples,
numSubSamples,
info->mData->base(),
@@ -2074,37 +2565,44 @@
return index;
}
-status_t MediaCodec::setNativeWindow(
- const sp<Surface> &surfaceTextureClient) {
- status_t err;
-
- if (mNativeWindow != NULL) {
- err = native_window_api_disconnect(
- mNativeWindow.get(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGW("native_window_api_disconnect returned an error: %s (%d)",
- strerror(-err), err);
+status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {
+ status_t err = OK;
+ if (surface != NULL) {
+ err = native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA);
+ if (err == BAD_VALUE) {
+ ALOGI("native window already connected. Assuming no change of surface");
+ } else if (err != OK) {
+ ALOGE("native_window_api_connect returned an error: %s (%d)", strerror(-err), err);
}
-
- mNativeWindow.clear();
}
+ return err;
+}
- if (surfaceTextureClient != NULL) {
- err = native_window_api_connect(
- surfaceTextureClient.get(), NATIVE_WINDOW_API_MEDIA);
-
+status_t MediaCodec::disconnectFromSurface() {
+ status_t err = OK;
+ if (mSurface != NULL) {
+ err = native_window_api_disconnect(mSurface.get(), NATIVE_WINDOW_API_MEDIA);
if (err != OK) {
- ALOGE("native_window_api_connect returned an error: %s (%d)",
- strerror(-err), err);
-
- return err;
+ ALOGW("native_window_api_disconnect returned an error: %s (%d)", strerror(-err), err);
}
-
- mNativeWindow = surfaceTextureClient;
+ // assume disconnected even on error
+ mSurface.clear();
}
+ return err;
+}
- return OK;
+status_t MediaCodec::handleSetSurface(const sp<Surface> &surface) {
+ status_t err = OK;
+ if (mSurface != NULL) {
+ (void)disconnectFromSurface();
+ }
+ if (surface != NULL) {
+ err = connectToSurface(surface);
+ if (err == OK) {
+ mSurface = surface;
+ }
+ }
+ return err;
}
void MediaCodec::onInputBufferAvailable() {
@@ -2263,12 +2761,6 @@
void MediaCodec::updateBatteryStat() {
if (mState == CONFIGURED && !mBatteryStatNotified) {
- AString mime;
- CHECK(mOutputFormat != NULL &&
- mOutputFormat->findString("mime", &mime));
-
- mIsVideo = mime.startsWithIgnoreCase("video/");
-
BatteryNotifier& notifier(BatteryNotifier::getInstance());
if (mIsVideo) {
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index cf6e937..d2352bc 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -18,11 +18,15 @@
#define LOG_TAG "MediaCodecList"
#include <utils/Log.h>
+#include "MediaCodecListOverrides.h"
+
#include <binder/IServiceManager.h>
#include <media/IMediaCodecList.h>
#include <media/IMediaPlayerService.h>
+#include <media/IResourceManagerService.h>
#include <media/MediaCodecInfo.h>
+#include <media/MediaResourcePolicy.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -31,6 +35,7 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
+#include <sys/stat.h>
#include <utils/threads.h>
#include <libexpat/expat.h>
@@ -41,21 +46,59 @@
static MediaCodecList *gCodecList = NULL;
+static bool parseBoolean(const char *s) {
+ if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
+ return true;
+ }
+ char *end;
+ unsigned long res = strtoul(s, &end, 10);
+ return *s != '\0' && *end == '\0' && res > 0;
+}
+
// static
sp<IMediaCodecList> MediaCodecList::sCodecList;
// static
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
- Mutex::Autolock autoLock(sInitMutex);
+ bool profilingNeeded = false;
+ Vector<sp<MediaCodecInfo>> infos;
- if (gCodecList == NULL) {
- gCodecList = new MediaCodecList;
- if (gCodecList->initCheck() == OK) {
- sCodecList = gCodecList;
+ {
+ Mutex::Autolock autoLock(sInitMutex);
+
+ if (gCodecList == NULL) {
+ gCodecList = new MediaCodecList;
+ if (gCodecList->initCheck() == OK) {
+ sCodecList = gCodecList;
+
+ struct stat s;
+ if (stat(kProfilingResults, &s) == -1) {
+ // profiling results doesn't existed
+ profilingNeeded = true;
+ for (size_t i = 0; i < gCodecList->countCodecs(); ++i) {
+ infos.push_back(gCodecList->getCodecInfo(i));
+ }
+ }
+ } else {
+ // failure to initialize may be temporary. retry on next call.
+ delete gCodecList;
+ gCodecList = NULL;
+ }
}
}
- return sCodecList;
+ if (profilingNeeded) {
+ profileCodecs(infos);
+ }
+
+ {
+ Mutex::Autolock autoLock(sInitMutex);
+ if (profilingNeeded) {
+ gCodecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
+ }
+
+ return sCodecList;
+ }
}
static Mutex sRemoteInitMutex;
@@ -94,11 +137,14 @@
}
MediaCodecList::MediaCodecList()
- : mInitCheck(NO_INIT) {
+ : mInitCheck(NO_INIT),
+ mUpdate(false),
+ mGlobalSettings(new AMessage()) {
parseTopLevelXMLFile("/etc/media_codecs.xml");
+ parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
}
-void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
+void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
// get href_base
char *href_base_end = strrchr(codecs_xml, '/');
if (href_base_end != NULL) {
@@ -112,20 +158,42 @@
OMXClient client;
mInitCheck = client.connect();
if (mInitCheck != OK) {
- return;
+ return; // this may fail if IMediaPlayerService is not available.
}
mOMX = client.interface();
parseXMLFile(codecs_xml);
mOMX.clear();
if (mInitCheck != OK) {
+ if (ignore_errors) {
+ mInitCheck = OK;
+ return;
+ }
mCodecInfos.clear();
return;
}
+ Vector<MediaResourcePolicy> policies;
+ AString value;
+ if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) {
+ policies.push_back(
+ MediaResourcePolicy(
+ String8(kPolicySupportsMultipleSecureCodecs),
+ String8(value.c_str())));
+ }
+ if (policies.size() > 0) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
+ sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder);
+ if (service == NULL) {
+ ALOGE("MediaCodecList: failed to get ResourceManagerService");
+ } else {
+ service->config(policies);
+ }
+ }
+
for (size_t i = mCodecInfos.size(); i-- > 0;) {
const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-
if (info.mCaps.size() == 0) {
// No types supported by this component???
ALOGW("Component %s does not support any type of media?",
@@ -169,6 +237,16 @@
}
ALOGV(" levels=[%s]", nice.c_str());
}
+ {
+ AString quirks;
+ for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
+ if (ix > 0) {
+ quirks.append(", ");
+ }
+ quirks.append(info.mQuirks[ix]);
+ }
+ ALOGV(" quirks=[%s]", quirks.c_str());
+ }
}
#endif
}
@@ -328,6 +406,16 @@
mCurrentSection = SECTION_DECODERS;
} else if (!strcmp(name, "Encoders")) {
mCurrentSection = SECTION_ENCODERS;
+ } else if (!strcmp(name, "Settings")) {
+ mCurrentSection = SECTION_SETTINGS;
+ }
+ break;
+ }
+
+ case SECTION_SETTINGS:
+ {
+ if (!strcmp(name, "Setting")) {
+ mInitCheck = addSettingFromAttributes(attrs);
}
break;
}
@@ -397,6 +485,14 @@
}
switch (mCurrentSection) {
+ case SECTION_SETTINGS:
+ {
+ if (!strcmp(name, "Settings")) {
+ mCurrentSection = SECTION_TOPLEVEL;
+ }
+ break;
+ }
+
case SECTION_DECODERS:
{
if (!strcmp(name, "Decoders")) {
@@ -462,10 +558,81 @@
--mDepth;
}
+status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
+ const char *name = NULL;
+ const char *value = NULL;
+ const char *update = NULL;
+
+ size_t i = 0;
+ while (attrs[i] != NULL) {
+ if (!strcmp(attrs[i], "name")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ name = attrs[i + 1];
+ ++i;
+ } else if (!strcmp(attrs[i], "value")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ value = attrs[i + 1];
+ ++i;
+ } else if (!strcmp(attrs[i], "update")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ update = attrs[i + 1];
+ ++i;
+ } else {
+ return -EINVAL;
+ }
+
+ ++i;
+ }
+
+ if (name == NULL || value == NULL) {
+ return -EINVAL;
+ }
+
+ mUpdate = (update != NULL) && parseBoolean(update);
+ if (mUpdate != mGlobalSettings->contains(name)) {
+ return -EINVAL;
+ }
+
+ mGlobalSettings->setString(name, value);
+ return OK;
+}
+
+void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
+ for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+ if (AString(name) == mCodecInfos[i]->getCodecName()) {
+ if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
+ ALOGW("Overrides with an unexpected mime %s", type);
+ // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
+ // overrides we don't want.
+ mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+ } else {
+ mCurrentInfo = mCodecInfos.editItemAt(i);
+ mCurrentInfo->updateMime(type); // to set the current cap
+ }
+ return;
+ }
+ }
+ mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+ // The next step involves trying to load the codec, which may
+ // fail. Only list the codec if this succeeds.
+ // However, keep mCurrentInfo object around until parsing
+ // of full codec info is completed.
+ if (initializeCapabilities(type) == OK) {
+ mCodecInfos.push_back(mCurrentInfo);
+ }
+}
+
status_t MediaCodecList::addMediaCodecFromAttributes(
bool encoder, const char **attrs) {
const char *name = NULL;
const char *type = NULL;
+ const char *update = NULL;
size_t i = 0;
while (attrs[i] != NULL) {
@@ -481,6 +648,12 @@
}
type = attrs[i + 1];
++i;
+ } else if (!strcmp(attrs[i], "update")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ update = attrs[i + 1];
+ ++i;
} else {
return -EINVAL;
}
@@ -492,14 +665,39 @@
return -EINVAL;
}
- mCurrentInfo = new MediaCodecInfo(name, encoder, type);
- // The next step involves trying to load the codec, which may
- // fail. Only list the codec if this succeeds.
- // However, keep mCurrentInfo object around until parsing
- // of full codec info is completed.
- if (initializeCapabilities(type) == OK) {
- mCodecInfos.push_back(mCurrentInfo);
+ mUpdate = (update != NULL) && parseBoolean(update);
+ ssize_t index = -1;
+ for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+ if (AString(name) == mCodecInfos[i]->getCodecName()) {
+ index = i;
+ }
}
+ if (mUpdate != (index >= 0)) {
+ return -EINVAL;
+ }
+
+ if (index >= 0) {
+ // existing codec
+ mCurrentInfo = mCodecInfos.editItemAt(index);
+ if (type != NULL) {
+ // existing type
+ if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
+ return -EINVAL;
+ }
+ mCurrentInfo->updateMime(type);
+ }
+ } else {
+ // new codec
+ mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+ // The next step involves trying to load the codec, which may
+ // fail. Only list the codec if this succeeds.
+ // However, keep mCurrentInfo object around until parsing
+ // of full codec info is completed.
+ if (initializeCapabilities(type) == OK) {
+ mCodecInfos.push_back(mCurrentInfo);
+ }
+ }
+
return OK;
}
@@ -553,6 +751,7 @@
status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
const char *name = NULL;
+ const char *update = NULL;
size_t i = 0;
while (attrs[i] != NULL) {
@@ -562,6 +761,12 @@
}
name = attrs[i + 1];
++i;
+ } else if (!strcmp(attrs[i], "update")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ update = attrs[i + 1];
+ ++i;
} else {
return -EINVAL;
}
@@ -573,14 +778,25 @@
return -EINVAL;
}
- status_t ret = mCurrentInfo->addMime(name);
+ bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
+ if (mUpdate != isExistingType) {
+ return -EINVAL;
+ }
+
+ status_t ret;
+ if (mUpdate) {
+ ret = mCurrentInfo->updateMime(name);
+ } else {
+ ret = mCurrentInfo->addMime(name);
+ }
+
if (ret != OK) {
return ret;
}
// The next step involves trying to load the codec, which may
// fail. Handle this gracefully (by not reporting such mime).
- if (initializeCapabilities(name) != OK) {
+ if (!mUpdate && initializeCapabilities(name) != OK) {
mCurrentInfo->removeMime(name);
}
return OK;
@@ -675,14 +891,16 @@
return -EINVAL;
}
- // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range
+ // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
+ // measured-frame-rate, measured-blocks-per-second: range
// quality: range + default + [scale]
// complexity: range + default
bool found;
if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
|| name == "blocks-per-second" || name == "complexity"
- || name == "frame-rate" || name == "quality" || name == "size") {
+ || name == "frame-rate" || name == "quality" || name == "size"
+ || name == "measured-blocks-per-second" || name == "measured-frame-rate") {
AString min, max;
if (msg->findString("min", &min) && msg->findString("max", &max)) {
min.append("-");
@@ -758,7 +976,8 @@
return limitFoundMissingAttr(name, "ranges", found);
} else if (msg->contains("scale")) {
return limitFoundMissingAttr(name, "scale");
- } else if ((name == "alignment" || name == "block-size") ^
+ } else if ((name == "alignment" || name == "block-size"
+ || name == "max-supported-instances") ^
(found = msg->findString("value", &value))) {
return limitFoundMissingAttr(name, "value", found);
}
@@ -780,15 +999,6 @@
return OK;
}
-static bool parseBoolean(const char *s) {
- if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
- return true;
- }
- char *end;
- unsigned long res = strtoul(s, &end, 10);
- return *s != '\0' && *end == '\0' && res > 0;
-}
-
status_t MediaCodecList::addFeature(const char **attrs) {
size_t i = 0;
const char *name = NULL;
@@ -860,4 +1070,8 @@
return mCodecInfos.size();
}
+const sp<AMessage> MediaCodecList::getGlobalSettings() const {
+ return mGlobalSettings;
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
new file mode 100644
index 0000000..0d95676
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecListOverrides"
+#include <utils/Log.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <gui/Surface.h>
+#include <media/ICrypto.h>
+#include <media/IMediaCodecList.h>
+#include <media/MediaCodecInfo.h>
+#include <media/MediaResourcePolicy.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodec.h>
+
+namespace android {
+
+const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml";
+
+// a limit to avoid allocating unreasonable number of codec instances in the measurement.
+// this should be in sync with the MAX_SUPPORTED_INSTANCES defined in MediaCodecInfo.java.
+static const int kMaxInstances = 32;
+
+// TODO: move MediaCodecInfo to C++. Until then, some temp methods to parse out info.
+static bool getMeasureSize(sp<MediaCodecInfo::Capabilities> caps, int32_t *width, int32_t *height) {
+ AString sizeRange;
+ if (!caps->getDetails()->findString("size-range", &sizeRange)) {
+ return false;
+ }
+ AString minSize;
+ AString maxSize;
+ if (!splitString(sizeRange, "-", &minSize, &maxSize)) {
+ return false;
+ }
+ AString sWidth;
+ AString sHeight;
+ if (!splitString(minSize, "x", &sWidth, &sHeight)) {
+ if (!splitString(minSize, "*", &sWidth, &sHeight)) {
+ return false;
+ }
+ }
+
+ *width = strtol(sWidth.c_str(), NULL, 10);
+ *height = strtol(sHeight.c_str(), NULL, 10);
+ return (*width > 0) && (*height > 0);
+}
+
+static void getMeasureBitrate(sp<MediaCodecInfo::Capabilities> caps, int32_t *bitrate) {
+ // Until have native MediaCodecInfo, we cannot get bitrates based on profile/levels.
+ // We use 200000 as default value for our measurement.
+ *bitrate = 200000;
+ AString bitrateRange;
+ if (!caps->getDetails()->findString("bitrate-range", &bitrateRange)) {
+ return;
+ }
+ AString minBitrate;
+ AString maxBitrate;
+ if (!splitString(bitrateRange, "-", &minBitrate, &maxBitrate)) {
+ return;
+ }
+
+ *bitrate = strtol(minBitrate.c_str(), NULL, 10);
+}
+
+static sp<AMessage> getMeasureFormat(
+ bool isEncoder, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+ sp<AMessage> format = new AMessage();
+ format->setString("mime", mime);
+
+ if (isEncoder) {
+ int32_t bitrate = 0;
+ getMeasureBitrate(caps, &bitrate);
+ format->setInt32("bitrate", bitrate);
+ }
+
+ if (mime.startsWith("video/")) {
+ int32_t width = 0;
+ int32_t height = 0;
+ if (!getMeasureSize(caps, &width, &height)) {
+ return NULL;
+ }
+ format->setInt32("width", width);
+ format->setInt32("height", height);
+
+ Vector<uint32_t> colorFormats;
+ caps->getSupportedColorFormats(&colorFormats);
+ if (colorFormats.size() == 0) {
+ return NULL;
+ }
+ format->setInt32("color-format", colorFormats[0]);
+
+ format->setFloat("frame-rate", 10.0);
+ format->setInt32("i-frame-interval", 10);
+ } else {
+ // TODO: profile hw audio
+ return NULL;
+ }
+
+ return format;
+}
+
+static size_t doProfileCodecs(
+ bool isEncoder, AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+ sp<AMessage> format = getMeasureFormat(isEncoder, mime, caps);
+ if (format == NULL) {
+ return 0;
+ }
+ if (isEncoder) {
+ format->setInt32("encoder", 1);
+ }
+ ALOGV("doProfileCodecs %s %s %s %s",
+ name.c_str(), mime.c_str(), isEncoder ? "encoder" : "decoder",
+ format->debugString().c_str());
+
+ status_t err = OK;
+ Vector<sp<MediaCodec>> codecs;
+ while (err == OK && codecs.size() < kMaxInstances) {
+ sp<ALooper> looper = new ALooper;
+ looper->setName("MediaCodec_looper");
+ ALOGV("doProfileCodecs for codec #%zu", codecs.size());
+ ALOGV("doProfileCodecs start looper");
+ looper->start(
+ false /* runOnCallingThread */, false /* canCallJava */, ANDROID_PRIORITY_AUDIO);
+ ALOGV("doProfileCodecs CreateByComponentName");
+ sp<MediaCodec> codec = MediaCodec::CreateByComponentName(looper, name.c_str(), &err);
+ if (err != OK) {
+ ALOGV("Failed to create codec: %s", name.c_str());
+ break;
+ }
+ const sp<Surface> nativeWindow;
+ const sp<ICrypto> crypto;
+ uint32_t flags = 0;
+ ALOGV("doProfileCodecs configure");
+ err = codec->configure(format, nativeWindow, crypto, flags);
+ if (err != OK) {
+ ALOGV("Failed to configure codec: %s with mime: %s", name.c_str(), mime.c_str());
+ codec->release();
+ break;
+ }
+ ALOGV("doProfileCodecs start");
+ err = codec->start();
+ if (err != OK) {
+ ALOGV("Failed to start codec: %s with mime: %s", name.c_str(), mime.c_str());
+ codec->release();
+ break;
+ }
+ codecs.push_back(codec);
+ }
+
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ ALOGV("doProfileCodecs release %s", name.c_str());
+ err = codecs[i]->release();
+ if (err != OK) {
+ ALOGE("Failed to release codec: %s with mime: %s", name.c_str(), mime.c_str());
+ }
+ }
+
+ return codecs.size();
+}
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2) {
+ ssize_t pos = s.find(delimiter.c_str());
+ if (pos < 0) {
+ return false;
+ }
+ *s1 = AString(s, 0, pos);
+ *s2 = AString(s, pos + 1, s.size() - pos - 1);
+ return true;
+}
+
+bool splitString(
+ const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3) {
+ AString temp;
+ if (!splitString(s, delimiter, s1, &temp)) {
+ return false;
+ }
+ if (!splitString(temp, delimiter, s2, s3)) {
+ return false;
+ }
+ return true;
+}
+
+void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos) {
+ CodecSettings global_results;
+ KeyedVector<AString, CodecSettings> encoder_results;
+ KeyedVector<AString, CodecSettings> decoder_results;
+ profileCodecs(infos, &global_results, &encoder_results, &decoder_results);
+ exportResultsToXML(kProfilingResults, global_results, encoder_results, decoder_results);
+}
+
+void profileCodecs(
+ const Vector<sp<MediaCodecInfo>> &infos,
+ CodecSettings *global_results,
+ KeyedVector<AString, CodecSettings> *encoder_results,
+ KeyedVector<AString, CodecSettings> *decoder_results,
+ bool forceToMeasure) {
+ KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure;
+ AString supportMultipleSecureCodecs = "true";
+ for (size_t i = 0; i < infos.size(); ++i) {
+ const sp<MediaCodecInfo> info = infos[i];
+ AString name = info->getCodecName();
+ if (name.startsWith("OMX.google.") ||
+ // TODO: reenable below codecs once fixed
+ name == "OMX.Intel.VideoDecoder.VP9.hybrid") {
+ continue;
+ }
+
+ Vector<AString> mimes;
+ info->getSupportedMimes(&mimes);
+ for (size_t i = 0; i < mimes.size(); ++i) {
+ const sp<MediaCodecInfo::Capabilities> &caps =
+ info->getCapabilitiesFor(mimes[i].c_str());
+ if (!forceToMeasure && caps->getDetails()->contains("max-supported-instances")) {
+ continue;
+ }
+
+ size_t max = doProfileCodecs(info->isEncoder(), name, mimes[i], caps);
+ if (max > 0) {
+ CodecSettings settings;
+ char maxStr[32];
+ sprintf(maxStr, "%zu", max);
+ settings.add("max-supported-instances", maxStr);
+
+ AString key = name;
+ key.append(" ");
+ key.append(mimes[i]);
+
+ if (info->isEncoder()) {
+ encoder_results->add(key, settings);
+ } else {
+ decoder_results->add(key, settings);
+ }
+
+ if (name.endsWith(".secure")) {
+ if (max <= 1) {
+ supportMultipleSecureCodecs = "false";
+ }
+ }
+ }
+ }
+ }
+ global_results->add(kPolicySupportsMultipleSecureCodecs, supportMultipleSecureCodecs);
+}
+
+static AString globalResultsToXml(const CodecSettings& results) {
+ AString ret;
+ for (size_t i = 0; i < results.size(); ++i) {
+ AString setting = AStringPrintf(
+ " <Setting name=\"%s\" value=\"%s\" />\n",
+ results.keyAt(i).c_str(),
+ results.valueAt(i).c_str());
+ ret.append(setting);
+ }
+ return ret;
+}
+
+static AString codecResultsToXml(const KeyedVector<AString, CodecSettings>& results) {
+ AString ret;
+ for (size_t i = 0; i < results.size(); ++i) {
+ AString name;
+ AString mime;
+ if (!splitString(results.keyAt(i), " ", &name, &mime)) {
+ continue;
+ }
+ AString codec =
+ AStringPrintf(" <MediaCodec name=\"%s\" type=\"%s\" update=\"true\" >\n",
+ name.c_str(),
+ mime.c_str());
+ ret.append(codec);
+ CodecSettings settings = results.valueAt(i);
+ for (size_t i = 0; i < settings.size(); ++i) {
+ // WARNING: we assume all the settings are "Limit". Currently we have only one type
+ // of setting in this case, which is "max-supported-instances".
+ AString setting = AStringPrintf(
+ " <Limit name=\"%s\" value=\"%s\" />\n",
+ settings.keyAt(i).c_str(),
+ settings.valueAt(i).c_str());
+ ret.append(setting);
+ }
+ ret.append(" </MediaCodec>\n");
+ }
+ return ret;
+}
+
+void exportResultsToXML(
+ const char *fileName,
+ const CodecSettings& global_results,
+ const KeyedVector<AString, CodecSettings>& encoder_results,
+ const KeyedVector<AString, CodecSettings>& decoder_results) {
+ if (global_results.size() == 0 && encoder_results.size() == 0 && decoder_results.size() == 0) {
+ return;
+ }
+
+ AString overrides;
+ overrides.append("<MediaCodecs>\n");
+ if (global_results.size() > 0) {
+ overrides.append(" <Settings>\n");
+ overrides.append(globalResultsToXml(global_results));
+ overrides.append(" </Settings>\n");
+ }
+ if (encoder_results.size() > 0) {
+ overrides.append(" <Encoders>\n");
+ overrides.append(codecResultsToXml(encoder_results));
+ overrides.append(" </Encoders>\n");
+ }
+ if (decoder_results.size() > 0) {
+ overrides.append(" <Decoders>\n");
+ overrides.append(codecResultsToXml(decoder_results));
+ overrides.append(" </Decoders>\n");
+ }
+ overrides.append("</MediaCodecs>\n");
+
+ FILE *f = fopen(fileName, "wb");
+ if (f == NULL) {
+ ALOGE("Failed to open %s for writing.", fileName);
+ return;
+ }
+ if (fwrite(overrides.c_str(), 1, overrides.size(), f) != overrides.size()) {
+ ALOGE("Failed to write to %s.", fileName);
+ }
+ fclose(f);
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h
new file mode 100644
index 0000000..e350d2a
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#define MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+extern const char *kProfilingResults;
+
+struct MediaCodecInfo;
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2);
+
+// profile codecs and save the result to xml file named kProfilingResults.
+void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos);
+
+// profile codecs and save the result to global_results, encoder_results and decoder_results.
+void profileCodecs(
+ const Vector<sp<MediaCodecInfo>> &infos,
+ CodecSettings *global_results,
+ KeyedVector<AString, CodecSettings> *encoder_results,
+ KeyedVector<AString, CodecSettings> *decoder_results,
+ bool forceToMeasure = false);
+
+void exportResultsToXML(
+ const char *fileName,
+ const CodecSettings& global_results,
+ const KeyedVector<AString, CodecSettings>& encoder_results,
+ const KeyedVector<AString, CodecSettings>& decoder_results);
+
+} // namespace android
+
+#endif // MEDIA_CODEC_LIST_OVERRIDES_H_
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index b6fa810..e089c46 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -20,6 +20,7 @@
#include <inttypes.h>
+#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
#include <media/ICrypto.h>
@@ -29,10 +30,11 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaCodec.h>
-#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaCodecSource.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MediaCodecSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/PersistentSurface.h>
#include <media/stagefright/Utils.h>
namespace android {
@@ -258,9 +260,10 @@
const sp<ALooper> &looper,
const sp<AMessage> &format,
const sp<MediaSource> &source,
+ const sp<IGraphicBufferConsumer> &consumer,
uint32_t flags) {
sp<MediaCodecSource> mediaSource =
- new MediaCodecSource(looper, format, source, flags);
+ new MediaCodecSource(looper, format, source, consumer, flags);
if (mediaSource->init() == OK) {
return mediaSource;
@@ -328,6 +331,7 @@
const sp<ALooper> &looper,
const sp<AMessage> &outputFormat,
const sp<MediaSource> &source,
+ const sp<IGraphicBufferConsumer> &consumer,
uint32_t flags)
: mLooper(looper),
mOutputFormat(outputFormat),
@@ -337,6 +341,7 @@
mStarted(false),
mStopping(false),
mDoMoreWorkPending(false),
+ mGraphicBufferConsumer(consumer),
mFirstSampleTimeUs(-1ll),
mEncoderReachedEOS(false),
mErrorCode(OK) {
@@ -399,6 +404,9 @@
ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+ mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
+ mEncoder->setCallback(mEncoderActivityNotify);
+
status_t err = mEncoder->configure(
mOutputFormat,
NULL /* nativeWindow */,
@@ -415,16 +423,21 @@
if (mFlags & FLAG_USE_SURFACE_INPUT) {
CHECK(mIsVideo);
- err = mEncoder->createInputSurface(&mGraphicBufferProducer);
+ if (mGraphicBufferConsumer != NULL) {
+ // When using persistent surface, we are only interested in the
+ // consumer, but have to use PersistentSurface as a wrapper to
+ // pass consumer over messages (similar to BufferProducerWrapper)
+ err = mEncoder->setInputSurface(
+ new PersistentSurface(NULL, mGraphicBufferConsumer));
+ } else {
+ err = mEncoder->createInputSurface(&mGraphicBufferProducer);
+ }
if (err != OK) {
return err;
}
}
- mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
- mEncoder->setCallback(mEncoderActivityNotify);
-
err = mEncoder->start();
if (err != OK) {
@@ -682,7 +695,6 @@
size_t size;
int64_t timeUs;
int32_t flags;
- native_handle_t* handle = NULL;
CHECK(msg->findInt32("index", &index));
CHECK(msg->findSize("offset", &offset));
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index c48a5ae..b0a65d2 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -62,5 +62,6 @@
const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
const char *MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
const char *MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
+const char *MEDIA_MIMETYPE_DATA_METADATA = "application/octet-stream";
} // namespace android
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index 7b6c7d9..b402e48 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -49,6 +49,7 @@
mMutex(),
mReleaseCondition(),
mNumOutstandingBuffers(0),
+ mUsageFlagsFromOutput(0),
mNativeSampleRateInHz(0),
mNumFramesWritten(0),
mHasAudio(false),
@@ -56,6 +57,10 @@
mPlaybackRate(0.0) {
mMediaClock = new MediaClock;
+ // initialize settings
+ mPlaybackSettings = AUDIO_PLAYBACK_RATE_DEFAULT;
+ mPlaybackSettings.mSpeed = mPlaybackRate;
+
mLooper = new ALooper;
mLooper->setName("MediaSync");
mLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
@@ -75,18 +80,37 @@
}
}
-status_t MediaSync::configureSurface(const sp<IGraphicBufferProducer> &output) {
+status_t MediaSync::setSurface(const sp<IGraphicBufferProducer> &output) {
Mutex::Autolock lock(mMutex);
- // TODO: support suface change.
- if (mOutput != NULL) {
- ALOGE("configureSurface: output surface has already been configured.");
+ if (output == mOutput) {
+ return NO_ERROR; // same output surface.
+ }
+
+ if (output == NULL && mSyncSettings.mSource == AVSYNC_SOURCE_VSYNC) {
+ ALOGE("setSurface: output surface is used as sync source and cannot be removed.");
return INVALID_OPERATION;
}
if (output != NULL) {
+ int newUsage = 0;
+ output->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &newUsage);
+
+ // Check usage flags only when current output surface has been used to create input surface.
+ if (mOutput != NULL && mInput != NULL) {
+ int ignoredFlags = (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER
+ | GRALLOC_USAGE_EXTERNAL_DISP);
+ // New output surface is not allowed to add new usage flag except ignored ones.
+ if ((newUsage & ~(mUsageFlagsFromOutput | ignoredFlags)) != 0) {
+ ALOGE("setSurface: new output surface has new usage flag not used by current one.");
+ return BAD_VALUE;
+ }
+ }
+
+ // Try to connect to new output surface. If failed, current output surface will not
+ // be changed.
IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
- sp<OutputListener> listener(new OutputListener(this));
+ sp<OutputListener> listener(new OutputListener(this, output));
IInterface::asBinder(output)->linkToDeath(listener);
status_t status =
output->connect(listener,
@@ -94,31 +118,63 @@
true /* producerControlledByApp */,
&queueBufferOutput);
if (status != NO_ERROR) {
- ALOGE("configureSurface: failed to connect (%d)", status);
+ ALOGE("setSurface: failed to connect (%d)", status);
return status;
}
-
- mOutput = output;
}
+ if (mOutput != NULL) {
+ mOutput->disconnect(NATIVE_WINDOW_API_MEDIA);
+ while (!mBuffersSentToOutput.isEmpty()) {
+ returnBufferToInput_l(mBuffersSentToOutput.valueAt(0), Fence::NO_FENCE);
+ mBuffersSentToOutput.removeItemsAt(0);
+ }
+ }
+
+ mOutput = output;
+
return NO_ERROR;
}
// |audioTrack| is used only for querying information.
-status_t MediaSync::configureAudioTrack(
- const sp<AudioTrack> &audioTrack, uint32_t nativeSampleRateInHz) {
+status_t MediaSync::setAudioTrack(const sp<AudioTrack> &audioTrack) {
Mutex::Autolock lock(mMutex);
// TODO: support audio track change.
if (mAudioTrack != NULL) {
- ALOGE("configureAudioTrack: audioTrack has already been configured.");
+ ALOGE("setAudioTrack: audioTrack has already been configured.");
return INVALID_OPERATION;
}
- mAudioTrack = audioTrack;
- mNativeSampleRateInHz = nativeSampleRateInHz;
+ if (audioTrack == NULL && mSyncSettings.mSource == AVSYNC_SOURCE_AUDIO) {
+ ALOGE("setAudioTrack: audioTrack is used as sync source and cannot be removed.");
+ return INVALID_OPERATION;
+ }
- return NO_ERROR;
+ if (audioTrack != NULL) {
+ // check if audio track supports the playback settings
+ if (mPlaybackSettings.mSpeed != 0.f
+ && audioTrack->setPlaybackRate(mPlaybackSettings) != OK) {
+ ALOGE("playback settings are not supported by the audio track");
+ return INVALID_OPERATION;
+ }
+ uint32_t nativeSampleRateInHz = audioTrack->getOriginalSampleRate();
+ if (nativeSampleRateInHz <= 0) {
+ ALOGE("setAudioTrack: native sample rate should be positive.");
+ return BAD_VALUE;
+ }
+ mAudioTrack = audioTrack;
+ mNativeSampleRateInHz = nativeSampleRateInHz;
+ (void)setPlaybackSettings_l(mPlaybackSettings);
+ }
+ else {
+ mAudioTrack = NULL;
+ mNativeSampleRateInHz = 0;
+ }
+
+ // potentially resync to new source
+ resync_l();
+ return OK;
}
status_t MediaSync::createInputSurface(
@@ -147,33 +203,79 @@
bufferConsumer->consumerConnect(listener, false /* controlledByApp */);
if (status == NO_ERROR) {
bufferConsumer->setConsumerName(String8("MediaSync"));
+ // propagate usage bits from output surface
+ mUsageFlagsFromOutput = 0;
+ mOutput->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &mUsageFlagsFromOutput);
+ bufferConsumer->setConsumerUsageBits(mUsageFlagsFromOutput);
*outBufferProducer = bufferProducer;
mInput = bufferConsumer;
}
return status;
}
-status_t MediaSync::setPlaybackRate(float rate) {
- if (rate < 0.0) {
- return BAD_VALUE;
+void MediaSync::resync_l() {
+ AVSyncSource src = mSyncSettings.mSource;
+ if (src == AVSYNC_SOURCE_DEFAULT) {
+ if (mAudioTrack != NULL) {
+ src = AVSYNC_SOURCE_AUDIO;
+ } else {
+ src = AVSYNC_SOURCE_SYSTEM_CLOCK;
+ }
}
- Mutex::Autolock lock(mMutex);
+ // TODO: resync ourselves to the current clock (e.g. on sync source change)
+ updatePlaybackRate_l(mPlaybackRate);
+}
+void MediaSync::updatePlaybackRate_l(float rate) {
if (rate > mPlaybackRate) {
mNextBufferItemMediaUs = -1;
}
mPlaybackRate = rate;
mMediaClock->setPlaybackRate(rate);
onDrainVideo_l();
-
- return OK;
}
sp<const MediaClock> MediaSync::getMediaClock() {
return mMediaClock;
}
+status_t MediaSync::getPlayTimeForPendingAudioFrames(int64_t *outTimeUs) {
+ Mutex::Autolock lock(mMutex);
+ // User should check the playback rate if it doesn't want to receive a
+ // huge number for play time.
+ if (mPlaybackRate == 0.0f) {
+ *outTimeUs = INT64_MAX;
+ return OK;
+ }
+
+ uint32_t numFramesPlayed = 0;
+ if (mAudioTrack != NULL) {
+ status_t res = mAudioTrack->getPosition(&numFramesPlayed);
+ if (res != OK) {
+ return res;
+ }
+ }
+
+ int64_t numPendingFrames = mNumFramesWritten - numFramesPlayed;
+ if (numPendingFrames < 0) {
+ numPendingFrames = 0;
+ ALOGW("getPlayTimeForPendingAudioFrames: pending frame count is negative.");
+ }
+ double timeUs = numPendingFrames * 1000000.0
+ / (mNativeSampleRateInHz * (double)mPlaybackRate);
+ if (timeUs > (double)INT64_MAX) {
+ // Overflow.
+ *outTimeUs = INT64_MAX;
+ ALOGW("getPlayTimeForPendingAudioFrames: play time for pending audio frames "
+ "is too high, possibly due to super low playback rate(%f)", mPlaybackRate);
+ } else {
+ *outTimeUs = (int64_t)timeUs;
+ }
+
+ return OK;
+}
+
status_t MediaSync::updateQueuedAudioData(
size_t sizeInBytes, int64_t presentationTimeUs) {
if (sizeInBytes == 0) {
@@ -190,13 +292,14 @@
int64_t numFrames = sizeInBytes / mAudioTrack->frameSize();
int64_t maxMediaTimeUs = presentationTimeUs
+ getDurationIfPlayedAtNativeSampleRate_l(numFrames);
- mNumFramesWritten += numFrames;
int64_t nowUs = ALooper::GetNowUs();
- int64_t nowMediaUs = maxMediaTimeUs
+ int64_t nowMediaUs = presentationTimeUs
- getDurationIfPlayedAtNativeSampleRate_l(mNumFramesWritten)
+ getPlayedOutAudioDurationMedia_l(nowUs);
+ mNumFramesWritten += numFrames;
+
int64_t oldRealTime = -1;
if (mNextBufferItemMediaUs != -1) {
oldRealTime = getRealTime(mNextBufferItemMediaUs, nowUs);
@@ -207,12 +310,13 @@
if (oldRealTime != -1) {
int64_t newRealTime = getRealTime(mNextBufferItemMediaUs, nowUs);
- if (newRealTime < oldRealTime) {
- mNextBufferItemMediaUs = -1;
- onDrainVideo_l();
+ if (newRealTime >= oldRealTime) {
+ return OK;
}
}
+ mNextBufferItemMediaUs = -1;
+ onDrainVideo_l();
return OK;
}
@@ -221,6 +325,95 @@
mInput->setConsumerName(String8(name.c_str()));
}
+status_t MediaSync::setVideoFrameRateHint(float rate) {
+ // ignored until we add the FrameScheduler
+ return rate >= 0.f ? OK : BAD_VALUE;
+}
+
+float MediaSync::getVideoFrameRate() {
+ // we don't know the frame rate
+ return -1.f;
+}
+
+status_t MediaSync::setSyncSettings(const AVSyncSettings &syncSettings) {
+ // validate settings
+ if (syncSettings.mSource >= AVSYNC_SOURCE_MAX
+ || syncSettings.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
+ || syncSettings.mTolerance < 0.f
+ || syncSettings.mTolerance >= AVSYNC_TOLERANCE_MAX) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ // verify that we have the sync source
+ switch (syncSettings.mSource) {
+ case AVSYNC_SOURCE_AUDIO:
+ if (mAudioTrack == NULL) {
+ ALOGE("setSyncSettings: audio sync source requires an audio track");
+ return BAD_VALUE;
+ }
+ break;
+ case AVSYNC_SOURCE_VSYNC:
+ if (mOutput == NULL) {
+ ALOGE("setSyncSettings: vsync sync source requires an output surface");
+ return BAD_VALUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ mSyncSettings = syncSettings;
+ resync_l();
+ return OK;
+}
+
+void MediaSync::getSyncSettings(AVSyncSettings *syncSettings) {
+ Mutex::Autolock lock(mMutex);
+ *syncSettings = mSyncSettings;
+}
+
+status_t MediaSync::setPlaybackSettings(const AudioPlaybackRate &rate) {
+ Mutex::Autolock lock(mMutex);
+
+ status_t err = setPlaybackSettings_l(rate);
+ if (err == OK) {
+ // TODO: adjust rate if using VSYNC as source
+ updatePlaybackRate_l(rate.mSpeed);
+ }
+ return err;
+}
+
+status_t MediaSync::setPlaybackSettings_l(const AudioPlaybackRate &rate) {
+ if (rate.mSpeed < 0.f || rate.mPitch < 0.f) {
+ // We don't validate other audio settings.
+ // They will be validated when/if audiotrack is set.
+ return BAD_VALUE;
+ }
+
+ if (mAudioTrack != NULL) {
+ if (rate.mSpeed == 0.f) {
+ mAudioTrack->pause();
+ } else {
+ status_t err = mAudioTrack->setPlaybackRate(rate);
+ if (err != OK) {
+ return BAD_VALUE;
+ }
+
+ // ignore errors
+ (void)mAudioTrack->start();
+ }
+ }
+ mPlaybackSettings = rate;
+ return OK;
+}
+
+void MediaSync::getPlaybackSettings(AudioPlaybackRate *rate) {
+ Mutex::Autolock lock(mMutex);
+ *rate = mPlaybackSettings;
+}
+
int64_t MediaSync::getRealTime(int64_t mediaTimeUs, int64_t nowUs) {
int64_t realUs;
if (mMediaClock->getRealTimeFor(mediaTimeUs, &realUs) != OK) {
@@ -311,12 +504,12 @@
return;
}
- int64_t nowUs = ALooper::GetNowUs();
-
while (!mBufferItems.empty()) {
+ int64_t nowUs = ALooper::GetNowUs();
BufferItem *bufferItem = &*mBufferItems.begin();
int64_t itemMediaUs = bufferItem->mTimestamp / 1000;
int64_t itemRealUs = getRealTime(itemMediaUs, nowUs);
+
if (itemRealUs <= nowUs) {
if (mHasAudio) {
if (nowUs - itemRealUs <= kMaxAllowedVideoLateTimeUs) {
@@ -336,15 +529,13 @@
}
mBufferItems.erase(mBufferItems.begin());
-
- if (mBufferItems.empty()) {
- mNextBufferItemMediaUs = -1;
- }
+ mNextBufferItemMediaUs = -1;
} else {
if (mNextBufferItemMediaUs == -1
- || mNextBufferItemMediaUs != itemMediaUs) {
+ || mNextBufferItemMediaUs > itemMediaUs) {
sp<AMessage> msg = new AMessage(kWhatDrainVideo, this);
msg->post(itemRealUs - nowUs);
+ mNextBufferItemMediaUs = itemMediaUs;
}
break;
}
@@ -389,8 +580,19 @@
return;
}
+ if (mBuffersFromInput.indexOfKey(bufferItem.mGraphicBuffer->getId()) >= 0) {
+ // Something is wrong since this buffer should be at our hands, bail.
+ mInput->consumerDisconnect();
+ onAbandoned_l(true /* isInput */);
+ return;
+ }
+ mBuffersFromInput.add(bufferItem.mGraphicBuffer->getId(), bufferItem.mGraphicBuffer);
+
mBufferItems.push_back(bufferItem);
- onDrainVideo_l();
+
+ if (mBufferItems.size() == 1) {
+ onDrainVideo_l();
+ }
}
void MediaSync::renderOneBufferItem_l( const BufferItem &bufferItem) {
@@ -423,12 +625,24 @@
return;
}
+ if (mBuffersSentToOutput.indexOfKey(bufferItem.mGraphicBuffer->getId()) >= 0) {
+ // Something is wrong since this buffer should be held by output now, bail.
+ mInput->consumerDisconnect();
+ onAbandoned_l(true /* isInput */);
+ return;
+ }
+ mBuffersSentToOutput.add(bufferItem.mGraphicBuffer->getId(), bufferItem.mGraphicBuffer);
+
ALOGV("queued buffer %#llx to output", (long long)bufferItem.mGraphicBuffer->getId());
}
-void MediaSync::onBufferReleasedByOutput() {
+void MediaSync::onBufferReleasedByOutput(sp<IGraphicBufferProducer> &output) {
Mutex::Autolock lock(mMutex);
+ if (output != mOutput) {
+ return; // This is not the current output, ignore.
+ }
+
sp<GraphicBuffer> buffer;
sp<Fence> fence;
status_t status = mOutput->detachNextBuffer(&buffer, &fence);
@@ -449,14 +663,31 @@
return;
}
+ ssize_t ix = mBuffersSentToOutput.indexOfKey(buffer->getId());
+ if (ix < 0) {
+ // The buffer is unknown, maybe leftover, ignore.
+ return;
+ }
+ mBuffersSentToOutput.removeItemsAt(ix);
+
returnBufferToInput_l(buffer, fence);
}
void MediaSync::returnBufferToInput_l(
const sp<GraphicBuffer> &buffer, const sp<Fence> &fence) {
+ ssize_t ix = mBuffersFromInput.indexOfKey(buffer->getId());
+ if (ix < 0) {
+ // The buffer is unknown, something is wrong, bail.
+ mOutput->disconnect(NATIVE_WINDOW_API_MEDIA);
+ onAbandoned_l(false /* isInput */);
+ return;
+ }
+ sp<GraphicBuffer> oldBuffer = mBuffersFromInput.valueAt(ix);
+ mBuffersFromInput.removeItemsAt(ix);
+
// Attach and release the buffer back to the input.
int consumerSlot;
- status_t status = mInput->attachBuffer(&consumerSlot, buffer);
+ status_t status = mInput->attachBuffer(&consumerSlot, oldBuffer);
ALOGE_IF(status != NO_ERROR, "attaching buffer to input failed (%d)", status);
if (status == NO_ERROR) {
status = mInput->releaseBuffer(consumerSlot, 0 /* frameNumber */,
@@ -469,7 +700,7 @@
return;
}
- ALOGV("released buffer %#llx to input", (long long)buffer->getId());
+ ALOGV("released buffer %#llx to input", (long long)oldBuffer->getId());
// Notify any waiting onFrameAvailable calls.
--mNumOutstandingBuffers;
@@ -494,6 +725,20 @@
case kWhatDrainVideo:
{
Mutex::Autolock lock(mMutex);
+ if (mNextBufferItemMediaUs != -1) {
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t itemRealUs = getRealTime(mNextBufferItemMediaUs, nowUs);
+
+ // The message could arrive earlier than expected due to
+ // various reasons, e.g., media clock has been changed because
+ // of new anchor time or playback rate. In such cases, the
+ // message needs to be re-posted.
+ if (itemRealUs > nowUs) {
+ msg->post(itemRealUs - nowUs);
+ break;
+ }
+ }
+
onDrainVideo_l();
break;
}
@@ -524,13 +769,15 @@
mSync->onAbandoned_l(true /* isInput */);
}
-MediaSync::OutputListener::OutputListener(const sp<MediaSync> &sync)
- : mSync(sync) {}
+MediaSync::OutputListener::OutputListener(const sp<MediaSync> &sync,
+ const sp<IGraphicBufferProducer> &output)
+ : mSync(sync),
+ mOutput(output) {}
MediaSync::OutputListener::~OutputListener() {}
void MediaSync::OutputListener::onBufferReleased() {
- mSync->onBufferReleasedByOutput();
+ mSync->onBufferReleasedByOutput(mOutput);
}
void MediaSync::OutputListener::binderDied(const wp<IBinder> &/* who */) {
diff --git a/media/libstagefright/MidiExtractor.cpp b/media/libstagefright/MidiExtractor.cpp
index 66fab77..f6b8c84 100644
--- a/media/libstagefright/MidiExtractor.cpp
+++ b/media/libstagefright/MidiExtractor.cpp
@@ -217,7 +217,7 @@
}
status_t MidiEngine::seekTo(int64_t positionUs) {
- ALOGV("seekTo %lld", positionUs);
+ ALOGV("seekTo %lld", (long long)positionUs);
EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false);
return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
}
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 8d70e50..1c53b40 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -503,7 +503,7 @@
ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
Mutex::Autolock autoSerializer(mSerializer);
- ALOGV("readAt offset %lld, size %zu", offset, size);
+ ALOGV("readAt offset %lld, size %zu", (long long)offset, size);
Mutex::Autolock autoLock(mLock);
if (mDisconnecting) {
@@ -579,7 +579,7 @@
ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
CHECK_LE(size, (size_t)mHighwaterThresholdBytes);
- ALOGV("readInternal offset %lld size %zu", offset, size);
+ ALOGV("readInternal offset %lld size %zu", (long long)offset, size);
Mutex::Autolock autoLock(mLock);
@@ -640,7 +640,7 @@
return OK;
}
- ALOGI("new range: offset= %lld", offset);
+ ALOGI("new range: offset= %lld", (long long)offset);
mCacheOffset = offset;
@@ -719,10 +719,10 @@
mKeepAliveIntervalUs = kDefaultKeepAliveIntervalUs;
}
- ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %" PRId64 " us",
+ ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %lld us",
mLowwaterThresholdBytes,
mHighwaterThresholdBytes,
- mKeepAliveIntervalUs);
+ (long long)mKeepAliveIntervalUs);
}
// static
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 230c1f7..f1ebea2 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -104,6 +104,14 @@
node_id node, OMX_U32 port_index,
sp<IGraphicBufferProducer> *bufferProducer);
+ virtual status_t createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer);
+
+ virtual status_t setInputSurface(
+ node_id node, OMX_U32 port_index,
+ const sp<IGraphicBufferConsumer> &bufferConsumer);
+
virtual status_t signalEndOfInputStream(node_id node);
virtual status_t allocateBuffer(
@@ -340,6 +348,20 @@
return err;
}
+status_t MuxOMX::createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer) {
+ // TODO: local or remote? Always use remote for now
+ return mRemoteOMX->createPersistentInputSurface(
+ bufferProducer, bufferConsumer);
+}
+
+status_t MuxOMX::setInputSurface(
+ node_id node, OMX_U32 port_index,
+ const sp<IGraphicBufferConsumer> &bufferConsumer) {
+ return getOMX(node)->setInputSurface(node, port_index, bufferConsumer);
+}
+
status_t MuxOMX::signalEndOfInputStream(node_id node) {
return getOMX(node)->signalEndOfInputStream(node);
}
@@ -400,10 +422,16 @@
sp<IBinder> binder = sm->getService(String16("media.player"));
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
- CHECK(service.get() != NULL);
+ if (service.get() == NULL) {
+ ALOGE("Cannot obtain IMediaPlayerService");
+ return NO_INIT;
+ }
mOMX = service->getOMX();
- CHECK(mOMX.get() != NULL);
+ if (mOMX.get() == NULL) {
+ ALOGE("Cannot obtain IOMX");
+ return NO_INIT;
+ }
if (!mOMX->livesLocally(0 /* node */, getpid())) {
ALOGI("Using client-side OMX mux.");
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4d30069..aa6a7c0 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -43,6 +43,7 @@
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/SurfaceUtils.h>
#include <media/stagefright/Utils.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <utils/Vector.h>
@@ -1057,7 +1058,7 @@
const sp<MetaData>& meta,
const CodecProfileLevel& defaultProfileLevel,
CodecProfileLevel &profileLevel) {
- CODEC_LOGV("Default profile: %ld, level %ld",
+ CODEC_LOGV("Default profile: %u, level #x%x",
defaultProfileLevel.mProfile, defaultProfileLevel.mLevel);
// Are the default profile and level overwriten?
@@ -1283,7 +1284,7 @@
success = success && meta->findInt32(kKeyHeight, &height);
CHECK(success);
- CODEC_LOGV("setVideoOutputFormat width=%ld, height=%ld", width, height);
+ CODEC_LOGV("setVideoOutputFormat width=%d, height=%d", width, height);
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
@@ -1650,7 +1651,7 @@
return err;
}
- CODEC_LOGV("allocating %lu buffers of size %lu on %s port",
+ CODEC_LOGV("allocating %u buffers of size %u on %s port",
def.nBufferCountActual, def.nBufferSize,
portIndex == kPortIndexInput ? "input" : "output");
@@ -1723,7 +1724,7 @@
mPortBuffers[portIndex].push(info);
- CODEC_LOGV("allocated buffer %p on %s port", buffer,
+ CODEC_LOGV("allocated buffer %u on %s port", buffer,
portIndex == kPortIndexInput ? "input" : "output");
}
@@ -1745,7 +1746,7 @@
if (mSkipCutBuffer != NULL) {
size_t prevbuffersize = mSkipCutBuffer->size();
if (prevbuffersize != 0) {
- ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbuffersize);
+ ALOGW("Replacing SkipCutBuffer holding %zu bytes", prevbuffersize);
}
}
mSkipCutBuffer = new SkipCutBuffer(delay * frameSize, padding * frameSize);
@@ -1783,35 +1784,6 @@
return OK;
}
-status_t OMXCodec::applyRotation() {
- sp<MetaData> meta = mSource->getFormat();
-
- int32_t rotationDegrees;
- if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
- rotationDegrees = 0;
- }
-
- uint32_t transform;
- switch (rotationDegrees) {
- case 0: transform = 0; break;
- case 90: transform = HAL_TRANSFORM_ROT_90; break;
- case 180: transform = HAL_TRANSFORM_ROT_180; break;
- case 270: transform = HAL_TRANSFORM_ROT_270; break;
- default: transform = 0; break;
- }
-
- status_t err = OK;
-
- if (transform) {
- err = native_window_set_buffers_transform(
- mNativeWindow.get(), transform);
- ALOGE("native_window_set_buffers_transform failed: %s (%d)",
- strerror(-err), -err);
- }
-
- return err;
-}
-
status_t OMXCodec::allocateOutputBuffersFromNativeWindow() {
// Get the number of buffers needed.
OMX_PARAM_PORTDEFINITIONTYPE def;
@@ -1825,21 +1797,11 @@
return err;
}
- err = native_window_set_buffers_geometry(
- mNativeWindow.get(),
- def.format.video.nFrameWidth,
- def.format.video.nFrameHeight,
- def.format.video.eColorFormat);
+ sp<MetaData> meta = mSource->getFormat();
- if (err != 0) {
- ALOGE("native_window_set_buffers_geometry failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = applyRotation();
- if (err != OK) {
- return err;
+ int32_t rotationDegrees;
+ if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
+ rotationDegrees = 0;
}
// Set up the native window.
@@ -1850,34 +1812,19 @@
// XXX: Currently this error is logged, but not fatal.
usage = 0;
}
+
if (mFlags & kEnableGrallocUsageProtected) {
usage |= GRALLOC_USAGE_PROTECTED;
}
- // Make sure to check whether either Stagefright or the video decoder
- // requested protected buffers.
- if (usage & GRALLOC_USAGE_PROTECTED) {
- // Verify that the ANativeWindow sends images directly to
- // SurfaceFlinger.
- int queuesToNativeWindow = 0;
- err = mNativeWindow->query(
- mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
- &queuesToNativeWindow);
- if (err != 0) {
- ALOGE("error authenticating native window: %d", err);
- return err;
- }
- if (queuesToNativeWindow != 1) {
- ALOGE("native window could not be authenticated");
- return PERMISSION_DENIED;
- }
- }
-
- ALOGV("native_window_set_usage usage=0x%lx", usage);
- err = native_window_set_usage(
- mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+ err = setNativeWindowSizeFormatAndUsage(
+ mNativeWindow.get(),
+ def.format.video.nFrameWidth,
+ def.format.video.nFrameHeight,
+ def.format.video.eColorFormat,
+ rotationDegrees,
+ usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
if (err != 0) {
- ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
return err;
}
@@ -2044,150 +1991,6 @@
return bufInfo;
}
-status_t OMXCodec::pushBlankBuffersToNativeWindow() {
- status_t err = NO_ERROR;
- ANativeWindowBuffer* anb = NULL;
- int numBufs = 0;
- int minUndequeuedBufs = 0;
-
- // We need to reconnect to the ANativeWindow as a CPU client to ensure that
- // no frames get dropped by SurfaceFlinger assuming that these are video
- // frames.
- err = native_window_api_disconnect(mNativeWindow.get(),
- NATIVE_WINDOW_API_MEDIA);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = native_window_api_connect(mNativeWindow.get(),
- NATIVE_WINDOW_API_CPU);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = native_window_set_buffers_geometry(mNativeWindow.get(), 1, 1,
- HAL_PIXEL_FORMAT_RGBX_8888);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: set_buffers_geometry failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = native_window_set_usage(mNativeWindow.get(),
- GRALLOC_USAGE_SW_WRITE_OFTEN);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: set_usage failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = native_window_set_scaling_mode(mNativeWindow.get(),
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- if (err != OK) {
- ALOGE("error pushing blank frames: set_scaling_mode failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = mNativeWindow->query(mNativeWindow.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query "
- "failed: %s (%d)", strerror(-err), -err);
- goto error;
- }
-
- numBufs = minUndequeuedBufs + 1;
- err = native_window_set_buffer_count(mNativeWindow.get(), numBufs);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- // We push numBufs + 1 buffers to ensure that we've drawn into the same
- // buffer twice. This should guarantee that the buffer has been displayed
- // on the screen and then been replaced, so an previous video frames are
- // guaranteed NOT to be currently displayed.
- for (int i = 0; i < numBufs + 1; i++) {
- err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
-
- // Fill the buffer with the a 1x1 checkerboard pattern ;)
- uint32_t* img = NULL;
- err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: lock failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- *img = 0;
-
- err = buf->unlock();
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: unlock failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- err = mNativeWindow->queueBuffer(mNativeWindow.get(),
- buf->getNativeBuffer(), -1);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)",
- strerror(-err), -err);
- goto error;
- }
-
- anb = NULL;
- }
-
-error:
-
- if (err != NO_ERROR) {
- // Clean up after an error.
- if (anb != NULL) {
- mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1);
- }
-
- native_window_api_disconnect(mNativeWindow.get(),
- NATIVE_WINDOW_API_CPU);
- native_window_api_connect(mNativeWindow.get(),
- NATIVE_WINDOW_API_MEDIA);
-
- return err;
- } else {
- // Clean up after success.
- err = native_window_api_disconnect(mNativeWindow.get(),
- NATIVE_WINDOW_API_CPU);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = native_window_api_connect(mNativeWindow.get(),
- NATIVE_WINDOW_API_MEDIA);
- if (err != NO_ERROR) {
- ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- return NO_ERROR;
- }
-}
-
int64_t OMXCodec::getDecodingTimeUs() {
CHECK(mIsEncoder && mIsVideo);
@@ -2708,7 +2511,7 @@
default:
{
- CODEC_LOGV("CMD_COMPLETE(%d, %ld)", cmd, data);
+ CODEC_LOGV("CMD_COMPLETE(%d, %u)", cmd, data);
break;
}
}
@@ -2734,7 +2537,7 @@
if (countBuffersWeOwn(mPortBuffers[kPortIndexInput]) !=
mPortBuffers[kPortIndexInput].size()) {
ALOGE("Codec did not return all input buffers "
- "(received %d / %d)",
+ "(received %zu / %zu)",
countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
mPortBuffers[kPortIndexInput].size());
TRESPASS();
@@ -2743,7 +2546,7 @@
if (countBuffersWeOwn(mPortBuffers[kPortIndexOutput]) !=
mPortBuffers[kPortIndexOutput].size()) {
ALOGE("Codec did not return all output buffers "
- "(received %d / %d)",
+ "(received %zu / %zu)",
countBuffersWeOwn(mPortBuffers[kPortIndexOutput]),
mPortBuffers[kPortIndexOutput].size());
TRESPASS();
@@ -2769,7 +2572,7 @@
// them has made it to the display. This allows the OMX
// component teardown to zero out any protected buffers
// without the risk of scanning out one of those buffers.
- pushBlankBuffersToNativeWindow();
+ pushBlankBuffersToNativeWindow(mNativeWindow.get());
}
setState(IDLE_TO_LOADED);
@@ -2847,7 +2650,7 @@
CHECK(info->mStatus == OWNED_BY_US
|| info->mStatus == OWNED_BY_NATIVE_WINDOW);
- CODEC_LOGV("freeing buffer %p on port %ld", info->mBuffer, portIndex);
+ CODEC_LOGV("freeing buffer %u on port %u", info->mBuffer, portIndex);
status_t err = freeBuffer(portIndex, i);
@@ -2894,7 +2697,7 @@
}
void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
- CODEC_LOGV("PORT_SETTINGS_CHANGED(%ld)", portIndex);
+ CODEC_LOGV("PORT_SETTINGS_CHANGED(%u)", portIndex);
CHECK(mState == EXECUTING || mState == EXECUTING_TO_IDLE);
CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
@@ -2921,7 +2724,7 @@
CHECK(mState == EXECUTING || mState == RECONFIGURING
|| mState == EXECUTING_TO_IDLE);
- CODEC_LOGV("flushPortAsync(%ld): we own %d out of %d buffers already.",
+ CODEC_LOGV("flushPortAsync(%u): we own %zu out of %zu buffers already.",
portIndex, countBuffersWeOwn(mPortBuffers[portIndex]),
mPortBuffers[portIndex].size());
@@ -2950,7 +2753,7 @@
CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
mPortStatus[portIndex] = DISABLING;
- CODEC_LOGV("sending OMX_CommandPortDisable(%ld)", portIndex);
+ CODEC_LOGV("sending OMX_CommandPortDisable(%u)", portIndex);
status_t err =
mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
CHECK_EQ(err, (status_t)OK);
@@ -2964,7 +2767,7 @@
CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLED);
mPortStatus[portIndex] = ENABLING;
- CODEC_LOGV("sending OMX_CommandPortEnable(%ld)", portIndex);
+ CODEC_LOGV("sending OMX_CommandPortEnable(%u)", portIndex);
return mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
}
@@ -3037,7 +2840,7 @@
if (info->mData == ptr) {
CODEC_LOGV(
- "input buffer data ptr = %p, buffer_id = %p",
+ "input buffer data ptr = %p, buffer_id = %u",
ptr,
info->mBuffer);
@@ -3147,7 +2950,7 @@
if (srcBuffer->meta_data()->findInt64(
kKeyTargetTime, &targetTimeUs)
&& targetTimeUs >= 0) {
- CODEC_LOGV("targetTimeUs = %lld us", targetTimeUs);
+ CODEC_LOGV("targetTimeUs = %lld us", (long long)targetTimeUs);
mTargetTimeUs = targetTimeUs;
} else {
mTargetTimeUs = -1;
@@ -3181,7 +2984,7 @@
if (offset == 0) {
CODEC_LOGE(
"Codec's input buffers are too small to accomodate "
- "buffer read from source (info->mSize = %d, srcLength = %d)",
+ "buffer read from source (info->mSize = %zu, srcLength = %zu)",
info->mSize, srcBuffer->range_length());
srcBuffer->release();
@@ -3287,10 +3090,10 @@
info = findEmptyInputBuffer();
}
- CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d), "
+ CODEC_LOGV("Calling emptyBuffer on buffer %u (length %zu), "
"timestamp %lld us (%.2f secs)",
info->mBuffer, offset,
- timestampUs, timestampUs / 1E6);
+ (long long)timestampUs, timestampUs / 1E6);
err = mOMX->emptyBuffer(
mNode, info->mBuffer, 0, offset,
@@ -3315,7 +3118,7 @@
return;
}
- CODEC_LOGV("Calling fillBuffer on buffer %p", info->mBuffer);
+ CODEC_LOGV("Calling fillBuffer on buffer %u", info->mBuffer);
status_t err = mOMX->fillBuffer(mNode, info->mBuffer);
if (err != OK) {
@@ -3372,7 +3175,7 @@
}
status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutNs);
if (err != OK) {
- CODEC_LOGE("Timed out waiting for output buffers: %d/%d",
+ CODEC_LOGE("Timed out waiting for output buffers: %zu/%zu",
countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
countBuffersWeOwn(mPortBuffers[kPortIndexOutput]));
}
@@ -3627,7 +3430,7 @@
void OMXCodec::setImageOutputFormat(
OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) {
- CODEC_LOGV("setImageOutputFormat(%ld, %ld)", width, height);
+ CODEC_LOGV("setImageOutputFormat(%u, %u)", width, height);
#if 0
OMX_INDEXTYPE index;
@@ -4281,14 +4084,14 @@
if ((OMX_U32)numChannels != params.nChannels) {
ALOGV("Codec outputs a different number of channels than "
"the input stream contains (contains %d channels, "
- "codec outputs %ld channels).",
+ "codec outputs %u channels).",
numChannels, params.nChannels);
}
if (sampleRate != (int32_t)params.nSamplingRate) {
ALOGV("Codec outputs at different sampling rate than "
"what the input stream contains (contains data at "
- "%d Hz, codec outputs %lu Hz)",
+ "%d Hz, codec outputs %u Hz)",
sampleRate, params.nSamplingRate);
}
@@ -4390,8 +4193,7 @@
mNode, OMX_IndexConfigCommonOutputCrop,
&rect, sizeof(rect));
- CODEC_LOGI(
- "video dimensions are %ld x %ld",
+ CODEC_LOGI("video dimensions are %u x %u",
video_def->nFrameWidth, video_def->nFrameHeight);
if (err == OK) {
@@ -4409,8 +4211,7 @@
rect.nLeft + rect.nWidth - 1,
rect.nTop + rect.nHeight - 1);
- CODEC_LOGI(
- "Crop rect is %ld x %ld @ (%ld, %ld)",
+ CODEC_LOGI("Crop rect is %u x %u @ (%d, %d)",
rect.nWidth, rect.nHeight, rect.nLeft, rect.nTop);
} else {
mOutputFormat->setRect(
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 6e32494..4297549 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -250,7 +250,7 @@
if (!memcmp(signature, "OggS", 4)) {
if (*pageOffset > startOffset) {
ALOGV("skipped %lld bytes of junk to reach next frame",
- *pageOffset - startOffset);
+ (long long)(*pageOffset - startOffset));
}
return OK;
@@ -277,7 +277,7 @@
prevGuess = 0;
}
- ALOGV("backing up %lld bytes", pageOffset - prevGuess);
+ ALOGV("backing up %lld bytes", (long long)(pageOffset - prevGuess));
status_t err = findNextPage(prevGuess, &prevPageOffset);
if (err != OK) {
@@ -295,7 +295,7 @@
}
ALOGV("prevPageOffset at %lld, pageOffset at %lld",
- prevPageOffset, pageOffset);
+ (long long)prevPageOffset, (long long)pageOffset);
for (;;) {
Page prevPage;
@@ -320,7 +320,7 @@
off64_t pos = timeUs * approxBitrate() / 8000000ll;
- ALOGV("seeking to offset %lld", pos);
+ ALOGV("seeking to offset %lld", (long long)pos);
return seekToOffset(pos);
}
@@ -348,7 +348,7 @@
const TOCEntry &entry = mTableOfContents.itemAt(left);
ALOGV("seeking to entry %zu / %zu at offset %lld",
- left, mTableOfContents.size(), entry.mPageOffset);
+ left, mTableOfContents.size(), (long long)entry.mPageOffset);
return seekToOffset(entry.mPageOffset);
}
@@ -391,8 +391,8 @@
ssize_t n;
if ((n = mSource->readAt(offset, header, sizeof(header)))
< (ssize_t)sizeof(header)) {
- ALOGV("failed to read %zu bytes at offset 0x%016llx, got %zd bytes",
- sizeof(header), offset, n);
+ ALOGV("failed to read %zu bytes at offset %#016llx, got %zd bytes",
+ sizeof(header), (long long)offset, n);
if (n < 0) {
return n;
@@ -505,8 +505,8 @@
packetSize);
if (n < (ssize_t)packetSize) {
- ALOGV("failed to read %zu bytes at 0x%016llx, got %zd bytes",
- packetSize, dataOffset, n);
+ ALOGV("failed to read %zu bytes at %#016llx, got %zd bytes",
+ packetSize, (long long)dataOffset, n);
return ERROR_IO;
}
@@ -753,7 +753,9 @@
oggpack_buffer bits;
oggpack_readinit(&bits, &ref);
- CHECK_EQ(oggpack_read(&bits, 8), type);
+ if (oggpack_read(&bits, 8) != type) {
+ return ERROR_MALFORMED;
+ }
for (size_t i = 0; i < 6; ++i) {
oggpack_read(&bits, 8); // skip 'vorbis'
}
@@ -761,7 +763,9 @@
switch (type) {
case 1:
{
- CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits));
+ if (0 != _vorbis_unpack_info(&mVi, &bits)) {
+ return ERROR_MALFORMED;
+ }
mMeta->setData(kKeyVorbisInfo, 0, data, size);
mMeta->setInt32(kKeySampleRate, mVi.rate);
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index bdd6d56..7f98485 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -230,8 +230,13 @@
return ERROR_MALFORMED;
}
+ if (SIZE_MAX / sizeof(SampleToChunkEntry) <= (size_t)mNumSampleToChunkOffsets)
+ return ERROR_OUT_OF_RANGE;
+
mSampleToChunkEntries =
- new SampleToChunkEntry[mNumSampleToChunkOffsets];
+ new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets];
+ if (!mSampleToChunkEntries)
+ return ERROR_OUT_OF_RANGE;
for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
uint8_t buffer[12];
@@ -330,11 +335,13 @@
}
mTimeToSampleCount = U32_AT(&header[4]);
- uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
+ uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t)sizeof(uint32_t);
if (allocSize > SIZE_MAX) {
return ERROR_OUT_OF_RANGE;
}
- mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
+ mTimeToSample = new (std::nothrow) uint32_t[mTimeToSampleCount * 2];
+ if (!mTimeToSample)
+ return ERROR_OUT_OF_RANGE;
size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
if (mDataSource->readAt(
@@ -376,12 +383,14 @@
}
mNumCompositionTimeDeltaEntries = numEntries;
- uint64_t allocSize = numEntries * 2 * sizeof(uint32_t);
+ uint64_t allocSize = numEntries * 2 * (uint64_t)sizeof(uint32_t);
if (allocSize > SIZE_MAX) {
return ERROR_OUT_OF_RANGE;
}
- mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries];
+ mCompositionTimeDeltaEntries = new (std::nothrow) uint32_t[2 * numEntries];
+ if (!mCompositionTimeDeltaEntries)
+ return ERROR_OUT_OF_RANGE;
if (mDataSource->readAt(
data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
@@ -426,12 +435,15 @@
ALOGV("Table of sync samples is empty or has only a single entry!");
}
- uint64_t allocSize = mNumSyncSamples * sizeof(uint32_t);
+ uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t);
if (allocSize > SIZE_MAX) {
return ERROR_OUT_OF_RANGE;
}
- mSyncSamples = new uint32_t[mNumSyncSamples];
+ mSyncSamples = new (std::nothrow) uint32_t[mNumSyncSamples];
+ if (!mSyncSamples)
+ return ERROR_OUT_OF_RANGE;
+
size_t size = mNumSyncSamples * sizeof(uint32_t);
if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
!= (ssize_t)size) {
@@ -499,7 +511,9 @@
return;
}
- mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes];
+ mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
+ if (!mSampleTimeEntries)
+ return;
uint32_t sampleIndex = 0;
uint32_t sampleTime = 0;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 101fc8a..e9566f2 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -47,10 +47,7 @@
StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
ALOGV("~StagefrightMetadataRetriever()");
-
- delete mAlbumArt;
- mAlbumArt = NULL;
-
+ clearMetadata();
mClient.disconnect();
}
@@ -60,11 +57,7 @@
const KeyedVector<String8, String8> *headers) {
ALOGV("setDataSource(%s)", uri);
- mParsedMetaData = false;
- mMetaData.clear();
- delete mAlbumArt;
- mAlbumArt = NULL;
-
+ clearMetadata();
mSource = DataSource::CreateFromURI(httpService, uri, headers);
if (mSource == NULL) {
@@ -92,11 +85,7 @@
ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
- mParsedMetaData = false;
- mMetaData.clear();
- delete mAlbumArt;
- mAlbumArt = NULL;
-
+ clearMetadata();
mSource = new FileSource(fd, offset, length);
status_t err;
@@ -117,6 +106,23 @@
return OK;
}
+status_t StagefrightMetadataRetriever::setDataSource(
+ const sp<DataSource>& source) {
+ ALOGV("setDataSource(DataSource)");
+
+ clearMetadata();
+ mSource = source;
+ mExtractor = MediaExtractor::Create(mSource);
+
+ if (mExtractor == NULL) {
+ ALOGE("Failed to instantiate a MediaExtractor.");
+ mSource.clear();
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
static bool isYUV420PlanarSupported(
OMXClient *client,
const sp<MetaData> &trackMeta) {
@@ -519,6 +525,12 @@
mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
+ float captureFps;
+ if (meta->findFloat(kKeyCaptureFramerate, &captureFps)) {
+ sprintf(tmp, "%f", captureFps);
+ mMetaData.add(METADATA_KEY_CAPTURE_FRAMERATE, String8(tmp));
+ }
+
bool hasAudio = false;
bool hasVideo = false;
int32_t videoWidth = -1;
@@ -629,4 +641,11 @@
}
}
+void StagefrightMetadataRetriever::clearMetadata() {
+ mParsedMetaData = false;
+ mMetaData.clear();
+ delete mAlbumArt;
+ mAlbumArt = NULL;
+}
+
} // namespace android
diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp
new file mode 100644
index 0000000..6b62e43
--- /dev/null
+++ b/media/libstagefright/SurfaceUtils.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceUtils"
+#include <utils/Log.h>
+
+#include <media/stagefright/SurfaceUtils.h>
+
+#include <gui/Surface.h>
+
+namespace android {
+
+status_t setNativeWindowSizeFormatAndUsage(
+ ANativeWindow *nativeWindow /* nonnull */,
+ int width, int height, int format, int rotation, int usage) {
+ status_t err = native_window_set_buffers_dimensions(nativeWindow, width, height);
+ if (err != NO_ERROR) {
+ ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = native_window_set_buffers_format(nativeWindow, format);
+ if (err != NO_ERROR) {
+ ALOGE("native_window_set_buffers_format failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ int transform = 0;
+ if ((rotation % 90) == 0) {
+ switch ((rotation / 90) & 3) {
+ case 1: transform = HAL_TRANSFORM_ROT_90; break;
+ case 2: transform = HAL_TRANSFORM_ROT_180; break;
+ case 3: transform = HAL_TRANSFORM_ROT_270; break;
+ default: transform = 0; break;
+ }
+ }
+
+ err = native_window_set_buffers_transform(nativeWindow, transform);
+ if (err != NO_ERROR) {
+ ALOGE("native_window_set_buffers_transform failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ // Make sure to check whether either Stagefright or the video decoder
+ // requested protected buffers.
+ if (usage & GRALLOC_USAGE_PROTECTED) {
+ // Verify that the ANativeWindow sends images directly to
+ // SurfaceFlinger.
+ int queuesToNativeWindow = 0;
+ err = nativeWindow->query(
+ nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &queuesToNativeWindow);
+ if (err != NO_ERROR) {
+ ALOGE("error authenticating native window: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+ if (queuesToNativeWindow != 1) {
+ ALOGE("native window could not be authenticated");
+ return PERMISSION_DENIED;
+ }
+ }
+
+ int consumerUsage = 0;
+ err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage);
+ if (err != NO_ERROR) {
+ ALOGW("failed to get consumer usage bits. ignoring");
+ err = NO_ERROR;
+ }
+
+ int finalUsage = usage | consumerUsage;
+ ALOGV("gralloc usage: %#x(producer) + %#x(consumer) = %#x", usage, consumerUsage, finalUsage);
+ err = native_window_set_usage(nativeWindow, finalUsage);
+ if (err != NO_ERROR) {
+ ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = native_window_set_scaling_mode(
+ nativeWindow, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ if (err != NO_ERROR) {
+ ALOGE("native_window_set_scaling_mode failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ ALOGD("set up nativeWindow %p for %dx%d, color %#x, rotation %d, usage %#x",
+ nativeWindow, width, height, format, rotation, finalUsage);
+ return NO_ERROR;
+}
+
+status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */) {
+ status_t err = NO_ERROR;
+ ANativeWindowBuffer* anb = NULL;
+ int numBufs = 0;
+ int minUndequeuedBufs = 0;
+
+ // We need to reconnect to the ANativeWindow as a CPU client to ensure that
+ // no frames get dropped by SurfaceFlinger assuming that these are video
+ // frames.
+ err = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_CPU);
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err);
+ (void)native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+ return err;
+ }
+
+ err = setNativeWindowSizeFormatAndUsage(
+ nativeWindow, 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ if (err != NO_ERROR) {
+ goto error;
+ }
+
+ static_cast<Surface*>(nativeWindow)->getIGraphicBufferProducer()->allowAllocation(true);
+
+ err = nativeWindow->query(nativeWindow,
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query "
+ "failed: %s (%d)", strerror(-err), -err);
+ goto error;
+ }
+
+ numBufs = minUndequeuedBufs + 1;
+ err = native_window_set_buffer_count(nativeWindow, numBufs);
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", strerror(-err), -err);
+ goto error;
+ }
+
+ // We push numBufs + 1 buffers to ensure that we've drawn into the same
+ // buffer twice. This should guarantee that the buffer has been displayed
+ // on the screen and then been replaced, so an previous video frames are
+ // guaranteed NOT to be currently displayed.
+ for (int i = 0; i < numBufs + 1; i++) {
+ err = native_window_dequeue_buffer_and_wait(nativeWindow, &anb);
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
+ strerror(-err), -err);
+ break;
+ }
+
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+
+ // Fill the buffer with the a 1x1 checkerboard pattern ;)
+ uint32_t *img = NULL;
+ err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: lock failed: %s (%d)", strerror(-err), -err);
+ break;
+ }
+
+ *img = 0;
+
+ err = buf->unlock();
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: unlock failed: %s (%d)", strerror(-err), -err);
+ break;
+ }
+
+ err = nativeWindow->queueBuffer(nativeWindow, buf->getNativeBuffer(), -1);
+ if (err != NO_ERROR) {
+ ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", strerror(-err), -err);
+ break;
+ }
+
+ anb = NULL;
+ }
+
+error:
+
+ if (anb != NULL) {
+ nativeWindow->cancelBuffer(nativeWindow, anb, -1);
+ anb = NULL;
+ }
+
+ // Clean up after success or error.
+ status_t err2 = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_CPU);
+ if (err2 != NO_ERROR) {
+ ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err2), -err2);
+ if (err == NO_ERROR) {
+ err = err2;
+ }
+ }
+
+ err2 = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+ if (err2 != NO_ERROR) {
+ ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err);
+ if (err == NO_ERROR) {
+ err = err2;
+ }
+ }
+
+ return err;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 8506e37..413628d 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -166,11 +166,26 @@
msg->setInt32("max-input-size", maxInputSize);
}
+ int32_t maxWidth;
+ if (meta->findInt32(kKeyMaxWidth, &maxWidth)) {
+ msg->setInt32("max-width", maxWidth);
+ }
+
+ int32_t maxHeight;
+ if (meta->findInt32(kKeyMaxHeight, &maxHeight)) {
+ msg->setInt32("max-height", maxHeight);
+ }
+
int32_t rotationDegrees;
if (meta->findInt32(kKeyRotation, &rotationDegrees)) {
msg->setInt32("rotation-degrees", rotationDegrees);
}
+ int32_t fps;
+ if (meta->findInt32(kKeyFrameRate, &fps)) {
+ msg->setInt32("frame-rate", fps);
+ }
+
uint32_t type;
const void *data;
size_t size;
@@ -568,6 +583,21 @@
meta->setInt32(kKeyMaxInputSize, maxInputSize);
}
+ int32_t maxWidth;
+ if (msg->findInt32("max-width", &maxWidth)) {
+ meta->setInt32(kKeyMaxWidth, maxWidth);
+ }
+
+ int32_t maxHeight;
+ if (msg->findInt32("max-height", &maxHeight)) {
+ meta->setInt32(kKeyMaxHeight, maxHeight);
+ }
+
+ int32_t fps;
+ if (msg->findInt32("frame-rate", &fps)) {
+ meta->setInt32(kKeyFrameRate, fps);
+ }
+
// reassemble the csd data into its original form
sp<ABuffer> csd0;
if (msg->findBuffer("csd-0", &csd0)) {
@@ -832,14 +862,32 @@
}
}
-int64_t HLSTime::getSegmentTimeUs(bool midpoint) const {
+int64_t HLSTime::getSegmentTimeUs() const {
int64_t segmentStartTimeUs = -1ll;
if (mMeta != NULL) {
CHECK(mMeta->findInt64("segmentStartTimeUs", &segmentStartTimeUs));
- if (midpoint) {
+
+ int64_t segmentFirstTimeUs;
+ if (mMeta->findInt64("segmentFirstTimeUs", &segmentFirstTimeUs)) {
+ segmentStartTimeUs += mTimeUs - segmentFirstTimeUs;
+ }
+
+ // adjust segment time by playlist age (for live streaming)
+ int64_t playlistTimeUs;
+ if (mMeta->findInt64("playlistTimeUs", &playlistTimeUs)) {
+ int64_t playlistAgeUs = ALooper::GetNowUs() - playlistTimeUs;
+
int64_t durationUs;
CHECK(mMeta->findInt64("segmentDurationUs", &durationUs));
- segmentStartTimeUs += durationUs / 2;
+
+ // round to nearest whole segment
+ playlistAgeUs = (playlistAgeUs + durationUs / 2)
+ / durationUs * durationUs;
+
+ segmentStartTimeUs -= playlistAgeUs;
+ if (segmentStartTimeUs < 0) {
+ segmentStartTimeUs = 0;
+ }
}
}
return segmentStartTimeUs;
@@ -853,5 +901,39 @@
|| (t0.mSeq == t1.mSeq && t0.mTimeUs < t1.mTimeUs);
}
+void writeToAMessage(sp<AMessage> msg, const AudioPlaybackRate &rate) {
+ msg->setFloat("speed", rate.mSpeed);
+ msg->setFloat("pitch", rate.mPitch);
+ msg->setInt32("audio-fallback-mode", rate.mFallbackMode);
+ msg->setInt32("audio-stretch-mode", rate.mStretchMode);
+}
+
+void readFromAMessage(const sp<AMessage> &msg, AudioPlaybackRate *rate /* nonnull */) {
+ *rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+ CHECK(msg->findFloat("speed", &rate->mSpeed));
+ CHECK(msg->findFloat("pitch", &rate->mPitch));
+ CHECK(msg->findInt32("audio-fallback-mode", (int32_t *)&rate->mFallbackMode));
+ CHECK(msg->findInt32("audio-stretch-mode", (int32_t *)&rate->mStretchMode));
+}
+
+void writeToAMessage(sp<AMessage> msg, const AVSyncSettings &sync, float videoFpsHint) {
+ msg->setInt32("sync-source", sync.mSource);
+ msg->setInt32("audio-adjust-mode", sync.mAudioAdjustMode);
+ msg->setFloat("tolerance", sync.mTolerance);
+ msg->setFloat("video-fps", videoFpsHint);
+}
+
+void readFromAMessage(
+ const sp<AMessage> &msg,
+ AVSyncSettings *sync /* nonnull */,
+ float *videoFps /* nonnull */) {
+ AVSyncSettings settings;
+ CHECK(msg->findInt32("sync-source", (int32_t *)&settings.mSource));
+ CHECK(msg->findInt32("audio-adjust-mode", (int32_t *)&settings.mAudioAdjustMode));
+ CHECK(msg->findFloat("tolerance", &settings.mTolerance));
+ CHECK(msg->findFloat("video-fps", videoFps));
+ *sync = settings;
+}
+
} // namespace android
diff --git a/media/libstagefright/VBRISeeker.cpp b/media/libstagefright/VBRISeeker.cpp
index e988f6d..8a0fcac 100644
--- a/media/libstagefright/VBRISeeker.cpp
+++ b/media/libstagefright/VBRISeeker.cpp
@@ -122,7 +122,7 @@
seeker->mSegments.push(numBytes);
- ALOGV("entry #%zu: %u offset 0x%016llx", i, numBytes, offset);
+ ALOGV("entry #%zu: %u offset %#016llx", i, numBytes, (long long)offset);
offset += numBytes;
}
@@ -163,7 +163,7 @@
*pos += mSegments.itemAt(segmentIndex++);
}
- ALOGV("getOffsetForTime %" PRId64 " us => 0x%016llx", *timeUs, *pos);
+ ALOGV("getOffsetForTime %lld us => 0x%016llx", (long long)*timeUs, (long long)*pos);
*timeUs = nowUs;
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 10937ec..965c55e 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -75,7 +75,7 @@
SoftAAC2::~SoftAAC2() {
aacDecoder_Close(mAACDecoder);
- delete mOutputDelayRingBuffer;
+ delete[] mOutputDelayRingBuffer;
}
void SoftAAC2::initPorts() {
diff --git a/media/libstagefright/codecs/avcdec/Android.mk b/media/libstagefright/codecs/avcdec/Android.mk
new file mode 100644
index 0000000..902ab57
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/Android.mk
@@ -0,0 +1,27 @@
+#ifeq ($(if $(wildcard external/libh264),1,0),1)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libstagefright_soft_avcdec
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_LIBRARIES := libavcdec
+LOCAL_SRC_FILES := SoftAVCDec.cpp
+
+LOCAL_C_INCLUDES := $(TOP)/external/libavc/decoder
+LOCAL_C_INCLUDES += $(TOP)/external/libavc/common
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libstagefright_omx
+LOCAL_SHARED_LIBRARIES += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_SHARED_LIBRARY)
+
+#endif
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
new file mode 100644
index 0000000..08e956a
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -0,0 +1,808 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftAVCDec"
+#include <utils/Log.h>
+
+#include "ih264_typedefs.h"
+#include "iv.h"
+#include "ivd.h"
+#include "ithread.h"
+#include "ih264d.h"
+#include "SoftAVCDec.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <OMX_VideoExt.h>
+
+namespace android {
+
+#define PRINT_TIME ALOGV
+
+#define componentName "video_decoder.avc"
+#define codingType OMX_VIDEO_CodingAVC
+#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_AVC
+
+/** Function and structure definitions to keep code similar for each codec */
+#define ivdec_api_function ih264d_api_function
+#define ivdext_init_ip_t ih264d_init_ip_t
+#define ivdext_init_op_t ih264d_init_op_t
+#define ivdext_fill_mem_rec_ip_t ih264d_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t ih264d_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t ih264d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t ih264d_ctl_set_num_cores_op_t
+
+#define IVDEXT_CMD_CTL_SET_NUM_CORES \
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
+
+static const CodecProfileLevel kProfileLevels[] = {
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
+
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1b },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel11 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel12 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel13 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel2 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel21 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel22 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel3 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel31 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel32 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel4 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel42 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel5 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel51 },
+ { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel52 },
+
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1b },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel11 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel12 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel13 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel2 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel21 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel22 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel3 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel31 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel32 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel4 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel41 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel42 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel5 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel51 },
+ { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel52 },
+};
+
+SoftAVC::SoftAVC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SoftVideoDecoderOMXComponent(
+ name, componentName, codingType,
+ kProfileLevels, ARRAY_SIZE(kProfileLevels),
+ 320 /* width */, 240 /* height */, callbacks,
+ appData, component),
+ mMemRecords(NULL),
+ mFlushOutBuffer(NULL),
+ mOmxColorFormat(OMX_COLOR_FormatYUV420Planar),
+ mIvColorFormat(IV_YUV_420P),
+ mNewWidth(mWidth),
+ mNewHeight(mHeight),
+ mChangingResolution(false) {
+ initPorts(
+ kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE);
+
+ GETTIME(&mTimeStart, NULL);
+
+ // If input dump is enabled, then open create an empty file
+ GENERATE_FILE_NAMES();
+ CREATE_DUMP_FILE(mInFile);
+
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAVC::~SoftAVC() {
+ CHECK_EQ(deInitDecoder(), (status_t)OK);
+}
+
+static size_t GetCPUCoreCount() {
+ long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGD("Number of CPU cores: %ld", cpuCoreCount);
+ return (size_t)cpuCoreCount;
+}
+
+void SoftAVC::logVersion() {
+ ivd_ctl_getversioninfo_ip_t s_ctl_ip;
+ ivd_ctl_getversioninfo_op_t s_ctl_op;
+ UWORD8 au1_buf[512];
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+ s_ctl_ip.pv_version_buffer = au1_buf;
+ s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf);
+
+ status =
+ ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in getting version number: 0x%x",
+ s_ctl_op.u4_error_code);
+ } else {
+ ALOGV("Ittiam decoder version number: %s",
+ (char *)s_ctl_ip.pv_version_buffer);
+ }
+ return;
+}
+
+status_t SoftAVC::setParams(size_t stride) {
+ ivd_ctl_set_config_ip_t s_ctl_ip;
+ ivd_ctl_set_config_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+ s_ctl_ip.u4_disp_wd = (UWORD32)stride;
+ s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+
+ s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+
+ ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the run-time parameters: 0x%x",
+ s_ctl_op.u4_error_code);
+
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t SoftAVC::resetPlugin() {
+ mIsInFlush = false;
+ mReceivedEOS = false;
+ memset(mTimeStamps, 0, sizeof(mTimeStamps));
+ memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
+
+ /* Initialize both start and end times */
+ gettimeofday(&mTimeStart, NULL);
+ gettimeofday(&mTimeEnd, NULL);
+
+ return OK;
+}
+
+status_t SoftAVC::resetDecoder() {
+ ivd_ctl_reset_ip_t s_ctl_ip;
+ ivd_ctl_reset_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ /* Set the run-time (dynamic) parameters */
+ setParams(outputBufferWidth());
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ return OK;
+}
+
+status_t SoftAVC::setNumCores() {
+ ivdext_ctl_set_num_cores_ip_t s_set_cores_ip;
+ ivdext_ctl_set_num_cores_op_t s_set_cores_op;
+ IV_API_CALL_STATUS_T status;
+ s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+ s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES);
+ s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+ s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in setting number of cores: 0x%x",
+ s_set_cores_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t SoftAVC::setFlushMode() {
+ IV_API_CALL_STATUS_T status;
+ ivd_ctl_flush_ip_t s_video_flush_ip;
+ ivd_ctl_flush_op_t s_video_flush_op;
+
+ s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+ s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+ s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+
+ /* Set the decoder in Flush mode, subsequent decode() calls will flush */
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status,
+ s_video_flush_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ mIsInFlush = true;
+ return OK;
+}
+
+status_t SoftAVC::initDecoder() {
+ IV_API_CALL_STATUS_T status;
+
+ UWORD32 u4_num_reorder_frames;
+ UWORD32 u4_num_ref_frames;
+ UWORD32 u4_share_disp_buf;
+ WORD32 i4_level;
+
+ mNumCores = GetCPUCoreCount();
+
+ /* Initialize number of ref and reorder modes (for H264) */
+ u4_num_reorder_frames = 16;
+ u4_num_ref_frames = 16;
+ u4_share_disp_buf = 0;
+
+ uint32_t displayStride = outputBufferWidth();
+ uint32_t displayHeight = outputBufferHeight();
+ uint32_t displaySizeY = displayStride * displayHeight;
+
+ if (displaySizeY > (1920 * 1088)) {
+ i4_level = 50;
+ } else if (displaySizeY > (1280 * 720)) {
+ i4_level = 40;
+ } else if (displaySizeY > (720 * 576)) {
+ i4_level = 31;
+ } else if (displaySizeY > (624 * 320)) {
+ i4_level = 30;
+ } else if (displaySizeY > (352 * 288)) {
+ i4_level = 21;
+ } else {
+ i4_level = 20;
+ }
+
+ {
+ iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+ iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+ s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+ s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+ s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+ ALOGV("Get number of mem records");
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_num_mem_rec_ip, (void *)&s_num_mem_rec_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in getting mem records: 0x%x",
+ s_num_mem_rec_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+ }
+
+ mMemRecords = (iv_mem_rec_t *)ivd_aligned_malloc(
+ 128, mNumMemRecords * sizeof(iv_mem_rec_t));
+ if (mMemRecords == NULL) {
+ ALOGE("Allocation failure");
+ return NO_MEMORY;
+ }
+
+ memset(mMemRecords, 0, mNumMemRecords * sizeof(iv_mem_rec_t));
+
+ {
+ size_t i;
+ ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+ ivdext_fill_mem_rec_op_t s_fill_mem_op;
+ iv_mem_rec_t *ps_mem_rec;
+
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size =
+ sizeof(ivdext_fill_mem_rec_ip_t);
+ s_fill_mem_ip.i4_level = i4_level;
+ s_fill_mem_ip.u4_num_reorder_frames = u4_num_reorder_frames;
+ s_fill_mem_ip.u4_num_ref_frames = u4_num_ref_frames;
+ s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf;
+ s_fill_mem_ip.u4_num_extra_disp_buf = 0;
+ s_fill_mem_ip.e_output_format = mIvColorFormat;
+
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = displayStride;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = displayHeight;
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size =
+ sizeof(ivdext_fill_mem_rec_op_t);
+
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < mNumMemRecords; i++) {
+ ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+ }
+
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_fill_mem_ip, (void *)&s_fill_mem_op);
+
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in filling mem records: 0x%x",
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ mNumMemRecords =
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled;
+
+ ps_mem_rec = mMemRecords;
+
+ for (i = 0; i < mNumMemRecords; i++) {
+ ps_mem_rec->pv_base = ivd_aligned_malloc(
+ ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+ if (ps_mem_rec->pv_base == NULL) {
+ ALOGE("Allocation failure for memory record #%zu of size %u",
+ i, ps_mem_rec->u4_mem_size);
+ status = IV_FAIL;
+ return NO_MEMORY;
+ }
+
+ ps_mem_rec++;
+ }
+ }
+
+ /* Initialize the decoder */
+ {
+ ivdext_init_ip_t s_init_ip;
+ ivdext_init_op_t s_init_op;
+
+ void *dec_fxns = (void *)ivdec_api_function;
+
+ s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+ s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+ s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = displayStride;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = displayHeight;
+
+ s_init_ip.i4_level = i4_level;
+ s_init_ip.u4_num_reorder_frames = u4_num_reorder_frames;
+ s_init_ip.u4_num_ref_frames = u4_num_ref_frames;
+ s_init_ip.u4_share_disp_buf = u4_share_disp_buf;
+ s_init_ip.u4_num_extra_disp_buf = 0;
+
+ s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op);
+
+ s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+ s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat;
+
+ mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+ mCodecCtx->pv_fxns = dec_fxns;
+ mCodecCtx->u4_size = sizeof(iv_obj_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, (void *)&s_init_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in init: 0x%x",
+ s_init_op.s_ivd_init_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ /* Reset the plugin state */
+ resetPlugin();
+
+ /* Set the run time (dynamic) parameters */
+ setParams(displayStride);
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ /* Get codec version */
+ logVersion();
+
+ /* Allocate internal picture buffer */
+ uint32_t bufferSize = displaySizeY * 3 / 2;
+ mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
+ if (NULL == mFlushOutBuffer) {
+ ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
+ return NO_MEMORY;
+ }
+
+ mInitNeeded = false;
+ mFlushNeeded = false;
+ return OK;
+}
+
+status_t SoftAVC::deInitDecoder() {
+ size_t i;
+
+ if (mMemRecords) {
+ iv_mem_rec_t *ps_mem_rec;
+
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < mNumMemRecords; i++) {
+ if (ps_mem_rec->pv_base) {
+ ivd_aligned_free(ps_mem_rec->pv_base);
+ }
+ ps_mem_rec++;
+ }
+ ivd_aligned_free(mMemRecords);
+ mMemRecords = NULL;
+ }
+
+ if (mFlushOutBuffer) {
+ ivd_aligned_free(mFlushOutBuffer);
+ mFlushOutBuffer = NULL;
+ }
+
+ mInitNeeded = true;
+ mChangingResolution = false;
+
+ return OK;
+}
+
+status_t SoftAVC::reInitDecoder() {
+ status_t ret;
+
+ deInitDecoder();
+
+ ret = initDecoder();
+ if (OK != ret) {
+ ALOGE("Create failure");
+ deInitDecoder();
+ return NO_MEMORY;
+ }
+ return OK;
+}
+
+void SoftAVC::onReset() {
+ SoftVideoDecoderOMXComponent::onReset();
+
+ resetDecoder();
+ resetPlugin();
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) {
+ const uint32_t oldWidth = mWidth;
+ const uint32_t oldHeight = mHeight;
+ OMX_ERRORTYPE ret = SoftVideoDecoderOMXComponent::internalSetParameter(index, params);
+ if (mWidth != oldWidth || mHeight != oldHeight) {
+ reInitDecoder();
+ }
+ return ret;
+}
+
+void SoftAVC::setDecodeArgs(
+ ivd_video_decode_ip_t *ps_dec_ip,
+ ivd_video_decode_op_t *ps_dec_op,
+ OMX_BUFFERHEADERTYPE *inHeader,
+ OMX_BUFFERHEADERTYPE *outHeader,
+ size_t timeStampIx) {
+ size_t sizeY = outputBufferWidth() * outputBufferHeight();
+ size_t sizeUV;
+ uint8_t *pBuf;
+
+ ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+ ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+ ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+
+ /* When in flush and after EOS with zero byte input,
+ * inHeader is set to zero. Hence check for non-null */
+ if (inHeader) {
+ ps_dec_ip->u4_ts = timeStampIx;
+ ps_dec_ip->pv_stream_buffer =
+ inHeader->pBuffer + inHeader->nOffset;
+ ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen;
+ } else {
+ ps_dec_ip->u4_ts = 0;
+ ps_dec_ip->pv_stream_buffer = NULL;
+ ps_dec_ip->u4_num_Bytes = 0;
+ }
+
+ if (outHeader) {
+ pBuf = outHeader->pBuffer;
+ } else {
+ pBuf = mFlushOutBuffer;
+ }
+
+ sizeUV = sizeY / 4;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV;
+
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf;
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY;
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV;
+ ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
+ return;
+}
+void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) {
+ /* Once the output buffers are flushed, ignore any buffers that are held in decoder */
+ if (kOutputPortIndex == portIndex) {
+ setFlushMode();
+
+ while (true) {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ IV_API_CALL_STATUS_T status;
+ size_t sizeY, sizeUV;
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ if (0 == s_dec_op.u4_output_present) {
+ resetPlugin();
+ break;
+ }
+ }
+ }
+}
+
+void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+ UNUSED(portIndex);
+
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
+ List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
+
+ /* If input EOS is seen and decoder is not in flush mode,
+ * set the decoder in flush mode.
+ * There can be a case where EOS is sent along with last picture data
+ * In that case, only after decoding that input data, decoder has to be
+ * put in flush. This case is handled here */
+
+ if (mReceivedEOS && !mIsInFlush) {
+ setFlushMode();
+ }
+
+ while (!outQueue.empty()) {
+ BufferInfo *inInfo;
+ OMX_BUFFERHEADERTYPE *inHeader;
+
+ BufferInfo *outInfo;
+ OMX_BUFFERHEADERTYPE *outHeader;
+ size_t timeStampIx;
+
+ inInfo = NULL;
+ inHeader = NULL;
+
+ if (!mIsInFlush) {
+ if (!inQueue.empty()) {
+ inInfo = *inQueue.begin();
+ inHeader = inInfo->mHeader;
+ } else {
+ break;
+ }
+ }
+
+ outInfo = *outQueue.begin();
+ outHeader = outInfo->mHeader;
+ outHeader->nFlags = 0;
+ outHeader->nTimeStamp = 0;
+ outHeader->nOffset = 0;
+
+ if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
+ mReceivedEOS = true;
+ if (inHeader->nFilledLen == 0) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ setFlushMode();
+ }
+ }
+
+ // When there is an init required and the decoder is not in flush mode,
+ // update output port's definition and reinitialize decoder.
+ if (mInitNeeded && !mIsInFlush) {
+ bool portWillReset = false;
+ handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight);
+
+ CHECK_EQ(reInitDecoder(), (status_t)OK);
+ return;
+ }
+
+ /* Get a free slot in timestamp array to hold input timestamp */
+ {
+ size_t i;
+ timeStampIx = 0;
+ for (i = 0; i < MAX_TIME_STAMPS; i++) {
+ if (!mTimeStampsValid[i]) {
+ timeStampIx = i;
+ break;
+ }
+ }
+ if (inHeader != NULL) {
+ mTimeStampsValid[timeStampIx] = true;
+ mTimeStamps[timeStampIx] = inHeader->nTimeStamp;
+ }
+ }
+
+ {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ WORD32 timeDelay, timeTaken;
+ size_t sizeY, sizeUV;
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+ // If input dump is enabled, then write to file
+ DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes);
+
+ GETTIME(&mTimeStart, NULL);
+ /* Compute time elapsed between end of previous decode()
+ * to start of current decode() */
+ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+ IV_API_CALL_STATUS_T status;
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+
+ bool unsupportedDimensions =
+ (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF));
+ bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+
+ GETTIME(&mTimeEnd, NULL);
+ /* Compute time taken for decode() */
+ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+ PRINT_TIME("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+ s_dec_op.u4_num_bytes_consumed);
+ if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
+ mFlushNeeded = true;
+ }
+
+ if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) {
+ /* If the input did not contain picture data, then ignore
+ * the associated timestamp */
+ mTimeStampsValid[timeStampIx] = false;
+ }
+
+ // This is needed to handle CTS DecoderTest testCodecResetsH264WithoutSurface,
+ // which is not sending SPS/PPS after port reconfiguration and flush to the codec.
+ if (unsupportedDimensions && !mFlushNeeded) {
+ bool portWillReset = false;
+ handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht);
+
+ CHECK_EQ(reInitDecoder(), (status_t)OK);
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+
+ ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ return;
+ }
+
+ // If the decoder is in the changing resolution mode and there is no output present,
+ // that means the switching is done and it's ready to reset the decoder and the plugin.
+ if (mChangingResolution && !s_dec_op.u4_output_present) {
+ mChangingResolution = false;
+ resetDecoder();
+ resetPlugin();
+ continue;
+ }
+
+ if (unsupportedDimensions || resChanged) {
+ mChangingResolution = true;
+ if (mFlushNeeded) {
+ setFlushMode();
+ }
+
+ if (unsupportedDimensions) {
+ mNewWidth = s_dec_op.u4_pic_wd;
+ mNewHeight = s_dec_op.u4_pic_ht;
+ mInitNeeded = true;
+ }
+ continue;
+ }
+
+ if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
+ uint32_t width = s_dec_op.u4_pic_wd;
+ uint32_t height = s_dec_op.u4_pic_ht;
+ bool portWillReset = false;
+ handlePortSettingsChange(&portWillReset, width, height);
+
+ if (portWillReset) {
+ resetDecoder();
+ return;
+ }
+ }
+
+ if (s_dec_op.u4_output_present) {
+ outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+
+ outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts];
+ mTimeStampsValid[s_dec_op.u4_ts] = false;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ } else {
+ /* If in flush mode and no output is returned by the codec,
+ * then come out of flush mode */
+ mIsInFlush = false;
+
+ /* If EOS was recieved on input port and there is no output
+ * from the codec, then signal EOS on output port */
+ if (mReceivedEOS) {
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ resetPlugin();
+ }
+ }
+ }
+
+ if (inHeader != NULL) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData,
+ OMX_COMPONENTTYPE **component) {
+ return new android::SoftAVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
new file mode 100644
index 0000000..191a71d
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFT_H264_DEC_H_
+
+#define SOFT_H264_DEC_H_
+
+#include "SoftVideoDecoderOMXComponent.h"
+#include <sys/time.h>
+
+namespace android {
+
+#define ivd_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ivd_aligned_free(buf) free(buf)
+
+/** Number of entries in the time-stamp array */
+#define MAX_TIME_STAMPS 64
+
+/** Maximum number of cores supported by the codec */
+#define CODEC_MAX_NUM_CORES 4
+
+#define CODEC_MAX_WIDTH 1920
+
+#define CODEC_MAX_HEIGHT 1088
+
+/** Input buffer size */
+#define INPUT_BUF_SIZE (1024 * 1024)
+
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+ diff = ((end.tv_sec - start.tv_sec) * 1000000) + \
+ (end.tv_usec - start.tv_usec);
+
+struct SoftAVC : public SoftVideoDecoderOMXComponent {
+ SoftAVC(const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAVC();
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onReset();
+ virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params);
+private:
+ // Number of input and output buffers
+ enum {
+ kNumBuffers = 8
+ };
+
+ iv_obj_t *mCodecCtx; // Codec context
+ iv_mem_rec_t *mMemRecords; // Memory records requested by the codec
+ size_t mNumMemRecords; // Number of memory records requested by the codec
+
+ size_t mNumCores; // Number of cores to be uesd by the codec
+
+ struct timeval mTimeStart; // Time at the start of decode()
+ struct timeval mTimeEnd; // Time at the end of decode()
+
+ // Internal buffer to be used to flush out the buffers from decoder
+ uint8_t *mFlushOutBuffer;
+
+ // Status of entries in the timestamp array
+ bool mTimeStampsValid[MAX_TIME_STAMPS];
+
+ // Timestamp array - Since codec does not take 64 bit timestamps,
+ // they are maintained in the plugin
+ OMX_S64 mTimeStamps[MAX_TIME_STAMPS];
+
+#ifdef FILE_DUMP_ENABLE
+ char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+ OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format
+ IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format
+
+ bool mIsInFlush; // codec is flush mode
+ bool mReceivedEOS; // EOS is receieved on input port
+ bool mInitNeeded;
+ uint32_t mNewWidth;
+ uint32_t mNewHeight;
+ // The input stream has changed to a different resolution, which is still supported by the
+ // codec. So the codec is switching to decode the new resolution.
+ bool mChangingResolution;
+ bool mFlushNeeded;
+
+ status_t initDecoder();
+ status_t deInitDecoder();
+ status_t setFlushMode();
+ status_t setParams(size_t stride);
+ void logVersion();
+ status_t setNumCores();
+ status_t resetDecoder();
+ status_t resetPlugin();
+ status_t reInitDecoder();
+
+ void setDecodeArgs(
+ ivd_video_decode_ip_t *ps_dec_ip,
+ ivd_video_decode_op_t *ps_dec_op,
+ OMX_BUFFERHEADERTYPE *inHeader,
+ OMX_BUFFERHEADERTYPE *outHeader,
+ size_t timeStampIx);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH "/sdcard/media/avcd_input"
+#define INPUT_DUMP_EXT "h264"
+
+#define GENERATE_FILE_NAMES() { \
+ GETTIME(&mTimeStart, NULL); \
+ strcpy(mInFile, ""); \
+ sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \
+ mTimeStart.tv_sec, mTimeStart.tv_usec, \
+ INPUT_DUMP_EXT); \
+}
+
+#define CREATE_DUMP_FILE(m_filename) { \
+ FILE *fp = fopen(m_filename, "wb"); \
+ if (fp != NULL) { \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not open file %s", m_filename); \
+ } \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size) \
+{ \
+ FILE *fp = fopen(m_filename, "ab"); \
+ if (fp != NULL && m_buf != NULL) { \
+ int i; \
+ i = fwrite(m_buf, 1, m_size, fp); \
+ ALOGD("fwrite ret %d to write %d", i, m_size); \
+ if (i != (int) m_size) { \
+ ALOGD("Error in fwrite, returned %d", i); \
+ perror("Error in write to file"); \
+ } \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not write to file %s", m_filename);\
+ } \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+} // namespace android
+
+#endif // SOFT_H264_DEC_H_
diff --git a/media/libstagefright/codecs/avcenc/Android.mk b/media/libstagefright/codecs/avcenc/Android.mk
new file mode 100644
index 0000000..24a4db9
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/Android.mk
@@ -0,0 +1,30 @@
+#ifeq ($(if $(wildcard external/libh264),1,0),1)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libstagefright_soft_avcenc
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_LIBRARIES := libavcenc
+LOCAL_SRC_FILES := SoftAVCEnc.cpp
+
+LOCAL_C_INCLUDES := $(TOP)/external/libavc/encoder
+LOCAL_C_INCLUDES += $(TOP)/external/libavc/common
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/hardware
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libstagefright_omx
+LOCAL_SHARED_LIBRARIES += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_SHARED_LIBRARY)
+
+#endif
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
new file mode 100755
index 0000000..6afac74
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -0,0 +1,1335 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftAVCEnc"
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "OMX_Video.h"
+
+#include <HardwareAPI.h>
+#include <MetadataBufferType.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <ui/Rect.h>
+
+#include "ih264_typedefs.h"
+#include "iv2.h"
+#include "ive2.h"
+#include "ih264e.h"
+#include "SoftAVCEnc.h"
+
+namespace android {
+
+ #define ive_api_function ih264e_api_function
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+typedef struct LevelConversion {
+ OMX_VIDEO_AVCLEVELTYPE omxLevel;
+ WORD32 avcLevel;
+} LevelConcersion;
+
+static LevelConversion ConversionTable[] = {
+ { OMX_VIDEO_AVCLevel1, 10 },
+ { OMX_VIDEO_AVCLevel1b, 9 },
+ { OMX_VIDEO_AVCLevel11, 11 },
+ { OMX_VIDEO_AVCLevel12, 12 },
+ { OMX_VIDEO_AVCLevel13, 13 },
+ { OMX_VIDEO_AVCLevel2, 20 },
+ { OMX_VIDEO_AVCLevel21, 21 },
+ { OMX_VIDEO_AVCLevel22, 22 },
+ { OMX_VIDEO_AVCLevel3, 30 },
+ { OMX_VIDEO_AVCLevel31, 31 },
+ { OMX_VIDEO_AVCLevel32, 32 },
+ { OMX_VIDEO_AVCLevel4, 40 },
+ { OMX_VIDEO_AVCLevel41, 41 },
+ { OMX_VIDEO_AVCLevel42, 42 },
+ { OMX_VIDEO_AVCLevel5, 50 },
+ { OMX_VIDEO_AVCLevel51, 51 },
+};
+
+static const CodecProfileLevel kProfileLevels[] = {
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4 },
+ { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+};
+
+
+static size_t GetCPUCoreCount() {
+ long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGD("Number of CPU cores: %ld", cpuCoreCount);
+ return (size_t)cpuCoreCount;
+}
+
+static status_t ConvertOmxAvcLevelToAvcSpecLevel(
+ OMX_VIDEO_AVCLEVELTYPE omxLevel, WORD32 *avcLevel) {
+ for (size_t i = 0; i < NELEM(ConversionTable); ++i) {
+ if (omxLevel == ConversionTable[i].omxLevel) {
+ *avcLevel = ConversionTable[i].avcLevel;
+ return OK;
+ }
+ }
+
+ ALOGE("ConvertOmxAvcLevelToAvcSpecLevel: %d level not supported",
+ (int32_t)omxLevel);
+
+ return BAD_VALUE;
+}
+
+static status_t ConvertAvcSpecLevelToOmxAvcLevel(
+ WORD32 avcLevel, OMX_VIDEO_AVCLEVELTYPE *omxLevel) {
+ for (size_t i = 0; i < NELEM(ConversionTable); ++i) {
+ if (avcLevel == ConversionTable[i].avcLevel) {
+ *omxLevel = ConversionTable[i].omxLevel;
+ return OK;
+ }
+ }
+
+ ALOGE("ConvertAvcSpecLevelToOmxAvcLevel: %d level not supported",
+ (int32_t)avcLevel);
+
+ return BAD_VALUE;
+}
+
+
+SoftAVC::SoftAVC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SoftVideoEncoderOMXComponent(
+ name, "video_encoder.avc", OMX_VIDEO_CodingAVC,
+ kProfileLevels, NELEM(kProfileLevels),
+ 176 /* width */, 144 /* height */,
+ callbacks, appData, component),
+ mIvVideoColorFormat(IV_YUV_420P),
+ mIDRFrameRefreshIntervalInSec(1),
+ mAVCEncProfile(IV_PROFILE_BASE),
+ mAVCEncLevel(31),
+ mPrevTimestampUs(-1),
+ mStarted(false),
+ mSawInputEOS(false),
+ mSignalledError(false),
+ mConversionBuffer(NULL),
+ mCodecCtx(NULL) {
+
+ initPorts(kNumBuffers, kNumBuffers, ((mWidth * mHeight * 3) >> 1),
+ MEDIA_MIMETYPE_VIDEO_AVC, 2);
+
+ // If dump is enabled, then open create an empty file
+ GENERATE_FILE_NAMES();
+ CREATE_DUMP_FILE(mInFile);
+ CREATE_DUMP_FILE(mOutFile);
+
+}
+
+SoftAVC::~SoftAVC() {
+ releaseEncoder();
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ CHECK(outQueue.empty());
+ CHECK(inQueue.empty());
+}
+
+OMX_ERRORTYPE SoftAVC::initEncParams() {
+ mCodecCtx = NULL;
+ mMemRecords = NULL;
+ mNumMemRecords = DEFAULT_MEM_REC_CNT;
+ mHeaderGenerated = 0;
+ mNumCores = GetCPUCoreCount();
+ mArch = DEFAULT_ARCH;
+ mSliceMode = DEFAULT_SLICE_MODE;
+ mSliceParam = DEFAULT_SLICE_PARAM;
+ mHalfPelEnable = DEFAULT_HPEL;
+ mIInterval = DEFAULT_I_INTERVAL;
+ mIDRInterval = DEFAULT_IDR_INTERVAL;
+ mDisableDeblkLevel = DEFAULT_DISABLE_DEBLK_LEVEL;
+ mFrameRate = DEFAULT_SRC_FRAME_RATE;
+ mEnableFastSad = DEFAULT_ENABLE_FAST_SAD;
+ mEnableAltRef = DEFAULT_ENABLE_ALT_REF;
+ mEncSpeed = DEFAULT_ENC_SPEED;
+ mIntra4x4 = DEFAULT_INTRA4x4;
+ mAIRMode = DEFAULT_AIR;
+ mAIRRefreshPeriod = DEFAULT_AIR_REFRESH_PERIOD;
+ mPSNREnable = DEFAULT_PSNR_ENABLE;
+ mReconEnable = DEFAULT_RECON_ENABLE;
+
+ gettimeofday(&mTimeStart, NULL);
+ gettimeofday(&mTimeEnd, NULL);
+
+ return OMX_ErrorNone;
+}
+
+
+OMX_ERRORTYPE SoftAVC::setDimensions() {
+ ive_ctl_set_dimensions_ip_t s_dimensions_ip;
+ ive_ctl_set_dimensions_op_t s_dimensions_op;
+ IV_STATUS_T status;
+
+ s_dimensions_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_dimensions_ip.e_sub_cmd = IVE_CMD_CTL_SET_DIMENSIONS;
+ s_dimensions_ip.u4_ht = mHeight;
+ s_dimensions_ip.u4_wd = mWidth;
+ s_dimensions_ip.u4_strd = mStride;
+
+ s_dimensions_ip.u4_timestamp_high = -1;
+ s_dimensions_ip.u4_timestamp_low = -1;
+
+ s_dimensions_ip.u4_size = sizeof(ive_ctl_set_dimensions_ip_t);
+ s_dimensions_op.u4_size = sizeof(ive_ctl_set_dimensions_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_dimensions_ip, &s_dimensions_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set frame dimensions = 0x%x\n",
+ s_dimensions_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setNumCores() {
+ IV_STATUS_T status;
+ ive_ctl_set_num_cores_ip_t s_num_cores_ip;
+ ive_ctl_set_num_cores_op_t s_num_cores_op;
+ s_num_cores_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_num_cores_ip.e_sub_cmd = IVE_CMD_CTL_SET_NUM_CORES;
+ s_num_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_CORES);
+ s_num_cores_ip.u4_timestamp_high = -1;
+ s_num_cores_ip.u4_timestamp_low = -1;
+ s_num_cores_ip.u4_size = sizeof(ive_ctl_set_num_cores_ip_t);
+
+ s_num_cores_op.u4_size = sizeof(ive_ctl_set_num_cores_op_t);
+
+ status = ive_api_function(
+ mCodecCtx, (void *) &s_num_cores_ip, (void *) &s_num_cores_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set processor params = 0x%x\n",
+ s_num_cores_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setFrameRate() {
+ ive_ctl_set_frame_rate_ip_t s_frame_rate_ip;
+ ive_ctl_set_frame_rate_op_t s_frame_rate_op;
+ IV_STATUS_T status;
+
+ s_frame_rate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_frame_rate_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMERATE;
+
+ s_frame_rate_ip.u4_src_frame_rate = mFrameRate;
+ s_frame_rate_ip.u4_tgt_frame_rate = mFrameRate;
+
+ s_frame_rate_ip.u4_timestamp_high = -1;
+ s_frame_rate_ip.u4_timestamp_low = -1;
+
+ s_frame_rate_ip.u4_size = sizeof(ive_ctl_set_frame_rate_ip_t);
+ s_frame_rate_op.u4_size = sizeof(ive_ctl_set_frame_rate_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_frame_rate_ip, &s_frame_rate_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set frame rate = 0x%x\n",
+ s_frame_rate_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setIpeParams() {
+ ive_ctl_set_ipe_params_ip_t s_ipe_params_ip;
+ ive_ctl_set_ipe_params_op_t s_ipe_params_op;
+ IV_STATUS_T status;
+
+ s_ipe_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_ipe_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_IPE_PARAMS;
+
+ s_ipe_params_ip.u4_enable_intra_4x4 = mIntra4x4;
+ s_ipe_params_ip.u4_enc_speed_preset = mEncSpeed;
+
+ s_ipe_params_ip.u4_timestamp_high = -1;
+ s_ipe_params_ip.u4_timestamp_low = -1;
+
+ s_ipe_params_ip.u4_size = sizeof(ive_ctl_set_ipe_params_ip_t);
+ s_ipe_params_op.u4_size = sizeof(ive_ctl_set_ipe_params_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_ipe_params_ip, &s_ipe_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set ipe params = 0x%x\n",
+ s_ipe_params_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setBitRate() {
+ ive_ctl_set_bitrate_ip_t s_bitrate_ip;
+ ive_ctl_set_bitrate_op_t s_bitrate_op;
+ IV_STATUS_T status;
+
+ s_bitrate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_bitrate_ip.e_sub_cmd = IVE_CMD_CTL_SET_BITRATE;
+
+ s_bitrate_ip.u4_target_bitrate = mBitrate;
+
+ s_bitrate_ip.u4_timestamp_high = -1;
+ s_bitrate_ip.u4_timestamp_low = -1;
+
+ s_bitrate_ip.u4_size = sizeof(ive_ctl_set_bitrate_ip_t);
+ s_bitrate_op.u4_size = sizeof(ive_ctl_set_bitrate_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_bitrate_ip, &s_bitrate_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set bit rate = 0x%x\n", s_bitrate_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setFrameType(IV_PICTURE_CODING_TYPE_T e_frame_type) {
+ ive_ctl_set_frame_type_ip_t s_frame_type_ip;
+ ive_ctl_set_frame_type_op_t s_frame_type_op;
+ IV_STATUS_T status;
+
+ s_frame_type_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_frame_type_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMETYPE;
+
+ s_frame_type_ip.e_frame_type = e_frame_type;
+
+ s_frame_type_ip.u4_timestamp_high = -1;
+ s_frame_type_ip.u4_timestamp_low = -1;
+
+ s_frame_type_ip.u4_size = sizeof(ive_ctl_set_frame_type_ip_t);
+ s_frame_type_op.u4_size = sizeof(ive_ctl_set_frame_type_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_frame_type_ip, &s_frame_type_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set frame type = 0x%x\n",
+ s_frame_type_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setQp() {
+ ive_ctl_set_qp_ip_t s_qp_ip;
+ ive_ctl_set_qp_op_t s_qp_op;
+ IV_STATUS_T status;
+
+ s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
+
+ s_qp_ip.u4_i_qp = DEFAULT_I_QP;
+ s_qp_ip.u4_i_qp_max = DEFAULT_QP_MAX;
+ s_qp_ip.u4_i_qp_min = DEFAULT_QP_MIN;
+
+ s_qp_ip.u4_p_qp = DEFAULT_P_QP;
+ s_qp_ip.u4_p_qp_max = DEFAULT_QP_MAX;
+ s_qp_ip.u4_p_qp_min = DEFAULT_QP_MIN;
+
+ s_qp_ip.u4_b_qp = DEFAULT_P_QP;
+ s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX;
+ s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN;
+
+ s_qp_ip.u4_timestamp_high = -1;
+ s_qp_ip.u4_timestamp_low = -1;
+
+ s_qp_ip.u4_size = sizeof(ive_ctl_set_qp_ip_t);
+ s_qp_op.u4_size = sizeof(ive_ctl_set_qp_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_qp_ip, &s_qp_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set qp 0x%x\n", s_qp_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setEncMode(IVE_ENC_MODE_T e_enc_mode) {
+ IV_STATUS_T status;
+ ive_ctl_set_enc_mode_ip_t s_enc_mode_ip;
+ ive_ctl_set_enc_mode_op_t s_enc_mode_op;
+
+ s_enc_mode_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_enc_mode_ip.e_sub_cmd = IVE_CMD_CTL_SET_ENC_MODE;
+
+ s_enc_mode_ip.e_enc_mode = e_enc_mode;
+
+ s_enc_mode_ip.u4_timestamp_high = -1;
+ s_enc_mode_ip.u4_timestamp_low = -1;
+
+ s_enc_mode_ip.u4_size = sizeof(ive_ctl_set_enc_mode_ip_t);
+ s_enc_mode_op.u4_size = sizeof(ive_ctl_set_enc_mode_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_enc_mode_ip, &s_enc_mode_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set in header encode mode = 0x%x\n",
+ s_enc_mode_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setVbvParams() {
+ ive_ctl_set_vbv_params_ip_t s_vbv_ip;
+ ive_ctl_set_vbv_params_op_t s_vbv_op;
+ IV_STATUS_T status;
+
+ s_vbv_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_vbv_ip.e_sub_cmd = IVE_CMD_CTL_SET_VBV_PARAMS;
+
+ s_vbv_ip.u4_vbv_buf_size = 0;
+ s_vbv_ip.u4_vbv_buffer_delay = 1000;
+
+ s_vbv_ip.u4_timestamp_high = -1;
+ s_vbv_ip.u4_timestamp_low = -1;
+
+ s_vbv_ip.u4_size = sizeof(ive_ctl_set_vbv_params_ip_t);
+ s_vbv_op.u4_size = sizeof(ive_ctl_set_vbv_params_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_vbv_ip, &s_vbv_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set VBC params = 0x%x\n", s_vbv_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setAirParams() {
+ ive_ctl_set_air_params_ip_t s_air_ip;
+ ive_ctl_set_air_params_op_t s_air_op;
+ IV_STATUS_T status;
+
+ s_air_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_air_ip.e_sub_cmd = IVE_CMD_CTL_SET_AIR_PARAMS;
+
+ s_air_ip.e_air_mode = mAIRMode;
+ s_air_ip.u4_air_refresh_period = mAIRRefreshPeriod;
+
+ s_air_ip.u4_timestamp_high = -1;
+ s_air_ip.u4_timestamp_low = -1;
+
+ s_air_ip.u4_size = sizeof(ive_ctl_set_air_params_ip_t);
+ s_air_op.u4_size = sizeof(ive_ctl_set_air_params_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_air_ip, &s_air_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set air params = 0x%x\n", s_air_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setMeParams() {
+ IV_STATUS_T status;
+ ive_ctl_set_me_params_ip_t s_me_params_ip;
+ ive_ctl_set_me_params_op_t s_me_params_op;
+
+ s_me_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_me_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_ME_PARAMS;
+
+ s_me_params_ip.u4_enable_fast_sad = mEnableFastSad;
+ s_me_params_ip.u4_enable_alt_ref = mEnableAltRef;
+
+ s_me_params_ip.u4_enable_hpel = mHalfPelEnable;
+ s_me_params_ip.u4_enable_qpel = DEFAULT_QPEL;
+ s_me_params_ip.u4_me_speed_preset = DEFAULT_ME_SPEED;
+ s_me_params_ip.u4_srch_rng_x = DEFAULT_SRCH_RNG_X;
+ s_me_params_ip.u4_srch_rng_y = DEFAULT_SRCH_RNG_Y;
+
+ s_me_params_ip.u4_timestamp_high = -1;
+ s_me_params_ip.u4_timestamp_low = -1;
+
+ s_me_params_ip.u4_size = sizeof(ive_ctl_set_me_params_ip_t);
+ s_me_params_op.u4_size = sizeof(ive_ctl_set_me_params_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_me_params_ip, &s_me_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set me params = 0x%x\n", s_me_params_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setGopParams() {
+ IV_STATUS_T status;
+ ive_ctl_set_gop_params_ip_t s_gop_params_ip;
+ ive_ctl_set_gop_params_op_t s_gop_params_op;
+
+ s_gop_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_gop_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_GOP_PARAMS;
+
+ s_gop_params_ip.u4_i_frm_interval = mIInterval;
+ s_gop_params_ip.u4_idr_frm_interval = mIDRInterval;
+ s_gop_params_ip.u4_num_b_frames = DEFAULT_B_FRAMES;
+
+ s_gop_params_ip.u4_timestamp_high = -1;
+ s_gop_params_ip.u4_timestamp_low = -1;
+
+ s_gop_params_ip.u4_size = sizeof(ive_ctl_set_gop_params_ip_t);
+ s_gop_params_op.u4_size = sizeof(ive_ctl_set_gop_params_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_gop_params_ip, &s_gop_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set ME params = 0x%x\n",
+ s_gop_params_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setProfileParams() {
+ IV_STATUS_T status;
+ ive_ctl_set_profile_params_ip_t s_profile_params_ip;
+ ive_ctl_set_profile_params_op_t s_profile_params_op;
+
+ s_profile_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_profile_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_PROFILE_PARAMS;
+
+ s_profile_params_ip.e_profile = DEFAULT_EPROFILE;
+
+ s_profile_params_ip.u4_timestamp_high = -1;
+ s_profile_params_ip.u4_timestamp_low = -1;
+
+ s_profile_params_ip.u4_size = sizeof(ive_ctl_set_profile_params_ip_t);
+ s_profile_params_op.u4_size = sizeof(ive_ctl_set_profile_params_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_profile_params_ip, &s_profile_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to set profile params = 0x%x\n",
+ s_profile_params_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setDeblockParams() {
+ IV_STATUS_T status;
+ ive_ctl_set_deblock_params_ip_t s_deblock_params_ip;
+ ive_ctl_set_deblock_params_op_t s_deblock_params_op;
+
+ s_deblock_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_deblock_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_DEBLOCK_PARAMS;
+
+ s_deblock_params_ip.u4_disable_deblock_level = mDisableDeblkLevel;
+
+ s_deblock_params_ip.u4_timestamp_high = -1;
+ s_deblock_params_ip.u4_timestamp_low = -1;
+
+ s_deblock_params_ip.u4_size = sizeof(ive_ctl_set_deblock_params_ip_t);
+ s_deblock_params_op.u4_size = sizeof(ive_ctl_set_deblock_params_op_t);
+
+ status = ive_api_function(mCodecCtx, &s_deblock_params_ip, &s_deblock_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to enable/disable deblock params = 0x%x\n",
+ s_deblock_params_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+ return OMX_ErrorNone;
+}
+
+void SoftAVC::logVersion() {
+ ive_ctl_getversioninfo_ip_t s_ctl_ip;
+ ive_ctl_getversioninfo_op_t s_ctl_op;
+ UWORD8 au1_buf[512];
+ IV_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVE_CMD_CTL_GETVERSION;
+ s_ctl_ip.u4_size = sizeof(ive_ctl_getversioninfo_ip_t);
+ s_ctl_op.u4_size = sizeof(ive_ctl_getversioninfo_op_t);
+ s_ctl_ip.pu1_version = au1_buf;
+ s_ctl_ip.u4_version_bufsize = sizeof(au1_buf);
+
+ status = ive_api_function(mCodecCtx, (void *) &s_ctl_ip, (void *) &s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in getting version: 0x%x", s_ctl_op.u4_error_code);
+ } else {
+ ALOGV("Ittiam encoder version: %s", (char *)s_ctl_ip.pu1_version);
+ }
+ return;
+}
+
+OMX_ERRORTYPE SoftAVC::initEncoder() {
+ IV_STATUS_T status;
+ size_t i;
+ WORD32 level;
+ uint32_t displaySizeY;
+ CHECK(!mStarted);
+
+ OMX_ERRORTYPE errType = OMX_ErrorNone;
+
+ displaySizeY = mWidth * mHeight;
+ if (displaySizeY > (1920 * 1088)) {
+ level = 50;
+ } else if (displaySizeY > (1280 * 720)) {
+ level = 40;
+ } else if (displaySizeY > (720 * 576)) {
+ level = 31;
+ } else if (displaySizeY > (624 * 320)) {
+ level = 30;
+ } else if (displaySizeY > (352 * 288)) {
+ level = 21;
+ } else {
+ level = 20;
+ }
+ mAVCEncLevel = MAX(level, mAVCEncLevel);
+
+ if (OMX_ErrorNone != (errType = initEncParams())) {
+ ALOGE("Failed to initialize encoder params");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return errType;
+ }
+
+ mStride = mWidth;
+
+ if (mInputDataIsMeta) {
+ if (mConversionBuffer) {
+ free(mConversionBuffer);
+ mConversionBuffer = NULL;
+ }
+
+ if (mConversionBuffer == NULL) {
+ mConversionBuffer = (uint8_t *)malloc(mStride * mHeight * 3 / 2);
+ if (mConversionBuffer == NULL) {
+ ALOGE("Allocating conversion buffer failed.");
+ return OMX_ErrorUndefined;
+ }
+ }
+ }
+
+ switch (mColorFormat) {
+ case OMX_COLOR_FormatYUV420SemiPlanar:
+ mIvVideoColorFormat = IV_YUV_420SP_UV;
+ ALOGV("colorFormat YUV_420SP");
+ break;
+ default:
+ case OMX_COLOR_FormatYUV420Planar:
+ mIvVideoColorFormat = IV_YUV_420P;
+ ALOGV("colorFormat YUV_420P");
+ break;
+ }
+
+ ALOGV("Params width %d height %d level %d colorFormat %d", mWidth,
+ mHeight, mAVCEncLevel, mIvVideoColorFormat);
+
+ /* Getting Number of MemRecords */
+ {
+ iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+ iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+ s_num_mem_rec_ip.u4_size = sizeof(iv_num_mem_rec_ip_t);
+ s_num_mem_rec_op.u4_size = sizeof(iv_num_mem_rec_op_t);
+
+ s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+ status = ive_api_function(0, &s_num_mem_rec_ip, &s_num_mem_rec_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Get number of memory records failed = 0x%x\n",
+ s_num_mem_rec_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+
+ mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+ }
+
+ /* Allocate array to hold memory records */
+ mMemRecords = (iv_mem_rec_t *)malloc(mNumMemRecords * sizeof(iv_mem_rec_t));
+ if (NULL == mMemRecords) {
+ ALOGE("Unable to allocate memory for hold memory records: Size %zu",
+ mNumMemRecords * sizeof(iv_mem_rec_t));
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return OMX_ErrorUndefined;
+ }
+
+ {
+ iv_mem_rec_t *ps_mem_rec;
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < mNumMemRecords; i++) {
+ ps_mem_rec->u4_size = sizeof(iv_mem_rec_t);
+ ps_mem_rec->pv_base = NULL;
+ ps_mem_rec->u4_mem_size = 0;
+ ps_mem_rec->u4_mem_alignment = 0;
+ ps_mem_rec->e_mem_type = IV_NA_MEM_TYPE;
+
+ ps_mem_rec++;
+ }
+ }
+
+ /* Getting MemRecords Attributes */
+ {
+ iv_fill_mem_rec_ip_t s_fill_mem_rec_ip;
+ iv_fill_mem_rec_op_t s_fill_mem_rec_op;
+
+ s_fill_mem_rec_ip.u4_size = sizeof(iv_fill_mem_rec_ip_t);
+ s_fill_mem_rec_op.u4_size = sizeof(iv_fill_mem_rec_op_t);
+
+ s_fill_mem_rec_ip.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+ s_fill_mem_rec_ip.ps_mem_rec = mMemRecords;
+ s_fill_mem_rec_ip.u4_num_mem_rec = mNumMemRecords;
+ s_fill_mem_rec_ip.u4_max_wd = mWidth;
+ s_fill_mem_rec_ip.u4_max_ht = mHeight;
+ s_fill_mem_rec_ip.u4_max_level = mAVCEncLevel;
+ s_fill_mem_rec_ip.e_color_format = DEFAULT_INP_COLOR_FORMAT;
+ s_fill_mem_rec_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+ s_fill_mem_rec_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+ s_fill_mem_rec_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+ s_fill_mem_rec_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+
+ status = ive_api_function(0, &s_fill_mem_rec_ip, &s_fill_mem_rec_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Fill memory records failed = 0x%x\n",
+ s_fill_mem_rec_op.u4_error_code);
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ /* Allocating Memory for Mem Records */
+ {
+ WORD32 total_size;
+ iv_mem_rec_t *ps_mem_rec;
+ total_size = 0;
+
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < mNumMemRecords; i++) {
+ ps_mem_rec->pv_base = ive_aligned_malloc(
+ ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+ if (ps_mem_rec->pv_base == NULL) {
+ ALOGE("Allocation failure for mem record id %zu size %u\n", i,
+ ps_mem_rec->u4_mem_size);
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return OMX_ErrorUndefined;
+
+ }
+ total_size += ps_mem_rec->u4_mem_size;
+
+ ps_mem_rec++;
+ }
+ printf("\nTotal memory for codec %d\n", total_size);
+ }
+
+ /* Codec Instance Creation */
+ {
+ ive_init_ip_t s_init_ip;
+ ive_init_op_t s_init_op;
+
+ mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+ mCodecCtx->u4_size = sizeof(iv_obj_t);
+ mCodecCtx->pv_fxns = (void *)ive_api_function;
+
+ s_init_ip.u4_size = sizeof(ive_init_ip_t);
+ s_init_op.u4_size = sizeof(ive_init_op_t);
+
+ s_init_ip.e_cmd = IV_CMD_INIT;
+ s_init_ip.u4_num_mem_rec = mNumMemRecords;
+ s_init_ip.ps_mem_rec = mMemRecords;
+ s_init_ip.u4_max_wd = mWidth;
+ s_init_ip.u4_max_ht = mHeight;
+ s_init_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+ s_init_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+ s_init_ip.u4_max_level = mAVCEncLevel;
+ s_init_ip.e_inp_color_fmt = mIvVideoColorFormat;
+
+ if (mReconEnable || mPSNREnable) {
+ s_init_ip.u4_enable_recon = 1;
+ } else {
+ s_init_ip.u4_enable_recon = 0;
+ }
+ s_init_ip.e_recon_color_fmt = DEFAULT_RECON_COLOR_FORMAT;
+ s_init_ip.e_rc_mode = DEFAULT_RC_MODE;
+ s_init_ip.u4_max_framerate = DEFAULT_MAX_FRAMERATE;
+ s_init_ip.u4_max_bitrate = DEFAULT_MAX_BITRATE;
+ s_init_ip.u4_max_num_bframes = DEFAULT_B_FRAMES;
+ s_init_ip.e_content_type = IV_PROGRESSIVE;
+ s_init_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+ s_init_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+ s_init_ip.e_slice_mode = mSliceMode;
+ s_init_ip.u4_slice_param = mSliceParam;
+ s_init_ip.e_arch = mArch;
+ s_init_ip.e_soc = DEFAULT_SOC;
+
+ status = ive_api_function(mCodecCtx, &s_init_ip, &s_init_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Init memory records failed = 0x%x\n",
+ s_init_op.u4_error_code);
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0 /* arg2 */, NULL /* data */);
+ return OMX_ErrorUndefined;
+ }
+ }
+
+ /* Get Codec Version */
+ logVersion();
+
+ /* set processor details */
+ setNumCores();
+
+ /* Video control Set Frame dimensions */
+ setDimensions();
+
+ /* Video control Set Frame rates */
+ setFrameRate();
+
+ /* Video control Set IPE Params */
+ setIpeParams();
+
+ /* Video control Set Bitrate */
+ setBitRate();
+
+ /* Video control Set QP */
+ setQp();
+
+ /* Video control Set AIR params */
+ setAirParams();
+
+ /* Video control Set VBV params */
+ setVbvParams();
+
+ /* Video control Set Motion estimation params */
+ setMeParams();
+
+ /* Video control Set GOP params */
+ setGopParams();
+
+ /* Video control Set Deblock params */
+ setDeblockParams();
+
+ /* Video control Set Profile params */
+ setProfileParams();
+
+ /* Video control Set in Encode header mode */
+ setEncMode(IVE_ENC_MODE_HEADER);
+
+ ALOGV("init_codec successfull");
+
+ mSpsPpsHeaderReceived = false;
+ mStarted = true;
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::releaseEncoder() {
+ IV_STATUS_T status = IV_SUCCESS;
+ iv_retrieve_mem_rec_ip_t s_retrieve_mem_ip;
+ iv_retrieve_mem_rec_op_t s_retrieve_mem_op;
+ iv_mem_rec_t *ps_mem_rec;
+ UWORD32 i;
+
+ if (!mStarted) {
+ return OMX_ErrorNone;
+ }
+
+ s_retrieve_mem_ip.u4_size = sizeof(iv_retrieve_mem_rec_ip_t);
+ s_retrieve_mem_op.u4_size = sizeof(iv_retrieve_mem_rec_op_t);
+ s_retrieve_mem_ip.e_cmd = IV_CMD_RETRIEVE_MEMREC;
+ s_retrieve_mem_ip.ps_mem_rec = mMemRecords;
+
+ status = ive_api_function(mCodecCtx, &s_retrieve_mem_ip, &s_retrieve_mem_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Unable to retrieve memory records = 0x%x\n",
+ s_retrieve_mem_op.u4_error_code);
+ return OMX_ErrorUndefined;
+ }
+
+ /* Free memory records */
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < s_retrieve_mem_op.u4_num_mem_rec_filled; i++) {
+ ive_aligned_free(ps_mem_rec->pv_base);
+ ps_mem_rec++;
+ }
+
+ free(mMemRecords);
+
+ if (mConversionBuffer != NULL) {
+ free(mConversionBuffer);
+ mConversionBuffer = NULL;
+ }
+
+ mStarted = false;
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::internalGetParameter(OMX_INDEXTYPE index, OMX_PTR params) {
+ switch (index) {
+ case OMX_IndexParamVideoBitrate:
+ {
+ OMX_VIDEO_PARAM_BITRATETYPE *bitRate =
+ (OMX_VIDEO_PARAM_BITRATETYPE *)params;
+
+ if (bitRate->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ bitRate->eControlRate = OMX_Video_ControlRateVariable;
+ bitRate->nTargetBitrate = mBitrate;
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamVideoAvc:
+ {
+ OMX_VIDEO_PARAM_AVCTYPE *avcParams = (OMX_VIDEO_PARAM_AVCTYPE *)params;
+
+ if (avcParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ avcParams->eProfile = OMX_VIDEO_AVCProfileBaseline;
+ OMX_VIDEO_AVCLEVELTYPE omxLevel = OMX_VIDEO_AVCLevel31;
+ if (OMX_ErrorNone
+ != ConvertAvcSpecLevelToOmxAvcLevel(mAVCEncLevel, &omxLevel)) {
+ return OMX_ErrorUndefined;
+ }
+
+ avcParams->eLevel = omxLevel;
+ avcParams->nRefFrames = 1;
+ avcParams->nBFrames = 0;
+ avcParams->bUseHadamard = OMX_TRUE;
+ avcParams->nAllowedPictureTypes = (OMX_VIDEO_PictureTypeI
+ | OMX_VIDEO_PictureTypeP);
+ avcParams->nRefIdx10ActiveMinus1 = 0;
+ avcParams->nRefIdx11ActiveMinus1 = 0;
+ avcParams->bWeightedPPrediction = OMX_FALSE;
+ avcParams->bEntropyCodingCABAC = OMX_FALSE;
+ avcParams->bconstIpred = OMX_FALSE;
+ avcParams->bDirect8x8Inference = OMX_FALSE;
+ avcParams->bDirectSpatialTemporal = OMX_FALSE;
+ avcParams->nCabacInitIdc = 0;
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SoftVideoEncoderOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) {
+ int32_t indexFull = index;
+
+ switch (indexFull) {
+ case OMX_IndexParamVideoBitrate:
+ {
+ return internalSetBitrateParams(
+ (const OMX_VIDEO_PARAM_BITRATETYPE *)params);
+ }
+
+ case OMX_IndexParamVideoAvc:
+ {
+ OMX_VIDEO_PARAM_AVCTYPE *avcType = (OMX_VIDEO_PARAM_AVCTYPE *)params;
+
+ if (avcType->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (avcType->eProfile != OMX_VIDEO_AVCProfileBaseline
+ || avcType->nRefFrames != 1 || avcType->nBFrames != 0
+ || avcType->bUseHadamard != OMX_TRUE
+ || (avcType->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) != 0
+ || avcType->nRefIdx10ActiveMinus1 != 0
+ || avcType->nRefIdx11ActiveMinus1 != 0
+ || avcType->bWeightedPPrediction != OMX_FALSE
+ || avcType->bEntropyCodingCABAC != OMX_FALSE
+ || avcType->bconstIpred != OMX_FALSE
+ || avcType->bDirect8x8Inference != OMX_FALSE
+ || avcType->bDirectSpatialTemporal != OMX_FALSE
+ || avcType->nCabacInitIdc != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (OK != ConvertOmxAvcLevelToAvcSpecLevel(avcType->eLevel, &mAVCEncLevel)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SoftVideoEncoderOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAVC::setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR _params) {
+ switch (index) {
+ case OMX_IndexConfigVideoIntraVOPRefresh:
+ {
+ OMX_CONFIG_INTRAREFRESHVOPTYPE *params =
+ (OMX_CONFIG_INTRAREFRESHVOPTYPE *)_params;
+
+ if (params->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorBadPortIndex;
+ }
+
+ mKeyFrameRequested = params->IntraRefreshVOP;
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexConfigVideoBitrate:
+ {
+ OMX_VIDEO_CONFIG_BITRATETYPE *params =
+ (OMX_VIDEO_CONFIG_BITRATETYPE *)_params;
+
+ if (params->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorBadPortIndex;
+ }
+
+ if (mBitrate != params->nEncodeBitrate) {
+ mBitrate = params->nEncodeBitrate;
+ mBitrateUpdated = true;
+ }
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::setConfig(index, _params);
+ }
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetBitrateParams(
+ const OMX_VIDEO_PARAM_BITRATETYPE *bitrate) {
+ if (bitrate->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorUnsupportedIndex;
+ }
+
+ mBitrate = bitrate->nTargetBitrate;
+ mBitrateUpdated = true;
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setEncodeArgs(
+ ive_video_encode_ip_t *ps_encode_ip,
+ ive_video_encode_op_t *ps_encode_op,
+ OMX_BUFFERHEADERTYPE *inputBufferHeader,
+ OMX_BUFFERHEADERTYPE *outputBufferHeader) {
+ iv_raw_buf_t *ps_inp_raw_buf;
+ const uint8_t *source;
+ UWORD8 *pu1_buf;
+
+ ps_inp_raw_buf = &ps_encode_ip->s_inp_buf;
+ ps_encode_ip->s_out_buf.pv_buf = outputBufferHeader->pBuffer;
+ ps_encode_ip->s_out_buf.u4_bytes = 0;
+ ps_encode_ip->s_out_buf.u4_bufsize = outputBufferHeader->nAllocLen;
+ ps_encode_ip->u4_size = sizeof(ive_video_encode_ip_t);
+ ps_encode_op->u4_size = sizeof(ive_video_encode_op_t);
+
+ ps_encode_ip->e_cmd = IVE_CMD_VIDEO_ENCODE;
+ ps_encode_ip->pv_bufs = NULL;
+ ps_encode_ip->pv_mb_info = NULL;
+ ps_encode_ip->pv_pic_info = NULL;
+ ps_encode_ip->u4_mb_info_type = 0;
+ ps_encode_ip->u4_pic_info_type = 0;
+ ps_encode_op->s_out_buf.pv_buf = NULL;
+
+ /* Initialize color formats */
+ ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat;
+
+ source = NULL;
+ if (inputBufferHeader) {
+ source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset;
+
+ if (mInputDataIsMeta) {
+ source = extractGraphicBuffer(
+ mConversionBuffer, (mWidth * mHeight * 3 / 2), source,
+ inputBufferHeader->nFilledLen, mWidth, mHeight);
+
+ if (source == NULL) {
+ ALOGE("Error in extractGraphicBuffer");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return OMX_ErrorUndefined;
+ }
+ }
+ }
+
+ pu1_buf = (UWORD8 *)source;
+ switch (mIvVideoColorFormat) {
+ case IV_YUV_420P:
+ {
+ ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+ pu1_buf += (mStride) * mHeight;
+ ps_inp_raw_buf->apv_bufs[1] = pu1_buf;
+ pu1_buf += (mStride / 2) * mHeight / 2;
+ ps_inp_raw_buf->apv_bufs[2] = pu1_buf;
+
+ ps_inp_raw_buf->au4_wd[0] = mWidth;
+ ps_inp_raw_buf->au4_wd[1] = mWidth / 2;
+ ps_inp_raw_buf->au4_wd[2] = mWidth / 2;
+
+ ps_inp_raw_buf->au4_ht[0] = mHeight;
+ ps_inp_raw_buf->au4_ht[1] = mHeight / 2;
+ ps_inp_raw_buf->au4_ht[2] = mHeight / 2;
+
+ ps_inp_raw_buf->au4_strd[0] = mStride;
+ ps_inp_raw_buf->au4_strd[1] = (mStride / 2);
+ ps_inp_raw_buf->au4_strd[2] = (mStride / 2);
+ break;
+ }
+
+ case IV_YUV_422ILE:
+ {
+ ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+ ps_inp_raw_buf->au4_wd[0] = mWidth * 2;
+ ps_inp_raw_buf->au4_ht[0] = mHeight;
+ ps_inp_raw_buf->au4_strd[0] = mStride * 2;
+ break;
+ }
+
+ case IV_YUV_420SP_UV:
+ case IV_YUV_420SP_VU:
+ default:
+ {
+ ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+ pu1_buf += (mStride) * mHeight;
+ ps_inp_raw_buf->apv_bufs[1] = pu1_buf;
+
+ ps_inp_raw_buf->au4_wd[0] = mWidth;
+ ps_inp_raw_buf->au4_wd[1] = mWidth;
+
+ ps_inp_raw_buf->au4_ht[0] = mHeight;
+ ps_inp_raw_buf->au4_ht[1] = mHeight / 2;
+
+ ps_inp_raw_buf->au4_strd[0] = mStride;
+ ps_inp_raw_buf->au4_strd[1] = mStride;
+ break;
+ }
+ }
+
+ ps_encode_ip->u4_is_last = 0;
+
+ if (inputBufferHeader) {
+ ps_encode_ip->u4_timestamp_high = (inputBufferHeader->nTimeStamp) >> 32;
+ ps_encode_ip->u4_timestamp_low = (inputBufferHeader->nTimeStamp) & 0xFFFFFFFF;
+ }
+
+ return OMX_ErrorNone;
+}
+
+void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+ IV_STATUS_T status;
+ WORD32 timeDelay, timeTaken;
+
+ UNUSED(portIndex);
+
+ // Initialize encoder if not already initialized
+ if (mCodecCtx == NULL) {
+ if (OMX_ErrorNone != initEncoder()) {
+ ALOGE("Failed to initialize encoder");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0 /* arg2 */, NULL /* data */);
+ return;
+ }
+ }
+ if (mSignalledError || mSawInputEOS) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ while (!mSawInputEOS && !inQueue.empty() && !outQueue.empty()) {
+ OMX_ERRORTYPE error;
+ ive_video_encode_ip_t s_encode_ip;
+ ive_video_encode_op_t s_encode_op;
+
+ BufferInfo *inputBufferInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inputBufferHeader = inputBufferInfo->mHeader;
+
+ BufferInfo *outputBufferInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader;
+
+ if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ inQueue.erase(inQueue.begin());
+ inputBufferInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inputBufferHeader);
+
+ outputBufferHeader->nFilledLen = 0;
+ outputBufferHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outQueue.erase(outQueue.begin());
+ outputBufferInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outputBufferHeader);
+ return;
+ }
+
+ outputBufferHeader->nTimeStamp = 0;
+ outputBufferHeader->nFlags = 0;
+ outputBufferHeader->nOffset = 0;
+ outputBufferHeader->nFilledLen = 0;
+ outputBufferHeader->nOffset = 0;
+
+ uint8_t *outPtr = (uint8_t *)outputBufferHeader->pBuffer;
+
+ if (!mSpsPpsHeaderReceived) {
+ error = setEncodeArgs(&s_encode_ip, &s_encode_op, NULL, outputBufferHeader);
+ if (error != OMX_ErrorNone) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return;
+ }
+ status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+ if (IV_SUCCESS != status) {
+ ALOGE("Encode Frame failed = 0x%x\n",
+ s_encode_op.u4_error_code);
+ } else {
+ ALOGV("Bytes Generated in header %d\n",
+ s_encode_op.s_out_buf.u4_bytes);
+ }
+
+ mSpsPpsHeaderReceived = true;
+
+ outputBufferHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG;
+ outputBufferHeader->nFilledLen = s_encode_op.s_out_buf.u4_bytes;
+ outputBufferHeader->nTimeStamp = inputBufferHeader->nTimeStamp;
+
+ outQueue.erase(outQueue.begin());
+ outputBufferInfo->mOwnedByUs = false;
+ DUMP_TO_FILE(
+ mOutFile, outputBufferHeader->pBuffer,
+ outputBufferHeader->nFilledLen);
+ notifyFillBufferDone(outputBufferHeader);
+
+ setEncMode(IVE_ENC_MODE_PICTURE);
+ return;
+ }
+
+ if (mBitrateUpdated) {
+ setBitRate();
+ }
+
+ if (mKeyFrameRequested) {
+ setFrameType(IV_IDR_FRAME);
+ }
+
+ mPrevTimestampUs = inputBufferHeader->nTimeStamp;
+
+ if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ mSawInputEOS = true;
+ }
+
+ error = setEncodeArgs(
+ &s_encode_ip, &s_encode_op, inputBufferHeader, outputBufferHeader);
+ if (error != OMX_ErrorNone) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return;
+ }
+
+ DUMP_TO_FILE(
+ mInFile, s_encode_ip.s_inp_buf.apv_bufs[0],
+ (mHeight * mStride * 3 / 2));
+ //DUMP_TO_FILE(mInFile, inputBufferHeader->pBuffer + inputBufferHeader->nOffset,
+ // inputBufferHeader->nFilledLen);
+
+ GETTIME(&mTimeStart, NULL);
+ /* Compute time elapsed between end of previous decode()
+ * to start of current decode() */
+ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+ status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+ if (IV_SUCCESS != status) {
+ ALOGE("Encode Frame failed = 0x%x\n",
+ s_encode_op.u4_error_code);
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ return;
+ }
+
+ GETTIME(&mTimeEnd, NULL);
+ /* Compute time taken for decode() */
+ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+ ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+ s_encode_op.s_out_buf.u4_bytes);
+
+
+ outputBufferHeader->nFlags = inputBufferHeader->nFlags;
+ outputBufferHeader->nFilledLen = s_encode_op.s_out_buf.u4_bytes;
+ outputBufferHeader->nTimeStamp = inputBufferHeader->nTimeStamp;
+
+ if (IV_IDR_FRAME
+ == s_encode_op.u4_encoded_frame_type) {
+ outputBufferHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME;
+ }
+
+ inQueue.erase(inQueue.begin());
+ inputBufferInfo->mOwnedByUs = false;
+
+ notifyEmptyBufferDone(inputBufferHeader);
+
+ if (mSawInputEOS) {
+ outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+ }
+
+ outputBufferInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+
+ DUMP_TO_FILE(
+ mOutFile, outputBufferHeader->pBuffer,
+ outputBufferHeader->nFilledLen);
+ notifyFillBufferDone(outputBufferHeader);
+
+ }
+ return;
+}
+
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+ return new android::SoftAVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h
new file mode 100644
index 0000000..2b35d45
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright 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 __SOFT_AVC_ENC_H__
+#define __SOFT_AVC_ENC_H__
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Vector.h>
+
+#include "SoftVideoEncoderOMXComponent.h"
+
+namespace android {
+
+class MediaBuffer;
+
+#define CODEC_MAX_CORES 4
+#define LEN_STATUS_BUFFER (10 * 1024)
+#define MAX_VBV_BUFF_SIZE (120 * 16384)
+#define MAX_NUM_IO_BUFS 3
+
+#define DEFAULT_MAX_REF_FRM 1
+#define DEFAULT_MAX_REORDER_FRM 0
+#define DEFAULT_QP_MIN 10
+#define DEFAULT_QP_MAX 40
+#define DEFAULT_MAX_BITRATE 20000000
+#define DEFAULT_MAX_SRCH_RANGE_X 256
+#define DEFAULT_MAX_SRCH_RANGE_Y 256
+#define DEFAULT_MAX_FRAMERATE 120000
+#define DEFAULT_NUM_CORES 1
+#define DEFAULT_NUM_CORES_PRE_ENC 0
+#define DEFAULT_FPS 30
+#define DEFAULT_ENC_SPEED IVE_NORMAL
+
+#define DEFAULT_MEM_REC_CNT 0
+#define DEFAULT_RECON_ENABLE 0
+#define DEFAULT_CHKSUM_ENABLE 0
+#define DEFAULT_START_FRM 0
+#define DEFAULT_NUM_FRMS 0xFFFFFFFF
+#define DEFAULT_INP_COLOR_FORMAT IV_YUV_420SP_VU
+#define DEFAULT_RECON_COLOR_FORMAT IV_YUV_420P
+#define DEFAULT_LOOPBACK 0
+#define DEFAULT_SRC_FRAME_RATE 30
+#define DEFAULT_TGT_FRAME_RATE 30
+#define DEFAULT_MAX_WD 1920
+#define DEFAULT_MAX_HT 1920
+#define DEFAULT_MAX_LEVEL 40
+#define DEFAULT_STRIDE 0
+#define DEFAULT_WD 1280
+#define DEFAULT_HT 720
+#define DEFAULT_PSNR_ENABLE 0
+#define DEFAULT_ME_SPEED 100
+#define DEFAULT_ENABLE_FAST_SAD 0
+#define DEFAULT_ENABLE_ALT_REF 0
+#define DEFAULT_RC_MODE IVE_RC_STORAGE
+#define DEFAULT_BITRATE 6000000
+#define DEFAULT_I_QP 22
+#define DEFAULT_I_QP_MAX DEFAULT_QP_MAX
+#define DEFAULT_I_QP_MIN DEFAULT_QP_MIN
+#define DEFAULT_P_QP 28
+#define DEFAULT_P_QP_MAX DEFAULT_QP_MAX
+#define DEFAULT_P_QP_MIN DEFAULT_QP_MIN
+#define DEFAULT_B_QP 22
+#define DEFAULT_B_QP_MAX DEFAULT_QP_MAX
+#define DEFAULT_B_QP_MIN DEFAULT_QP_MIN
+#define DEFAULT_AIR IVE_AIR_MODE_NONE
+#define DEFAULT_AIR_REFRESH_PERIOD 30
+#define DEFAULT_SRCH_RNG_X 64
+#define DEFAULT_SRCH_RNG_Y 48
+#define DEFAULT_I_INTERVAL 30
+#define DEFAULT_IDR_INTERVAL 1000
+#define DEFAULT_B_FRAMES 0
+#define DEFAULT_DISABLE_DEBLK_LEVEL 0
+#define DEFAULT_HPEL 1
+#define DEFAULT_QPEL 1
+#define DEFAULT_I4 1
+#define DEFAULT_EPROFILE IV_PROFILE_BASE
+#define DEFAULT_SLICE_MODE IVE_SLICE_MODE_NONE
+#define DEFAULT_SLICE_PARAM 256
+#define DEFAULT_ARCH ARCH_ARM_A9Q
+#define DEFAULT_SOC SOC_GENERIC
+#define DEFAULT_INTRA4x4 0
+#define STRLENGTH 500
+
+
+
+#define MIN(a, b) ((a) < (b))? (a) : (b)
+#define MAX(a, b) ((a) > (b))? (a) : (b)
+#define ALIGN16(x) ((((x) + 15) >> 4) << 4)
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN4096(x) ((((x) + 4095) >> 12) << 12)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+ diff = ((end.tv_sec - start.tv_sec) * 1000000) + \
+ (end.tv_usec - start.tv_usec);
+
+#define ive_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ive_aligned_free(buf) free(buf)
+
+struct SoftAVC : public SoftVideoEncoderOMXComponent {
+ SoftAVC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+ // Override SimpleSoftOMXComponent methods
+ virtual OMX_ERRORTYPE internalGetParameter(
+ OMX_INDEXTYPE index, OMX_PTR params);
+
+ virtual OMX_ERRORTYPE internalSetParameter(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+
+protected:
+ virtual ~SoftAVC();
+
+private:
+ enum {
+ kNumBuffers = 2,
+ };
+
+ // OMX input buffer's timestamp and flags
+ typedef struct {
+ int64_t mTimeUs;
+ int32_t mFlags;
+ } InputBufferInfo;
+
+ int32_t mStride;
+
+ uint32_t mFrameRate;
+
+ struct timeval mTimeStart; // Time at the start of decode()
+ struct timeval mTimeEnd; // Time at the end of decode()
+
+
+ // If a request for a change it bitrate has been received.
+ bool mBitrateUpdated;
+
+ bool mKeyFrameRequested;
+
+#ifdef FILE_DUMP_ENABLE
+ char mInFile[200];
+ char mOutFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+ IV_COLOR_FORMAT_T mIvVideoColorFormat;
+
+ int32_t mIDRFrameRefreshIntervalInSec;
+ IV_PROFILE_T mAVCEncProfile;
+ WORD32 mAVCEncLevel;
+ int64_t mNumInputFrames;
+ int64_t mPrevTimestampUs;
+ bool mStarted;
+ bool mSpsPpsHeaderReceived;
+
+ bool mSawInputEOS;
+ bool mSignalledError;
+ bool mIntra4x4;
+ bool mEnableFastSad;
+ bool mEnableAltRef;
+ bool mReconEnable;
+ bool mPSNREnable;
+ IVE_SPEED_CONFIG mEncSpeed;
+
+ uint8_t *mConversionBuffer;
+
+ iv_obj_t *mCodecCtx; // Codec context
+ iv_mem_rec_t *mMemRecords; // Memory records requested by the codec
+ size_t mNumMemRecords; // Number of memory records requested by codec
+ size_t mNumCores; // Number of cores used by the codec
+
+ UWORD32 mHeaderGenerated;
+
+ IV_ARCH_T mArch;
+ IVE_SLICE_MODE_T mSliceMode;
+ UWORD32 mSliceParam;
+ bool mHalfPelEnable;
+ UWORD32 mIInterval;
+ UWORD32 mIDRInterval;
+ UWORD32 mDisableDeblkLevel;
+ IVE_AIR_MODE_T mAIRMode;
+ UWORD32 mAIRRefreshPeriod;
+
+ OMX_ERRORTYPE initEncParams();
+ OMX_ERRORTYPE initEncoder();
+ OMX_ERRORTYPE releaseEncoder();
+
+ // Verifies the component role tried to be set to this OMX component is
+ // strictly video_encoder.avc
+ OMX_ERRORTYPE internalSetRoleParams(
+ const OMX_PARAM_COMPONENTROLETYPE *role);
+
+ // Updates bitrate to reflect port settings.
+ OMX_ERRORTYPE internalSetBitrateParams(
+ const OMX_VIDEO_PARAM_BITRATETYPE *bitrate);
+
+ OMX_ERRORTYPE setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR _params);
+
+ // Handles port definition changes.
+ OMX_ERRORTYPE internalSetPortParams(
+ const OMX_PARAM_PORTDEFINITIONTYPE *port);
+
+ OMX_ERRORTYPE internalSetFormatParams(
+ const OMX_VIDEO_PARAM_PORTFORMATTYPE *format);
+
+ OMX_ERRORTYPE setFrameType(IV_PICTURE_CODING_TYPE_T e_frame_type);
+ OMX_ERRORTYPE setQp();
+ OMX_ERRORTYPE setEncMode(IVE_ENC_MODE_T e_enc_mode);
+ OMX_ERRORTYPE setDimensions();
+ OMX_ERRORTYPE setNumCores();
+ OMX_ERRORTYPE setFrameRate();
+ OMX_ERRORTYPE setIpeParams();
+ OMX_ERRORTYPE setBitRate();
+ OMX_ERRORTYPE setAirParams();
+ OMX_ERRORTYPE setMeParams();
+ OMX_ERRORTYPE setGopParams();
+ OMX_ERRORTYPE setProfileParams();
+ OMX_ERRORTYPE setDeblockParams();
+ OMX_ERRORTYPE setVbvParams();
+ void logVersion();
+ OMX_ERRORTYPE setEncodeArgs(
+ ive_video_encode_ip_t *ps_encode_ip,
+ ive_video_encode_op_t *ps_encode_op,
+ OMX_BUFFERHEADERTYPE *inputBufferHeader,
+ OMX_BUFFERHEADERTYPE *outputBufferHeader);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH "/sdcard/media/avce_input"
+#define INPUT_DUMP_EXT "yuv"
+#define OUTPUT_DUMP_PATH "/sdcard/media/avce_output"
+#define OUTPUT_DUMP_EXT "h264"
+
+#define GENERATE_FILE_NAMES() { \
+ GETTIME(&mTimeStart, NULL); \
+ strcpy(mInFile, ""); \
+ sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \
+ mTimeStart.tv_sec, mTimeStart.tv_usec, \
+ INPUT_DUMP_EXT); \
+ strcpy(mOutFile, ""); \
+ sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,\
+ mTimeStart.tv_sec, mTimeStart.tv_usec, \
+ OUTPUT_DUMP_EXT); \
+}
+
+#define CREATE_DUMP_FILE(m_filename) { \
+ FILE *fp = fopen(m_filename, "wb"); \
+ if (fp != NULL) { \
+ ALOGD("Opened file %s", m_filename); \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not open file %s", m_filename); \
+ } \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size) \
+{ \
+ FILE *fp = fopen(m_filename, "ab"); \
+ if (fp != NULL && m_buf != NULL) { \
+ int i; \
+ i = fwrite(m_buf, 1, m_size, fp); \
+ ALOGD("fwrite ret %d to write %d", i, m_size); \
+ if (i != (int)m_size) { \
+ ALOGD("Error in fwrite, returned %d", i); \
+ perror("Error in write to file"); \
+ } \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not write to file %s", m_filename);\
+ } \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+} // namespace android
+
+#endif // __SOFT_AVC_ENC_H__
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
index cddd176..5c05a0e 100644
--- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
@@ -143,7 +143,7 @@
s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
- ALOGV("Set the run-time (dynamic) parameters stride = %u", stride);
+ ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
(void *)&s_ctl_op);
@@ -408,7 +408,7 @@
uint32_t bufferSize = displaySizeY * 3 / 2;
mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
if (NULL == mFlushOutBuffer) {
- ALOGE("Could not allocate flushOutputBuffer of size %zu", bufferSize);
+ ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
return NO_MEMORY;
}
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.mk b/media/libstagefright/codecs/mpeg2dec/Android.mk
new file mode 100644
index 0000000..23b126d
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/Android.mk
@@ -0,0 +1,27 @@
+ifeq ($(if $(wildcard external/libmpeg2),1,0),1)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libstagefright_soft_mpeg2dec
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_LIBRARIES := libmpeg2dec
+LOCAL_SRC_FILES := SoftMPEG2.cpp
+
+LOCAL_C_INCLUDES := $(TOP)/external/libmpeg2/decoder
+LOCAL_C_INCLUDES += $(TOP)/external/libmpeg2/common
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libstagefright_omx
+LOCAL_SHARED_LIBRARIES += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
new file mode 100644
index 0000000..78b3ab4
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -0,0 +1,771 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftMPEG2"
+#include <utils/Log.h>
+
+#include "iv_datatypedef.h"
+#include "iv.h"
+#include "ivd.h"
+#include "ithread.h"
+#include "impeg2d.h"
+#include "SoftMPEG2.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <OMX_VideoExt.h>
+
+namespace android {
+
+#define componentName "video_decoder.mpeg2"
+#define codingType OMX_VIDEO_CodingMPEG2
+#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_MPEG2
+
+/** Function and structure definitions to keep code similar for each codec */
+#define ivdec_api_function impeg2d_api_function
+#define ivdext_init_ip_t impeg2d_init_ip_t
+#define ivdext_init_op_t impeg2d_init_op_t
+#define ivdext_fill_mem_rec_ip_t impeg2d_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t impeg2d_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t impeg2d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t impeg2d_ctl_set_num_cores_op_t
+
+#define IVDEXT_CMD_CTL_SET_NUM_CORES \
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
+
+static const CodecProfileLevel kProfileLevels[] = {
+ { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelLL },
+ { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelML },
+ { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelH14 },
+ { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelHL },
+
+ { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelLL },
+ { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelML },
+ { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelH14 },
+ { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelHL },
+};
+
+SoftMPEG2::SoftMPEG2(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SoftVideoDecoderOMXComponent(
+ name, componentName, codingType,
+ kProfileLevels, ARRAY_SIZE(kProfileLevels),
+ 320 /* width */, 240 /* height */, callbacks,
+ appData, component),
+ mMemRecords(NULL),
+ mFlushOutBuffer(NULL),
+ mOmxColorFormat(OMX_COLOR_FormatYUV420Planar),
+ mIvColorFormat(IV_YUV_420P),
+ mNewWidth(mWidth),
+ mNewHeight(mHeight),
+ mChangingResolution(false) {
+ initPorts(kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE);
+
+ // If input dump is enabled, then open create an empty file
+ GENERATE_FILE_NAMES();
+ CREATE_DUMP_FILE(mInFile);
+
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftMPEG2::~SoftMPEG2() {
+ CHECK_EQ(deInitDecoder(), (status_t)OK);
+}
+
+
+static size_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
+ OMX_S64 minTimeStamp = LLONG_MAX;
+ int idx = -1;
+ for (size_t i = 0; i < MAX_TIME_STAMPS; i++) {
+ if (pIsTimeStampValid[i]) {
+ if (pNTimeStamp[i] < minTimeStamp) {
+ minTimeStamp = pNTimeStamp[i];
+ idx = i;
+ }
+ }
+ }
+ return idx;
+}
+
+static size_t GetCPUCoreCount() {
+ long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+ return (size_t)cpuCoreCount;
+}
+
+void SoftMPEG2::logVersion() {
+ ivd_ctl_getversioninfo_ip_t s_ctl_ip;
+ ivd_ctl_getversioninfo_op_t s_ctl_op;
+ UWORD8 au1_buf[512];
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+ s_ctl_ip.pv_version_buffer = au1_buf;
+ s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in getting version number: 0x%x",
+ s_ctl_op.u4_error_code);
+ } else {
+ ALOGV("Ittiam decoder version number: %s",
+ (char *)s_ctl_ip.pv_version_buffer);
+ }
+ return;
+}
+
+status_t SoftMPEG2::setParams(size_t stride) {
+ ivd_ctl_set_config_ip_t s_ctl_ip;
+ ivd_ctl_set_config_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+ s_ctl_ip.u4_disp_wd = (UWORD32)stride;
+ s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+
+ s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+
+ ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the run-time parameters: 0x%x",
+ s_ctl_op.u4_error_code);
+
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t SoftMPEG2::resetPlugin() {
+ mIsInFlush = false;
+ mReceivedEOS = false;
+ memset(mTimeStamps, 0, sizeof(mTimeStamps));
+ memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
+
+ /* Initialize both start and end times */
+ gettimeofday(&mTimeStart, NULL);
+ gettimeofday(&mTimeEnd, NULL);
+
+ return OK;
+}
+
+status_t SoftMPEG2::resetDecoder() {
+ ivd_ctl_reset_ip_t s_ctl_ip;
+ ivd_ctl_reset_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ /* Set the run-time (dynamic) parameters */
+ setParams(outputBufferWidth());
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ return OK;
+}
+
+status_t SoftMPEG2::setNumCores() {
+ ivdext_ctl_set_num_cores_ip_t s_set_cores_ip;
+ ivdext_ctl_set_num_cores_op_t s_set_cores_op;
+ IV_API_CALL_STATUS_T status;
+ s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+ s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES);
+ s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+ s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in setting number of cores: 0x%x",
+ s_set_cores_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t SoftMPEG2::setFlushMode() {
+ IV_API_CALL_STATUS_T status;
+ ivd_ctl_flush_ip_t s_video_flush_ip;
+ ivd_ctl_flush_op_t s_video_flush_op;
+
+ s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+ s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+ s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+
+ /* Set the decoder in Flush mode, subsequent decode() calls will flush */
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status,
+ s_video_flush_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ mWaitForI = true;
+ mIsInFlush = true;
+ return OK;
+}
+
+status_t SoftMPEG2::initDecoder() {
+ IV_API_CALL_STATUS_T status;
+
+ UWORD32 u4_num_reorder_frames;
+ UWORD32 u4_num_ref_frames;
+ UWORD32 u4_share_disp_buf;
+
+ mNumCores = GetCPUCoreCount();
+ mWaitForI = true;
+
+ /* Initialize number of ref and reorder modes (for MPEG2) */
+ u4_num_reorder_frames = 16;
+ u4_num_ref_frames = 16;
+ u4_share_disp_buf = 0;
+
+ uint32_t displayStride = outputBufferWidth();
+ uint32_t displayHeight = outputBufferHeight();
+ uint32_t displaySizeY = displayStride * displayHeight;
+
+ {
+ iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+ iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+ s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+ s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+ s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_num_mem_rec_ip, (void *)&s_num_mem_rec_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in getting mem records: 0x%x",
+ s_num_mem_rec_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+ }
+
+ mMemRecords = (iv_mem_rec_t *)ivd_aligned_malloc(
+ 128, mNumMemRecords * sizeof(iv_mem_rec_t));
+ if (mMemRecords == NULL) {
+ ALOGE("Allocation failure");
+ return NO_MEMORY;
+ }
+
+ memset(mMemRecords, 0, mNumMemRecords * sizeof(iv_mem_rec_t));
+
+ {
+ size_t i;
+ ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+ ivdext_fill_mem_rec_op_t s_fill_mem_op;
+ iv_mem_rec_t *ps_mem_rec;
+
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size =
+ sizeof(ivdext_fill_mem_rec_ip_t);
+
+ s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf;
+ s_fill_mem_ip.e_output_format = mIvColorFormat;
+
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = displayStride;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = displayHeight;
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size =
+ sizeof(ivdext_fill_mem_rec_op_t);
+
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < mNumMemRecords; i++) {
+ ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+ }
+
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_fill_mem_ip, (void *)&s_fill_mem_op);
+
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in filling mem records: 0x%x",
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ mNumMemRecords =
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled;
+
+ ps_mem_rec = mMemRecords;
+
+ for (i = 0; i < mNumMemRecords; i++) {
+ ps_mem_rec->pv_base = ivd_aligned_malloc(
+ ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+ if (ps_mem_rec->pv_base == NULL) {
+ ALOGE("Allocation failure for memory record #%zu of size %u",
+ i, ps_mem_rec->u4_mem_size);
+ status = IV_FAIL;
+ return NO_MEMORY;
+ }
+
+ ps_mem_rec++;
+ }
+ }
+
+ /* Initialize the decoder */
+ {
+ ivdext_init_ip_t s_init_ip;
+ ivdext_init_op_t s_init_op;
+
+ void *dec_fxns = (void *)ivdec_api_function;
+
+ s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+ s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+ s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = displayStride;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = displayHeight;
+
+ s_init_ip.u4_share_disp_buf = u4_share_disp_buf;
+
+ s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op);
+
+ s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+ s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat;
+
+ mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+ mCodecCtx->pv_fxns = dec_fxns;
+ mCodecCtx->u4_size = sizeof(iv_obj_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, (void *)&s_init_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in init: 0x%x",
+ s_init_op.s_ivd_init_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ /* Reset the plugin state */
+ resetPlugin();
+
+ /* Set the run time (dynamic) parameters */
+ setParams(displayStride);
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ /* Get codec version */
+ logVersion();
+
+ /* Allocate internal picture buffer */
+ uint32_t bufferSize = displaySizeY * 3 / 2;
+ mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
+ if (NULL == mFlushOutBuffer) {
+ ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
+ return NO_MEMORY;
+ }
+
+ mInitNeeded = false;
+ mFlushNeeded = false;
+ return OK;
+}
+
+status_t SoftMPEG2::deInitDecoder() {
+ size_t i;
+
+ if (mMemRecords) {
+ iv_mem_rec_t *ps_mem_rec;
+
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < mNumMemRecords; i++) {
+ if (ps_mem_rec->pv_base) {
+ ivd_aligned_free(ps_mem_rec->pv_base);
+ }
+ ps_mem_rec++;
+ }
+ ivd_aligned_free(mMemRecords);
+ mMemRecords = NULL;
+ }
+
+ if (mFlushOutBuffer) {
+ ivd_aligned_free(mFlushOutBuffer);
+ mFlushOutBuffer = NULL;
+ }
+
+ mInitNeeded = true;
+ mChangingResolution = false;
+
+ return OK;
+}
+
+status_t SoftMPEG2::reInitDecoder() {
+ status_t ret;
+
+ deInitDecoder();
+
+ ret = initDecoder();
+ if (OK != ret) {
+ ALOGE("Create failure");
+ deInitDecoder();
+ return NO_MEMORY;
+ }
+ return OK;
+}
+
+void SoftMPEG2::onReset() {
+ SoftVideoDecoderOMXComponent::onReset();
+
+ mWaitForI = true;
+
+ resetDecoder();
+ resetPlugin();
+}
+
+OMX_ERRORTYPE SoftMPEG2::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) {
+ const uint32_t oldWidth = mWidth;
+ const uint32_t oldHeight = mHeight;
+ OMX_ERRORTYPE ret = SoftVideoDecoderOMXComponent::internalSetParameter(index, params);
+ if (mWidth != oldWidth || mHeight != oldHeight) {
+ reInitDecoder();
+ }
+ return ret;
+}
+
+void SoftMPEG2::setDecodeArgs(
+ ivd_video_decode_ip_t *ps_dec_ip,
+ ivd_video_decode_op_t *ps_dec_op,
+ OMX_BUFFERHEADERTYPE *inHeader,
+ OMX_BUFFERHEADERTYPE *outHeader,
+ size_t timeStampIx) {
+ size_t sizeY = outputBufferWidth() * outputBufferHeight();
+ size_t sizeUV;
+ uint8_t *pBuf;
+
+ ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+ ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+ ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+
+ /* When in flush and after EOS with zero byte input,
+ * inHeader is set to zero. Hence check for non-null */
+ if (inHeader) {
+ ps_dec_ip->u4_ts = timeStampIx;
+ ps_dec_ip->pv_stream_buffer = inHeader->pBuffer
+ + inHeader->nOffset;
+ ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen;
+ } else {
+ ps_dec_ip->u4_ts = 0;
+ ps_dec_ip->pv_stream_buffer = NULL;
+ ps_dec_ip->u4_num_Bytes = 0;
+ }
+
+ if (outHeader) {
+ pBuf = outHeader->pBuffer;
+ } else {
+ pBuf = mFlushOutBuffer;
+ }
+
+ sizeUV = sizeY / 4;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV;
+
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf;
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY;
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV;
+ ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
+ return;
+}
+void SoftMPEG2::onPortFlushCompleted(OMX_U32 portIndex) {
+ /* Once the output buffers are flushed, ignore any buffers that are held in decoder */
+ if (kOutputPortIndex == portIndex) {
+ setFlushMode();
+
+ while (true) {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ IV_API_CALL_STATUS_T status;
+ size_t sizeY, sizeUV;
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ if (0 == s_dec_op.u4_output_present) {
+ resetPlugin();
+ break;
+ }
+ }
+ }
+}
+
+void SoftMPEG2::onQueueFilled(OMX_U32 portIndex) {
+ UNUSED(portIndex);
+
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
+ List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
+
+ /* If input EOS is seen and decoder is not in flush mode,
+ * set the decoder in flush mode.
+ * There can be a case where EOS is sent along with last picture data
+ * In that case, only after decoding that input data, decoder has to be
+ * put in flush. This case is handled here */
+
+ if (mReceivedEOS && !mIsInFlush) {
+ setFlushMode();
+ }
+
+ while (!outQueue.empty()) {
+ BufferInfo *inInfo;
+ OMX_BUFFERHEADERTYPE *inHeader;
+
+ BufferInfo *outInfo;
+ OMX_BUFFERHEADERTYPE *outHeader;
+ size_t timeStampIx;
+
+ inInfo = NULL;
+ inHeader = NULL;
+
+ if (!mIsInFlush) {
+ if (!inQueue.empty()) {
+ inInfo = *inQueue.begin();
+ inHeader = inInfo->mHeader;
+ } else {
+ break;
+ }
+ }
+
+ outInfo = *outQueue.begin();
+ outHeader = outInfo->mHeader;
+ outHeader->nFlags = 0;
+ outHeader->nTimeStamp = 0;
+ outHeader->nOffset = 0;
+
+ if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
+ mReceivedEOS = true;
+ if (inHeader->nFilledLen == 0) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ setFlushMode();
+ }
+ }
+
+ // When there is an init required and the decoder is not in flush mode,
+ // update output port's definition and reinitialize decoder.
+ if (mInitNeeded && !mIsInFlush) {
+ bool portWillReset = false;
+ handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight);
+
+ CHECK_EQ(reInitDecoder(), (status_t)OK);
+ return;
+ }
+
+ /* Get a free slot in timestamp array to hold input timestamp */
+ {
+ size_t i;
+ timeStampIx = 0;
+ for (i = 0; i < MAX_TIME_STAMPS; i++) {
+ if (!mTimeStampsValid[i]) {
+ timeStampIx = i;
+ break;
+ }
+ }
+ if (inHeader != NULL) {
+ mTimeStampsValid[timeStampIx] = true;
+ mTimeStamps[timeStampIx] = inHeader->nTimeStamp;
+ }
+ }
+
+ {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ WORD32 timeDelay, timeTaken;
+ size_t sizeY, sizeUV;
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+ // If input dump is enabled, then write to file
+ DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes);
+
+ if (s_dec_ip.u4_num_Bytes > 0) {
+ char *ptr = (char *)s_dec_ip.pv_stream_buffer;
+ }
+
+ GETTIME(&mTimeStart, NULL);
+ /* Compute time elapsed between end of previous decode()
+ * to start of current decode() */
+ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+ IV_API_CALL_STATUS_T status;
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+
+ bool unsupportedDimensions = (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code);
+ bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+
+ GETTIME(&mTimeEnd, NULL);
+ /* Compute time taken for decode() */
+ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+ ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+ s_dec_op.u4_num_bytes_consumed);
+ if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
+ mFlushNeeded = true;
+ }
+
+ if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) {
+ /* If the input did not contain picture data, then ignore
+ * the associated timestamp */
+ mTimeStampsValid[timeStampIx] = false;
+ }
+
+ // This is needed to handle CTS DecoderTest testCodecResetsMPEG2WithoutSurface,
+ // which is not sending SPS/PPS after port reconfiguration and flush to the codec.
+ if (unsupportedDimensions && !mFlushNeeded) {
+ bool portWillReset = false;
+ handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht);
+
+ CHECK_EQ(reInitDecoder(), (status_t)OK);
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+
+ ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ return;
+ }
+
+ // If the decoder is in the changing resolution mode and there is no output present,
+ // that means the switching is done and it's ready to reset the decoder and the plugin.
+ if (mChangingResolution && !s_dec_op.u4_output_present) {
+ mChangingResolution = false;
+ resetDecoder();
+ resetPlugin();
+ continue;
+ }
+
+ if (unsupportedDimensions || resChanged) {
+ mChangingResolution = true;
+ if (mFlushNeeded) {
+ setFlushMode();
+ }
+
+ if (unsupportedDimensions) {
+ mNewWidth = s_dec_op.u4_pic_wd;
+ mNewHeight = s_dec_op.u4_pic_ht;
+ mInitNeeded = true;
+ }
+ continue;
+ }
+
+ if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
+ uint32_t width = s_dec_op.u4_pic_wd;
+ uint32_t height = s_dec_op.u4_pic_ht;
+ bool portWillReset = false;
+ handlePortSettingsChange(&portWillReset, width, height);
+
+ if (portWillReset) {
+ resetDecoder();
+ return;
+ }
+ }
+
+ if (s_dec_op.u4_output_present) {
+ size_t timeStampIdx;
+ outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+
+ timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid);
+ outHeader->nTimeStamp = mTimeStamps[timeStampIdx];
+ mTimeStampsValid[timeStampIdx] = false;
+
+ /* mWaitForI waits for the first I picture. Once made FALSE, it
+ has to remain false till explicitly set to TRUE. */
+ mWaitForI = mWaitForI && !(IV_I_FRAME == s_dec_op.e_pic_type);
+
+ if (mWaitForI) {
+ s_dec_op.u4_output_present = false;
+ } else {
+ ALOGV("Output timestamp: %lld, res: %ux%u",
+ (long long)outHeader->nTimeStamp, mWidth, mHeight);
+ DUMP_TO_FILE(mOutFile, outHeader->pBuffer, outHeader->nFilledLen);
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ }
+ } else {
+ /* If in flush mode and no output is returned by the codec,
+ * then come out of flush mode */
+ mIsInFlush = false;
+
+ /* If EOS was recieved on input port and there is no output
+ * from the codec, then signal EOS on output port */
+ if (mReceivedEOS) {
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ resetPlugin();
+ }
+ }
+ }
+
+ // TODO: Handle more than one picture data
+ if (inHeader != NULL) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+ const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData,
+ OMX_COMPONENTTYPE **component) {
+ return new android::SoftMPEG2(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h
new file mode 100644
index 0000000..a625e08
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFT_MPEG2_H_
+
+#define SOFT_MPEG2_H_
+
+#include "SoftVideoDecoderOMXComponent.h"
+#include <sys/time.h>
+
+namespace android {
+
+#define ivd_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ivd_aligned_free(buf) free(buf)
+
+/** Number of entries in the time-stamp array */
+#define MAX_TIME_STAMPS 64
+
+/** Maximum number of cores supported by the codec */
+#define CODEC_MAX_NUM_CORES 4
+
+#define CODEC_MAX_WIDTH 1920
+
+#define CODEC_MAX_HEIGHT 1088
+
+/** Input buffer size */
+#define INPUT_BUF_SIZE (1024 * 1024)
+
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+ diff = ((end.tv_sec - start.tv_sec) * 1000000) + \
+ (end.tv_usec - start.tv_usec);
+
+struct SoftMPEG2 : public SoftVideoDecoderOMXComponent {
+ SoftMPEG2(
+ const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftMPEG2();
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onReset();
+ virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params);
+private:
+ // Number of input and output buffers
+ enum {
+ kNumBuffers = 8
+ };
+
+ iv_obj_t *mCodecCtx; // Codec context
+ iv_mem_rec_t *mMemRecords; // Memory records requested by the codec
+ size_t mNumMemRecords; // Number of memory records requested by the codec
+
+ size_t mNumCores; // Number of cores to be uesd by the codec
+
+ struct timeval mTimeStart; // Time at the start of decode()
+ struct timeval mTimeEnd; // Time at the end of decode()
+
+ // Internal buffer to be used to flush out the buffers from decoder
+ uint8_t *mFlushOutBuffer;
+
+ // Status of entries in the timestamp array
+ bool mTimeStampsValid[MAX_TIME_STAMPS];
+
+ // Timestamp array - Since codec does not take 64 bit timestamps,
+ // they are maintained in the plugin
+ OMX_S64 mTimeStamps[MAX_TIME_STAMPS];
+
+#ifdef FILE_DUMP_ENABLE
+ char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+ OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format
+ IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format
+
+ bool mIsInFlush; // codec is flush mode
+ bool mReceivedEOS; // EOS is receieved on input port
+ bool mInitNeeded;
+ uint32_t mNewWidth;
+ uint32_t mNewHeight;
+ // The input stream has changed to a different resolution, which is still supported by the
+ // codec. So the codec is switching to decode the new resolution.
+ bool mChangingResolution;
+ bool mFlushNeeded;
+ bool mWaitForI;
+
+ status_t initDecoder();
+ status_t deInitDecoder();
+ status_t setFlushMode();
+ status_t setParams(size_t stride);
+ void logVersion();
+ status_t setNumCores();
+ status_t resetDecoder();
+ status_t resetPlugin();
+ status_t reInitDecoder();
+
+ void setDecodeArgs(
+ ivd_video_decode_ip_t *ps_dec_ip,
+ ivd_video_decode_op_t *ps_dec_op,
+ OMX_BUFFERHEADERTYPE *inHeader,
+ OMX_BUFFERHEADERTYPE *outHeader,
+ size_t timeStampIx);
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG2);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH "/sdcard/media/mpeg2d_input"
+#define INPUT_DUMP_EXT "m2v"
+
+#define GENERATE_FILE_NAMES() { \
+ GETTIME(&mTimeStart, NULL); \
+ strcpy(mInFile, ""); \
+ sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \
+ mTimeStart.tv_sec, mTimeStart.tv_usec, \
+ INPUT_DUMP_EXT); \
+}
+
+#define CREATE_DUMP_FILE(m_filename) { \
+ FILE *fp = fopen(m_filename, "wb"); \
+ if (fp != NULL) { \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not open file %s", m_filename); \
+ } \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size) \
+{ \
+ FILE *fp = fopen(m_filename, "ab"); \
+ if (fp != NULL && m_buf != NULL) { \
+ int i; \
+ i = fwrite(m_buf, 1, m_size, fp); \
+ ALOGD("fwrite ret %d to write %d", i, m_size); \
+ if (i != (int)m_size) { \
+ ALOGD("Error in fwrite, returned %d", i); \
+ perror("Error in write to file"); \
+ } \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not write to file %s", m_filename);\
+ } \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+} // namespace android
+
+#endif // SOFT_MPEG2_H_
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index 6e6a78a..a35909e 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -139,7 +139,7 @@
uint32_t height = mImg->d_h;
outInfo = *outQueue.begin();
outHeader = outInfo->mHeader;
- CHECK_EQ(mImg->fmt, IMG_FMT_I420);
+ CHECK_EQ(mImg->fmt, VPX_IMG_FMT_I420);
handlePortSettingsChange(portWillReset, width, height);
if (*portWillReset) {
return true;
@@ -151,12 +151,12 @@
outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv;
uint8_t *dst = outHeader->pBuffer;
- const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y];
- const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U];
- const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V];
- size_t srcYStride = mImg->stride[PLANE_Y];
- size_t srcUStride = mImg->stride[PLANE_U];
- size_t srcVStride = mImg->stride[PLANE_V];
+ const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y];
+ const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U];
+ const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V];
+ size_t srcYStride = mImg->stride[VPX_PLANE_Y];
+ size_t srcUStride = mImg->stride[VPX_PLANE_U];
+ size_t srcVStride = mImg->stride[VPX_PLANE_V];
copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
mImg = NULL;
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
index 970acf3..e654843 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
@@ -661,7 +661,8 @@
BufferInfo *outputBufferInfo = *outputBufferInfoQueue.begin();
OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader;
- if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ if ((inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) &&
+ inputBufferHeader->nFilledLen == 0) {
inputBufferInfoQueue.erase(inputBufferInfoQueue.begin());
inputBufferInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inputBufferHeader);
@@ -762,6 +763,9 @@
encoded_packet->data.frame.sz);
outputBufferInfo->mOwnedByUs = false;
outputBufferInfoQueue.erase(outputBufferInfoQueue.begin());
+ if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+ }
notifyFillBufferDone(outputBufferHeader);
}
}
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
index 8f356b6..c559682 100644
--- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -364,7 +364,7 @@
} else {
numFrames = vorbis_dsp_pcmout(
mState, (int16_t *)outHeader->pBuffer,
- kMaxNumSamplesPerBuffer);
+ (kMaxNumSamplesPerBuffer / mVi->channels));
if (numFrames < 0) {
ALOGE("vorbis_dsp_pcmout returned %d", numFrames);
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index 59a64ba..4f7c48f 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -9,6 +9,9 @@
$(TOP)/frameworks/native/include/media/openmax \
$(TOP)/hardware/msm7k
+LOCAL_CFLAGS += -Werror
+LOCAL_CLANG := true
+
LOCAL_MODULE:= libstagefright_color_conversion
include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml
old mode 100644
new mode 100755
index 7e9fa18..740f96b
--- a/media/libstagefright/data/media_codecs_google_video.xml
+++ b/media/libstagefright/data/media_codecs_google_video.xml
@@ -16,6 +16,15 @@
<Included>
<Decoders>
+ <MediaCodec name="OMX.google.mpeg2.decoder" type="video/mpeg2">
+ <!-- profiles and levels: ProfileMain : LevelHL -->
+ <Limit name="size" min="16x16" max="1920x1088" />
+ <Limit name="alignment" value="2x2" />
+ <Limit name="block-size" value="16x16" />
+ <Limit name="blocks-per-second" range="1-244800" />
+ <Limit name="bitrate" range="1-20000000" />
+ <Feature name="adaptive-playback" />
+ </MediaCodec>
<MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es">
<!-- profiles and levels: ProfileSimple : Level3 -->
<Limit name="size" min="2x2" max="352x288" />
@@ -34,12 +43,12 @@
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="OMX.google.h264.decoder" type="video/avc">
- <!-- profiles and levels: ProfileBaseline : Level51 -->
- <Limit name="size" min="2x2" max="2048x2048" />
+ <!-- profiles and levels: ProfileHigh : Level41 -->
+ <Limit name="size" min="16x16" max="1920x1088" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="blocks-per-second" range="1-983040" />
- <Limit name="bitrate" range="1-40000000" />
+ <Limit name="blocks-per-second" range="1-244800" />
+ <Limit name="bitrate" range="1-12000000" />
<Feature name="adaptive-playback" />
</MediaCodec>
<MediaCodec name="OMX.google.hevc.decoder" type="video/hevc">
@@ -78,12 +87,12 @@
<Limit name="bitrate" range="1-128000" />
</MediaCodec>
<MediaCodec name="OMX.google.h264.encoder" type="video/avc">
- <!-- profiles and levels: ProfileBaseline : Level2 -->
- <Limit name="size" min="16x16" max="896x896" />
- <Limit name="alignment" value="16x16" />
+ <!-- profiles and levels: ProfileBaseline : Level41 -->
+ <Limit name="size" min="16x16" max="1920x1088" />
+ <Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="blocks-per-second" range="1-11880" />
- <Limit name="bitrate" range="1-2000000" />
+ <Limit name="blocks-per-second" range="1-244800" />
+ <Limit name="bitrate" range="1-12000000" />
</MediaCodec>
<MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es">
<!-- profiles and levels: ProfileCore : Level2 -->
diff --git a/media/libstagefright/filters/Android.mk b/media/libstagefright/filters/Android.mk
index 36ab444..179f054 100644
--- a/media/libstagefright/filters/Android.mk
+++ b/media/libstagefright/filters/Android.mk
@@ -20,7 +20,8 @@
intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
LOCAL_C_INCLUDES += $(intermediates)
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE:= libstagefright_mediafilter
diff --git a/media/libstagefright/filters/ColorConvert.cpp b/media/libstagefright/filters/ColorConvert.cpp
index a5039f9..a8d5dd2 100644
--- a/media/libstagefright/filters/ColorConvert.cpp
+++ b/media/libstagefright/filters/ColorConvert.cpp
@@ -93,8 +93,8 @@
void convertRGBAToARGB(
uint8_t *src, int32_t width, int32_t height, uint32_t stride,
uint8_t *dest) {
- for (size_t i = 0; i < height; ++i) {
- for (size_t j = 0; j < width; ++j) {
+ for (int32_t i = 0; i < height; ++i) {
+ for (int32_t j = 0; j < width; ++j) {
uint8_t r = *src++;
uint8_t g = *src++;
uint8_t b = *src++;
diff --git a/media/libstagefright/filters/GraphicBufferListener.cpp b/media/libstagefright/filters/GraphicBufferListener.cpp
index 66374ba..a606315 100644
--- a/media/libstagefright/filters/GraphicBufferListener.cpp
+++ b/media/libstagefright/filters/GraphicBufferListener.cpp
@@ -40,7 +40,7 @@
status_t err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
if (err != NO_ERROR) {
- ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+ ALOGE("Unable to set BQ max acquired buffer count to %zu: %d",
bufferCount, err);
return err;
}
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index 0a09575..0cf6b06 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -76,6 +76,11 @@
(new AMessage(kWhatCreateInputSurface, this))->post();
}
+void MediaFilter::initiateSetInputSurface(
+ const sp<PersistentSurface> & /* surface */) {
+ ALOGW("initiateSetInputSurface() unsupported");
+}
+
void MediaFilter::initiateStart() {
(new AMessage(kWhatStart, this))->post();
}
@@ -804,7 +809,7 @@
eosBuf->mGeneration = mGeneration;
eosBuf->mData->setRange(0, 0);
postDrainThisBuffer(eosBuf);
- ALOGV("Posted EOS on output buffer %zu", eosBuf->mBufferID);
+ ALOGV("Posted EOS on output buffer %u", eosBuf->mBufferID);
}
mPortEOS[kPortIndexOutput] = true;
diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp
index ec4a960..0d1cea4 100644
--- a/media/libstagefright/foundation/ADebug.cpp
+++ b/media/libstagefright/foundation/ADebug.cpp
@@ -19,6 +19,7 @@
#include <ctype.h>
#define LOG_TAG "ADebug"
+#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/misc.h>
@@ -113,5 +114,43 @@
return debugName;
}
+//static
+bool ADebug::getExperimentFlag(
+ bool allow, const char *name, uint64_t modulo,
+ uint64_t limit, uint64_t plus, uint64_t timeDivisor) {
+ static volatile int32_t haveSerial = 0;
+ static uint64_t serialNum;
+ if (!android_atomic_acquire_load(&haveSerial)) {
+ // calculate initial counter value based on serial number
+ static char serial[PROPERTY_VALUE_MAX];
+ property_get("ro.serialno", serial, "0");
+ uint64_t num = 0; // it is okay for this number to overflow
+ for (size_t i = 0; i < NELEM(serial) && serial[i] != '\0'; ++i) {
+ const char &c = serial[i];
+ // try to use most letters of serialno
+ if (isdigit(c)) {
+ num = num * 10 + (c - '0');
+ } else if (islower(c)) {
+ num = num * 26 + (c - 'a');
+ } else if (isupper(c)) {
+ num = num * 26 + (c - 'A');
+ } else {
+ num = num * 256 + c;
+ }
+ }
+ ALOGI("got serial");
+ serialNum = num;
+ android_atomic_release_store(1, &haveSerial);
+ }
+ ALOGI("serial: %llu, time: %llu", (long long)serialNum, (long long)time(NULL));
+ // MINOR: use modulo for counter and time, so that their sum does not
+ // roll over, and mess up the correlation between related experiments.
+ // e.g. keep (a mod 2N) = 0 impl (a mod N) = 0
+ time_t counter = (time(NULL) / timeDivisor) % modulo + plus + serialNum % modulo;
+ bool enable = allow && (counter % modulo < limit);
+ ALOGI("experiment '%s': %s", name, enable ? "ENABLED" : "disabled");
+ return enable;
+}
+
} // namespace android
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 08355c7..c68264c 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -29,7 +29,8 @@
liblog \
libpowermanager
-LOCAL_CFLAGS += -Wno-multichar -Werror
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE:= libstagefright_foundation
diff --git a/media/libstagefright/http/Android.mk b/media/libstagefright/http/Android.mk
index 7f3307d..5fb51c1 100644
--- a/media/libstagefright/http/Android.mk
+++ b/media/libstagefright/http/Android.mk
@@ -21,7 +21,8 @@
LOCAL_CFLAGS += -Wno-multichar
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/http/MediaHTTP.cpp b/media/libstagefright/http/MediaHTTP.cpp
index bb89567..2d9b3d4 100644
--- a/media/libstagefright/http/MediaHTTP.cpp
+++ b/media/libstagefright/http/MediaHTTP.cpp
@@ -30,12 +30,11 @@
namespace android {
MediaHTTP::MediaHTTP(const sp<IMediaHTTPConnection> &conn)
- : mInitCheck(NO_INIT),
+ : mInitCheck((conn != NULL) ? OK : NO_INIT),
mHTTPConnection(conn),
mCachedSizeValid(false),
mCachedSize(0ll),
mDrmManagerClient(NULL) {
- mInitCheck = OK;
}
MediaHTTP::~MediaHTTP() {
@@ -171,6 +170,10 @@
}
String8 MediaHTTP::getUri() {
+ if (mInitCheck != OK) {
+ return String8::empty();
+ }
+
String8 uri;
if (OK == mHTTPConnection->getUri(&uri)) {
return uri;
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index 93b7935..fc85835 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -3,6 +3,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ HTTPDownloader.cpp \
LiveDataSource.cpp \
LiveSession.cpp \
M3UParser.cpp \
@@ -12,7 +13,8 @@
$(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
LOCAL_SHARED_LIBRARIES := \
libbinder \
diff --git a/media/libstagefright/httplive/HTTPDownloader.cpp b/media/libstagefright/httplive/HTTPDownloader.cpp
new file mode 100644
index 0000000..3b44bae
--- /dev/null
+++ b/media/libstagefright/httplive/HTTPDownloader.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HTTPDownloader"
+#include <utils/Log.h>
+
+#include "HTTPDownloader.h"
+#include "M3UParser.h"
+
+#include <media/IMediaHTTPConnection.h>
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <openssl/aes.h>
+#include <openssl/md5.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+HTTPDownloader::HTTPDownloader(
+ const sp<IMediaHTTPService> &httpService,
+ const KeyedVector<String8, String8> &headers) :
+ mHTTPDataSource(new MediaHTTP(httpService->makeHTTPConnection())),
+ mExtraHeaders(headers),
+ mDisconnecting(false) {
+}
+
+void HTTPDownloader::reconnect() {
+ AutoMutex _l(mLock);
+ mDisconnecting = false;
+}
+
+void HTTPDownloader::disconnect() {
+ {
+ AutoMutex _l(mLock);
+ mDisconnecting = true;
+ }
+ mHTTPDataSource->disconnect();
+}
+
+bool HTTPDownloader::isDisconnecting() {
+ AutoMutex _l(mLock);
+ return mDisconnecting;
+}
+
+/*
+ * Illustration of parameters:
+ *
+ * 0 `range_offset`
+ * +------------+-------------------------------------------------------+--+--+
+ * | | | next block to fetch | | |
+ * | | `source` handle => `out` buffer | | | |
+ * | `url` file |<--------- buffer size --------->|<--- `block_size` -->| | |
+ * | |<----------- `range_length` / buffer capacity ----------->| |
+ * |<------------------------------ file_size ------------------------------->|
+ *
+ * Special parameter values:
+ * - range_length == -1 means entire file
+ * - block_size == 0 means entire range
+ *
+ */
+ssize_t HTTPDownloader::fetchBlock(
+ const char *url, sp<ABuffer> *out,
+ int64_t range_offset, int64_t range_length,
+ uint32_t block_size, /* download block size */
+ String8 *actualUrl,
+ bool reconnect /* force connect HTTP when resuing source */) {
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ off64_t size;
+
+ if (reconnect) {
+ if (!strncasecmp(url, "file://", 7)) {
+ mDataSource = new FileSource(url + 7);
+ } else if (strncasecmp(url, "http://", 7)
+ && strncasecmp(url, "https://", 8)) {
+ return ERROR_UNSUPPORTED;
+ } else {
+ KeyedVector<String8, String8> headers = mExtraHeaders;
+ if (range_offset > 0 || range_length >= 0) {
+ headers.add(
+ String8("Range"),
+ String8(
+ AStringPrintf(
+ "bytes=%lld-%s",
+ range_offset,
+ range_length < 0
+ ? "" : AStringPrintf("%lld",
+ range_offset + range_length - 1).c_str()).c_str()));
+ }
+
+ status_t err = mHTTPDataSource->connect(url, &headers);
+
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ if (err != OK) {
+ return err;
+ }
+
+ mDataSource = mHTTPDataSource;
+ }
+ }
+
+ status_t getSizeErr = mDataSource->getSize(&size);
+
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ if (getSizeErr != OK) {
+ size = 65536;
+ }
+
+ sp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);
+ if (*out == NULL) {
+ buffer->setRange(0, 0);
+ }
+
+ ssize_t bytesRead = 0;
+ // adjust range_length if only reading partial block
+ if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) {
+ range_length = buffer->size() + block_size;
+ }
+ for (;;) {
+ // Only resize when we don't know the size.
+ size_t bufferRemaining = buffer->capacity() - buffer->size();
+ if (bufferRemaining == 0 && getSizeErr != OK) {
+ size_t bufferIncrement = buffer->size() / 2;
+ if (bufferIncrement < 32768) {
+ bufferIncrement = 32768;
+ }
+ bufferRemaining = bufferIncrement;
+
+ ALOGV("increasing download buffer to %zu bytes",
+ buffer->size() + bufferRemaining);
+
+ sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
+ memcpy(copy->data(), buffer->data(), buffer->size());
+ copy->setRange(0, buffer->size());
+
+ buffer = copy;
+ }
+
+ size_t maxBytesToRead = bufferRemaining;
+ if (range_length >= 0) {
+ int64_t bytesLeftInRange = range_length - buffer->size();
+ if (bytesLeftInRange < (int64_t)maxBytesToRead) {
+ maxBytesToRead = bytesLeftInRange;
+
+ if (bytesLeftInRange == 0) {
+ break;
+ }
+ }
+ }
+
+ // The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)
+ // to help us break out of the loop.
+ ssize_t n = mDataSource->readAt(
+ buffer->size(), buffer->data() + buffer->size(),
+ maxBytesToRead);
+
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ if (n < 0) {
+ return n;
+ }
+
+ if (n == 0) {
+ break;
+ }
+
+ buffer->setRange(0, buffer->size() + (size_t)n);
+ bytesRead += n;
+ }
+
+ *out = buffer;
+ if (actualUrl != NULL) {
+ *actualUrl = mDataSource->getUri();
+ if (actualUrl->isEmpty()) {
+ *actualUrl = url;
+ }
+ }
+
+ return bytesRead;
+}
+
+ssize_t HTTPDownloader::fetchFile(
+ const char *url, sp<ABuffer> *out, String8 *actualUrl) {
+ ssize_t err = fetchBlock(url, out, 0, -1, 0, actualUrl, true /* reconnect */);
+
+ // close off the connection after use
+ mHTTPDataSource->disconnect();
+
+ return err;
+}
+
+sp<M3UParser> HTTPDownloader::fetchPlaylist(
+ const char *url, uint8_t *curPlaylistHash, bool *unchanged) {
+ ALOGV("fetchPlaylist '%s'", url);
+
+ *unchanged = false;
+
+ sp<ABuffer> buffer;
+ String8 actualUrl;
+ ssize_t err = fetchFile(url, &buffer, &actualUrl);
+
+ // close off the connection after use
+ mHTTPDataSource->disconnect();
+
+ if (err <= 0) {
+ return NULL;
+ }
+
+ // MD5 functionality is not available on the simulator, treat all
+ // playlists as changed.
+
+#if defined(HAVE_ANDROID_OS)
+ uint8_t hash[16];
+
+ MD5_CTX m;
+ MD5_Init(&m);
+ MD5_Update(&m, buffer->data(), buffer->size());
+
+ MD5_Final(hash, &m);
+
+ if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) {
+ // playlist unchanged
+ *unchanged = true;
+
+ return NULL;
+ }
+
+ if (curPlaylistHash != NULL) {
+ memcpy(curPlaylistHash, hash, sizeof(hash));
+ }
+#endif
+
+ sp<M3UParser> playlist =
+ new M3UParser(actualUrl.string(), buffer->data(), buffer->size());
+
+ if (playlist->initCheck() != OK) {
+ ALOGE("failed to parse .m3u8 playlist");
+
+ return NULL;
+ }
+
+ return playlist;
+}
+
+} // namespace android
diff --git a/media/libstagefright/httplive/HTTPDownloader.h b/media/libstagefright/httplive/HTTPDownloader.h
new file mode 100644
index 0000000..1db4a48
--- /dev/null
+++ b/media/libstagefright/httplive/HTTPDownloader.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HTTP_DOWNLOADER_H_
+
+#define HTTP_DOWNLOADER_H_
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+class DataSource;
+struct HTTPBase;
+struct IMediaHTTPService;
+struct M3UParser;
+
+struct HTTPDownloader : public RefBase {
+ HTTPDownloader(
+ const sp<IMediaHTTPService> &httpService,
+ const KeyedVector<String8, String8> &headers);
+
+ void reconnect();
+ void disconnect();
+ bool isDisconnecting();
+ // If given a non-zero block_size (default 0), it is used to cap the number of
+ // bytes read in from the DataSource. If given a non-NULL buffer, new content
+ // is read into the end.
+ //
+ // The DataSource we read from is responsible for signaling error or EOF to help us
+ // break out of the read loop. The DataSource can be returned to the caller, so
+ // that the caller can reuse it for subsequent fetches (within the initially
+ // requested range).
+ //
+ // For reused HTTP sources, the caller must download a file sequentially without
+ // any overlaps or gaps to prevent reconnection.
+ ssize_t fetchBlock(
+ const char *url,
+ sp<ABuffer> *out,
+ int64_t range_offset, /* open file at range_offset */
+ int64_t range_length, /* open file for range_length (-1: entire file) */
+ uint32_t block_size, /* download block size (0: entire range) */
+ String8 *actualUrl, /* returns actual URL */
+ bool reconnect /* force connect http */
+ );
+
+ // simplified version to fetch a single file
+ ssize_t fetchFile(
+ const char *url,
+ sp<ABuffer> *out,
+ String8 *actualUrl = NULL);
+
+ // fetch a playlist file
+ sp<M3UParser> fetchPlaylist(
+ const char *url, uint8_t *curPlaylistHash, bool *unchanged);
+
+private:
+ sp<HTTPBase> mHTTPDataSource;
+ sp<DataSource> mDataSource;
+ KeyedVector<String8, String8> mExtraHeaders;
+
+ Mutex mLock;
+ bool mDisconnecting;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HTTPDownloader);
+};
+
+} // namespace android
+
+#endif // HTTP_DOWNLOADER_H_
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 2d93152..64a8532 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -19,25 +19,19 @@
#include <utils/Log.h>
#include "LiveSession.h"
-
+#include "HTTPDownloader.h"
#include "M3UParser.h"
#include "PlaylistFetcher.h"
-#include "include/HTTPBase.h"
#include "mpeg2ts/AnotherPacketSource.h"
#include <cutils/properties.h>
-#include <media/IMediaHTTPConnection.h>
#include <media/IMediaHTTPService.h>
-#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
@@ -45,8 +39,6 @@
#include <ctype.h>
#include <inttypes.h>
-#include <openssl/aes.h>
-#include <openssl/md5.h>
namespace android {
@@ -66,12 +58,13 @@
BandwidthEstimator();
void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
- bool estimateBandwidth(int32_t *bandwidth);
+ bool estimateBandwidth(int32_t *bandwidth, bool *isStable = NULL);
private:
// Bandwidth estimation parameters
- static const int32_t kMaxBandwidthHistoryItems = 20;
- static const int64_t kMaxBandwidthHistoryWindowUs = 5000000ll; // 5 sec
+ static const int32_t kMinBandwidthHistoryItems = 20;
+ static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec
+ static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec
struct BandwidthEntry {
int64_t mDelayUs;
@@ -80,6 +73,9 @@
Mutex mLock;
List<BandwidthEntry> mBandwidthHistory;
+ List<int32_t> mPrevEstimates;
+ bool mHasNewSample;
+ bool mIsStable;
int64_t mTotalTransferTimeUs;
size_t mTotalTransferBytes;
@@ -87,6 +83,8 @@
};
LiveSession::BandwidthEstimator::BandwidthEstimator() :
+ mHasNewSample(false),
+ mIsStable(true),
mTotalTransferTimeUs(0),
mTotalTransferBytes(0) {
}
@@ -101,12 +99,23 @@
mTotalTransferTimeUs += delayUs;
mTotalTransferBytes += numBytes;
mBandwidthHistory.push_back(entry);
+ mHasNewSample = true;
+ // Remove no more than 10% of total transfer time at a time
+ // to avoid sudden jump on bandwidth estimation. There might
+ // be long blocking reads that takes up signification time,
+ // we have to keep a longer window in that case.
+ int64_t bandwidthHistoryWindowUs = mTotalTransferTimeUs * 9 / 10;
+ if (bandwidthHistoryWindowUs < kMinBandwidthHistoryWindowUs) {
+ bandwidthHistoryWindowUs = kMinBandwidthHistoryWindowUs;
+ } else if (bandwidthHistoryWindowUs > kMaxBandwidthHistoryWindowUs) {
+ bandwidthHistoryWindowUs = kMaxBandwidthHistoryWindowUs;
+ }
// trim old samples, keeping at least kMaxBandwidthHistoryItems samples,
// and total transfer time at least kMaxBandwidthHistoryWindowUs.
- while (mBandwidthHistory.size() > kMaxBandwidthHistoryItems) {
+ while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) {
List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
- if (mTotalTransferTimeUs - it->mDelayUs < kMaxBandwidthHistoryWindowUs) {
+ if (mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) {
break;
}
mTotalTransferTimeUs -= it->mDelayUs;
@@ -115,14 +124,67 @@
}
}
-bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps) {
+bool LiveSession::BandwidthEstimator::estimateBandwidth(
+ int32_t *bandwidthBps, bool *isStable) {
AutoMutex autoLock(mLock);
if (mBandwidthHistory.size() < 2) {
return false;
}
+ if (!mHasNewSample) {
+ *bandwidthBps = *(--mPrevEstimates.end());
+ if (isStable) {
+ *isStable = mIsStable;
+ }
+ return true;
+ }
+
*bandwidthBps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+ mPrevEstimates.push_back(*bandwidthBps);
+ while (mPrevEstimates.size() > 3) {
+ mPrevEstimates.erase(mPrevEstimates.begin());
+ }
+ mHasNewSample = false;
+
+ int32_t minEstimate = -1, maxEstimate = -1;
+ List<int32_t>::iterator it;
+ for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) {
+ int32_t estimate = *it;
+ if (minEstimate < 0 || minEstimate > estimate) {
+ minEstimate = estimate;
+ }
+ if (maxEstimate < 0 || maxEstimate < estimate) {
+ maxEstimate = estimate;
+ }
+ }
+ mIsStable = (maxEstimate <= minEstimate * 4 / 3);
+ if (isStable) {
+ *isStable = mIsStable;
+ }
+#if 0
+ {
+ char dumpStr[1024] = {0};
+ size_t itemIdx = 0;
+ size_t histSize = mBandwidthHistory.size();
+ sprintf(dumpStr, "estimate bps=%d stable=%d history (n=%d): {",
+ *bandwidthBps, mIsStable, histSize);
+ List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
+ for (; it != mBandwidthHistory.end(); ++it) {
+ if (itemIdx > 50) {
+ sprintf(dumpStr + strlen(dumpStr),
+ "...(%zd more items)... }", histSize - itemIdx);
+ break;
+ }
+ sprintf(dumpStr + strlen(dumpStr), "%dk/%.3fs%s",
+ it->mNumBytes / 1024,
+ (double)it->mDelayUs * 1.0e-6,
+ (it == (--mBandwidthHistory.end())) ? "}" : ", ");
+ itemIdx++;
+ }
+ ALOGE(dumpStr);
+ }
+#endif
return true;
}
@@ -135,12 +197,47 @@
return "timeUsAudio";
case STREAMTYPE_SUBTITLES:
return "timeUsSubtitle";
+ case STREAMTYPE_METADATA:
+ return "timeUsMetadata"; // unused
default:
TRESPASS();
}
return NULL;
}
+//static
+const char *LiveSession::getNameForStream(StreamType type) {
+ switch (type) {
+ case STREAMTYPE_VIDEO:
+ return "video";
+ case STREAMTYPE_AUDIO:
+ return "audio";
+ case STREAMTYPE_SUBTITLES:
+ return "subs";
+ case STREAMTYPE_METADATA:
+ return "metadata";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+//static
+ATSParser::SourceType LiveSession::getSourceTypeForStream(StreamType type) {
+ switch (type) {
+ case STREAMTYPE_VIDEO:
+ return ATSParser::VIDEO;
+ case STREAMTYPE_AUDIO:
+ return ATSParser::AUDIO;
+ case STREAMTYPE_METADATA:
+ return ATSParser::META;
+ case STREAMTYPE_SUBTITLES:
+ default:
+ TRESPASS();
+ }
+ return ATSParser::NUM_SOURCE_TYPES; // should not reach here
+}
+
LiveSession::LiveSession(
const sp<AMessage> ¬ify, uint32_t flags,
const sp<IMediaHTTPService> &httpService)
@@ -151,11 +248,12 @@
mInPreparationPhase(true),
mPollBufferingGeneration(0),
mPrevBufferPercentage(-1),
- mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())),
mCurBandwidthIndex(-1),
mOrigBandwidthIndex(-1),
mLastBandwidthBps(-1ll),
mBandwidthEstimator(new BandwidthEstimator()),
+ mMaxWidth(720),
+ mMaxHeight(480),
mStreamMask(0),
mNewStreamMask(0),
mSwapMask(0),
@@ -170,12 +268,13 @@
mUpSwitchMargin(kUpSwitchMarginUs),
mFirstTimeUsValid(false),
mFirstTimeUs(0),
- mLastSeekTimeUs(0) {
+ mLastSeekTimeUs(0),
+ mHasMetadata(false) {
mStreams[kAudioIndex] = StreamItem("audio");
mStreams[kVideoIndex] = StreamItem("video");
mStreams[kSubtitleIndex] = StreamItem("subtitles");
- for (size_t i = 0; i < kMaxStreams; ++i) {
+ for (size_t i = 0; i < kNumSources; ++i) {
mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
}
@@ -187,12 +286,30 @@
}
}
+int64_t LiveSession::calculateMediaTimeUs(
+ int64_t firstTimeUs, int64_t timeUs, int32_t discontinuitySeq) {
+ if (timeUs >= firstTimeUs) {
+ timeUs -= firstTimeUs;
+ } else {
+ timeUs = 0;
+ }
+ timeUs += mLastSeekTimeUs;
+ if (mDiscontinuityOffsetTimesUs.indexOfKey(discontinuitySeq) >= 0) {
+ timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq);
+ }
+ return timeUs;
+}
+
status_t LiveSession::dequeueAccessUnit(
StreamType stream, sp<ABuffer> *accessUnit) {
status_t finalResult = OK;
sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
- ssize_t idx = typeToIndex(stream);
+ ssize_t streamIdx = typeToIndex(stream);
+ if (streamIdx < 0) {
+ return BAD_VALUE;
+ }
+ const char *streamStr = getNameForStream(stream);
// Do not let client pull data if we don't have data packets yet.
// We might only have a format discontinuity queued without data.
// When NuPlayerDecoder dequeues the format discontinuity, it will
@@ -200,6 +317,9 @@
// thinks it can do seamless change, so will not shutdown decoder.
// When the actual format arrives, it can't handle it and get stuck.
if (!packetSource->hasDataBufferAvailable(&finalResult)) {
+ ALOGV("[%s] dequeueAccessUnit: no buffer available (finalResult=%d)",
+ streamStr, finalResult);
+
if (finalResult == OK) {
return -EAGAIN;
} else {
@@ -212,26 +332,6 @@
status_t err = packetSource->dequeueAccessUnit(accessUnit);
- size_t streamIdx;
- const char *streamStr;
- switch (stream) {
- case STREAMTYPE_AUDIO:
- streamIdx = kAudioIndex;
- streamStr = "audio";
- break;
- case STREAMTYPE_VIDEO:
- streamIdx = kVideoIndex;
- streamStr = "video";
- break;
- case STREAMTYPE_SUBTITLES:
- streamIdx = kSubtitleIndex;
- streamStr = "subs";
- break;
- default:
- TRESPASS();
- }
-
- StreamItem& strm = mStreams[streamIdx];
if (err == INFO_DISCONTINUITY) {
// adaptive streaming, discontinuities in the playlist
int32_t type;
@@ -249,9 +349,11 @@
} else if (err == OK) {
if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
- int64_t timeUs;
+ int64_t timeUs, originalTimeUs;
int32_t discontinuitySeq = 0;
+ StreamItem& strm = mStreams[streamIdx];
CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs));
+ originalTimeUs = timeUs;
(*accessUnit)->meta()->findInt32("discontinuitySeq", &discontinuitySeq);
if (discontinuitySeq > (int32_t) strm.mCurDiscontinuitySeq) {
int64_t offsetTimeUs;
@@ -261,7 +363,8 @@
offsetTimeUs = 0;
}
- if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) {
+ if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0
+ && strm.mLastDequeuedTimeUs >= 0) {
int64_t firstTimeUs;
firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq);
offsetTimeUs += strm.mLastDequeuedTimeUs - firstTimeUs;
@@ -293,17 +396,10 @@
}
strm.mLastDequeuedTimeUs = timeUs;
- if (timeUs >= firstTimeUs) {
- timeUs -= firstTimeUs;
- } else {
- timeUs = 0;
- }
- timeUs += mLastSeekTimeUs;
- if (mDiscontinuityOffsetTimesUs.indexOfKey(discontinuitySeq) >= 0) {
- timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq);
- }
+ timeUs = calculateMediaTimeUs(firstTimeUs, timeUs, discontinuitySeq);
- ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs);
+ ALOGV("[%s] dequeueAccessUnit: time %lld us, original %lld us",
+ streamStr, (long long)timeUs, (long long)originalTimeUs);
(*accessUnit)->meta()->setInt64("timeUs", timeUs);
mLastDequeuedTimeUs = timeUs;
mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
@@ -316,6 +412,17 @@
(*accessUnit)->meta()->setInt32(
"trackIndex", mPlaylist->getSelectedIndex());
(*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs);
+ } else if (stream == STREAMTYPE_METADATA) {
+ HLSTime mdTime((*accessUnit)->meta());
+ if (mDiscontinuityAbsStartTimesUs.indexOfKey(mdTime.mSeq) < 0) {
+ packetSource->requeueAccessUnit((*accessUnit));
+ return -EAGAIN;
+ } else {
+ int64_t firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(mdTime.mSeq);
+ int64_t timeUs = calculateMediaTimeUs(firstTimeUs, mdTime.mTimeUs, mdTime.mSeq);
+ (*accessUnit)->meta()->setInt64("timeUs", timeUs);
+ (*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs);
+ }
}
} else {
ALOGI("[%s] encountered error %d", streamStr, err);
@@ -340,13 +447,16 @@
if (stream == STREAMTYPE_AUDIO) {
// set AAC input buffer size to 32K bytes (256kbps x 1sec)
meta->setInt32(kKeyMaxInputSize, 32 * 1024);
+ } else if (stream == STREAMTYPE_VIDEO) {
+ meta->setInt32(kKeyMaxWidth, mMaxWidth);
+ meta->setInt32(kKeyMaxHeight, mMaxHeight);
}
return convertMetaDataToMessage(meta, format);
}
-sp<HTTPBase> LiveSession::getHTTPDataSource() {
- return new MediaHTTP(mHTTPService->makeHTTPConnection());
+sp<HTTPDownloader> LiveSession::getHTTPDownloader() {
+ return new HTTPDownloader(mHTTPService, mExtraHeaders);
}
void LiveSession::connectAsync(
@@ -409,7 +519,7 @@
if (lastDequeueMeta == NULL) {
// this means we don't have enough cushion, try again later
ALOGV("[%s] up switching failed due to insufficient buffer",
- stream == STREAMTYPE_AUDIO ? "audio" : "video");
+ getNameForStream(stream));
return false;
}
} else {
@@ -428,7 +538,7 @@
if (firstNewMeta[i] == NULL) {
HLSTime dequeueTime(lastDequeueMeta);
ALOGV("[%s] dequeue time (%d, %lld) past start time",
- stream == STREAMTYPE_AUDIO ? "audio" : "video",
+ getNameForStream(stream),
dequeueTime.mSeq, (long long) dequeueTime.mTimeUs);
return false;
}
@@ -493,16 +603,15 @@
case kWhatSeek:
{
- sp<AReplyToken> seekReplyID;
- CHECK(msg->senderAwaitsResponse(&seekReplyID));
- mSeekReplyID = seekReplyID;
+ if (mReconfigurationInProgress) {
+ msg->post(50000);
+ break;
+ }
+
+ CHECK(msg->senderAwaitsResponse(&mSeekReplyID));
mSeekReply = new AMessage;
- status_t err = onSeek(msg);
-
- if (err != OK) {
- msg->post(50000);
- }
+ onSeek(msg);
break;
}
@@ -525,6 +634,11 @@
break;
}
+ ALOGV("fetcher-%d %s",
+ mFetcherInfos[index].mFetcher->getFetcherID(),
+ what == PlaylistFetcher::kWhatPaused ?
+ "paused" : "stopped");
+
if (what == PlaylistFetcher::kWhatStopped) {
mFetcherLooper->unregisterHandler(
mFetcherInfos[index].mFetcher->id());
@@ -544,6 +658,7 @@
if (--mContinuationCounter == 0) {
mContinuation->post();
}
+ ALOGV("%zu fetcher(s) left", mContinuationCounter);
}
break;
}
@@ -636,6 +751,9 @@
int32_t switchGeneration;
CHECK(msg->findInt32("switchGeneration", &switchGeneration));
+ ALOGV("kWhatStartedAt: switchGen=%d, mSwitchGen=%d",
+ switchGeneration, mSwitchGeneration);
+
if (switchGeneration != mSwitchGeneration) {
break;
}
@@ -667,6 +785,7 @@
if (checkSwitchProgress(stopParams, delayUs, &needResumeUntil)) {
// playback time hasn't passed startAt time
if (!needResumeUntil) {
+ ALOGV("finish switch");
for (size_t i = 0; i < kMaxStreams; ++i) {
if ((mSwapMask & indexToType(i))
&& uri == mStreams[i].mNewUri) {
@@ -682,6 +801,7 @@
// Resume fetcher for the original variant; the resumed fetcher should
// continue until the timestamps found in msg, which is stored by the
// new fetcher to indicate where the new variant has started buffering.
+ ALOGV("finish switch with resumeUntilAsync");
for (size_t i = 0; i < mFetcherInfos.size(); i++) {
const FetcherInfo &info = mFetcherInfos.valueAt(i);
if (info.mToBeRemoved) {
@@ -693,8 +813,10 @@
// playback time passed startAt time
if (switchUp) {
// if switching up, cancel and retry if condition satisfies again
+ ALOGV("cancel up switch because we're too late");
cancelBandwidthSwitch(true /* resume */);
} else {
+ ALOGV("retry down switch at next sample");
resumeFetcher(uri, mSwapMask, -1, true /* newUri */);
}
}
@@ -706,6 +828,23 @@
break;
}
+ case PlaylistFetcher::kWhatPlaylistFetched:
+ {
+ onMasterPlaylistFetched(msg);
+ break;
+ }
+
+ case PlaylistFetcher::kWhatMetadataDetected:
+ {
+ if (!mHasMetadata) {
+ mHasMetadata = true;
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatMetadataDetected);
+ notify->post();
+ }
+ break;
+ }
+
default:
TRESPASS();
}
@@ -731,12 +870,6 @@
break;
}
- case kWhatFinishDisconnect2:
- {
- onFinishDisconnect2();
- break;
- }
-
case kWhatPollBuffering:
{
int32_t generation;
@@ -766,7 +899,7 @@
// static
LiveSession::StreamType LiveSession::indexToType(int idx) {
- CHECK(idx >= 0 && idx < kMaxStreams);
+ CHECK(idx >= 0 && idx < kNumSources);
return (StreamType)(1 << idx);
}
@@ -779,6 +912,8 @@
return 1;
case STREAMTYPE_SUBTITLES:
return 2;
+ case STREAMTYPE_METADATA:
+ return 3;
default:
return -1;
};
@@ -786,8 +921,10 @@
}
void LiveSession::onConnect(const sp<AMessage> &msg) {
- AString url;
- CHECK(msg->findString("url", &url));
+ CHECK(msg->findString("url", &mMasterURL));
+
+ // TODO currently we don't know if we are coming here from incognito mode
+ ALOGI("onConnect %s", uriDebugString(mMasterURL).c_str());
KeyedVector<String8, String8> *headers = NULL;
if (!msg->findPointer("headers", (void **)&headers)) {
@@ -799,21 +936,6 @@
headers = NULL;
}
- // TODO currently we don't know if we are coming here from incognito mode
- ALOGI("onConnect %s", uriDebugString(url).c_str());
-
- mMasterURL = url;
-
- bool dummy;
- mPlaylist = fetchPlaylist(url.c_str(), NULL /* curPlaylistHash */, &dummy);
-
- if (mPlaylist == NULL) {
- ALOGE("unable to fetch master playlist %s.", uriDebugString(url).c_str());
-
- postPrepared(ERROR_IO);
- return;
- }
-
// create looper for fetchers
if (mFetcherLooper == NULL) {
mFetcherLooper = new ALooper();
@@ -822,6 +944,31 @@
mFetcherLooper->start(false, false);
}
+ // create fetcher to fetch the master playlist
+ addFetcher(mMasterURL.c_str())->fetchPlaylistAsync();
+}
+
+void LiveSession::onMasterPlaylistFetched(const sp<AMessage> &msg) {
+ AString uri;
+ CHECK(msg->findString("uri", &uri));
+ ssize_t index = mFetcherInfos.indexOfKey(uri);
+ if (index < 0) {
+ ALOGW("fetcher for master playlist is gone.");
+ return;
+ }
+
+ // no longer useful, remove
+ mFetcherLooper->unregisterHandler(mFetcherInfos[index].mFetcher->id());
+ mFetcherInfos.removeItemsAt(index);
+
+ CHECK(msg->findObject("playlist", (sp<RefBase> *)&mPlaylist));
+ if (mPlaylist == NULL) {
+ ALOGE("unable to fetch master playlist %s.",
+ uriDebugString(mMasterURL).c_str());
+
+ postPrepared(ERROR_IO);
+ return;
+ }
// We trust the content provider to make a reasonable choice of preferred
// initial bandwidth by listing it first in the variant playlist.
// At startup we really don't have a good estimate on the available
@@ -830,6 +977,9 @@
size_t initialBandwidth = 0;
size_t initialBandwidthIndex = 0;
+ int32_t maxWidth = 0;
+ int32_t maxHeight = 0;
+
if (mPlaylist->isVariantPlaylist()) {
Vector<BandwidthItem> itemsWithVideo;
for (size_t i = 0; i < mPlaylist->size(); ++i) {
@@ -843,6 +993,14 @@
CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
+ int32_t width, height;
+ if (meta->findInt32("width", &width)) {
+ maxWidth = max(maxWidth, width);
+ }
+ if (meta->findInt32("height", &height)) {
+ maxHeight = max(maxHeight, height);
+ }
+
mBandwidthItems.push(item);
if (mPlaylist->hasType(i, "video")) {
itemsWithVideo.push(item);
@@ -876,6 +1034,9 @@
mBandwidthItems.push(item);
}
+ mMaxWidth = maxWidth > 0 ? maxWidth : mMaxWidth;
+ mMaxHeight = maxHeight > 0 ? maxHeight : mMaxHeight;
+
mPlaylist->pickRandomMediaItems();
changeConfiguration(
0ll /* timeUs */, initialBandwidthIndex, false /* pickTrack */);
@@ -891,22 +1052,26 @@
// cancel buffer polling
cancelPollBuffering();
+ // TRICKY: don't wait for all fetcher to be stopped when disconnecting
+ //
+ // Some fetchers might be stuck in connect/getSize at this point. These
+ // operations will eventually timeout (as we have a timeout set in
+ // MediaHTTPConnection), but we don't want to block the main UI thread
+ // until then. Here we just need to make sure we clear all references
+ // to the fetchers, so that when they finally exit from the blocking
+ // operation, they can be destructed.
+ //
+ // There is one very tricky point though. For this scheme to work, the
+ // fecther must hold a reference to LiveSession, so that LiveSession is
+ // destroyed after fetcher. Otherwise LiveSession would get stuck in its
+ // own destructor when it waits for mFetcherLooper to stop, which still
+ // blocks main UI thread.
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
mFetcherInfos.valueAt(i).mFetcher->stopAsync();
+ mFetcherLooper->unregisterHandler(
+ mFetcherInfos.valueAt(i).mFetcher->id());
}
-
- sp<AMessage> msg = new AMessage(kWhatFinishDisconnect2, this);
-
- mContinuationCounter = mFetcherInfos.size();
- mContinuation = msg;
-
- if (mContinuationCounter == 0) {
- msg->post();
- }
-}
-
-void LiveSession::onFinishDisconnect2() {
- mContinuation.clear();
+ mFetcherInfos.clear();
mPacketSources.valueFor(STREAMTYPE_AUDIO)->signalEOS(ERROR_END_OF_STREAM);
mPacketSources.valueFor(STREAMTYPE_VIDEO)->signalEOS(ERROR_END_OF_STREAM);
@@ -933,7 +1098,8 @@
notify->setInt32("switchGeneration", mSwitchGeneration);
FetcherInfo info;
- info.mFetcher = new PlaylistFetcher(notify, this, uri, mSubtitleGeneration);
+ info.mFetcher = new PlaylistFetcher(
+ notify, this, uri, mCurBandwidthIndex, mSubtitleGeneration);
info.mDurationUs = -1ll;
info.mToBeRemoved = false;
info.mToBeResumed = false;
@@ -944,204 +1110,51 @@
return info.mFetcher;
}
-/*
- * Illustration of parameters:
- *
- * 0 `range_offset`
- * +------------+-------------------------------------------------------+--+--+
- * | | | next block to fetch | | |
- * | | `source` handle => `out` buffer | | | |
- * | `url` file |<--------- buffer size --------->|<--- `block_size` -->| | |
- * | |<----------- `range_length` / buffer capacity ----------->| |
- * |<------------------------------ file_size ------------------------------->|
- *
- * Special parameter values:
- * - range_length == -1 means entire file
- * - block_size == 0 means entire range
- *
- */
-ssize_t LiveSession::fetchFile(
- const char *url, sp<ABuffer> *out,
- int64_t range_offset, int64_t range_length,
- uint32_t block_size, /* download block size */
- sp<DataSource> *source, /* to return and reuse source */
- String8 *actualUrl,
- bool forceConnectHTTP /* force connect HTTP when resuing source */) {
- off64_t size;
- sp<DataSource> temp_source;
- if (source == NULL) {
- source = &temp_source;
- }
-
- if (*source == NULL || forceConnectHTTP) {
- if (!strncasecmp(url, "file://", 7)) {
- *source = new FileSource(url + 7);
- } else if (strncasecmp(url, "http://", 7)
- && strncasecmp(url, "https://", 8)) {
- return ERROR_UNSUPPORTED;
- } else {
- KeyedVector<String8, String8> headers = mExtraHeaders;
- if (range_offset > 0 || range_length >= 0) {
- headers.add(
- String8("Range"),
- String8(
- AStringPrintf(
- "bytes=%lld-%s",
- range_offset,
- range_length < 0
- ? "" : AStringPrintf("%lld",
- range_offset + range_length - 1).c_str()).c_str()));
- }
-
- HTTPBase* httpDataSource =
- (*source == NULL) ? mHTTPDataSource.get() : (HTTPBase*)source->get();
- status_t err = httpDataSource->connect(url, &headers);
-
- if (err != OK) {
- return err;
- }
-
- if (*source == NULL) {
- *source = mHTTPDataSource;
- }
- }
- }
-
- status_t getSizeErr = (*source)->getSize(&size);
- if (getSizeErr != OK) {
- size = 65536;
- }
-
- sp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);
- if (*out == NULL) {
- buffer->setRange(0, 0);
- }
-
- ssize_t bytesRead = 0;
- // adjust range_length if only reading partial block
- if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) {
- range_length = buffer->size() + block_size;
- }
- for (;;) {
- // Only resize when we don't know the size.
- size_t bufferRemaining = buffer->capacity() - buffer->size();
- if (bufferRemaining == 0 && getSizeErr != OK) {
- size_t bufferIncrement = buffer->size() / 2;
- if (bufferIncrement < 32768) {
- bufferIncrement = 32768;
- }
- bufferRemaining = bufferIncrement;
-
- ALOGV("increasing download buffer to %zu bytes",
- buffer->size() + bufferRemaining);
-
- sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
- memcpy(copy->data(), buffer->data(), buffer->size());
- copy->setRange(0, buffer->size());
-
- buffer = copy;
- }
-
- size_t maxBytesToRead = bufferRemaining;
- if (range_length >= 0) {
- int64_t bytesLeftInRange = range_length - buffer->size();
- if (bytesLeftInRange < (int64_t)maxBytesToRead) {
- maxBytesToRead = bytesLeftInRange;
-
- if (bytesLeftInRange == 0) {
- break;
- }
- }
- }
-
- // The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)
- // to help us break out of the loop.
- ssize_t n = (*source)->readAt(
- buffer->size(), buffer->data() + buffer->size(),
- maxBytesToRead);
-
- if (n < 0) {
- return n;
- }
-
- if (n == 0) {
- break;
- }
-
- buffer->setRange(0, buffer->size() + (size_t)n);
- bytesRead += n;
- }
-
- *out = buffer;
- if (actualUrl != NULL) {
- *actualUrl = (*source)->getUri();
- if (actualUrl->isEmpty()) {
- *actualUrl = url;
- }
- }
-
- return bytesRead;
-}
-
-sp<M3UParser> LiveSession::fetchPlaylist(
- const char *url, uint8_t *curPlaylistHash, bool *unchanged) {
- ALOGV("fetchPlaylist '%s'", url);
-
- *unchanged = false;
-
- sp<ABuffer> buffer;
- String8 actualUrl;
- ssize_t err = fetchFile(url, &buffer, 0, -1, 0, NULL, &actualUrl);
-
- // close off the connection after use
- mHTTPDataSource->disconnect();
-
- if (err <= 0) {
- return NULL;
- }
-
- // MD5 functionality is not available on the simulator, treat all
- // playlists as changed.
-
-#if defined(HAVE_ANDROID_OS)
- uint8_t hash[16];
-
- MD5_CTX m;
- MD5_Init(&m);
- MD5_Update(&m, buffer->data(), buffer->size());
-
- MD5_Final(hash, &m);
-
- if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) {
- // playlist unchanged
- *unchanged = true;
-
- return NULL;
- }
-
- if (curPlaylistHash != NULL) {
- memcpy(curPlaylistHash, hash, sizeof(hash));
- }
-#endif
-
- sp<M3UParser> playlist =
- new M3UParser(actualUrl.string(), buffer->data(), buffer->size());
-
- if (playlist->initCheck() != OK) {
- ALOGE("failed to parse .m3u8 playlist");
-
- return NULL;
- }
-
- return playlist;
-}
-
#if 0
static double uniformRand() {
return (double)rand() / RAND_MAX;
}
#endif
+bool LiveSession::UriIsSameAsIndex(const AString &uri, int32_t i, bool newUri) {
+ ALOGI("[timed_id3] i %d UriIsSameAsIndex newUri %s, %s", i,
+ newUri ? "true" : "false",
+ newUri ? mStreams[i].mNewUri.c_str() : mStreams[i].mUri.c_str());
+ return i >= 0
+ && ((!newUri && uri == mStreams[i].mUri)
+ || (newUri && uri == mStreams[i].mNewUri));
+}
+
+sp<AnotherPacketSource> LiveSession::getPacketSourceForStreamIndex(
+ size_t trackIndex, bool newUri) {
+ StreamType type = indexToType(trackIndex);
+ sp<AnotherPacketSource> source = NULL;
+ if (newUri) {
+ source = mPacketSources2.valueFor(type);
+ source->clear();
+ } else {
+ source = mPacketSources.valueFor(type);
+ };
+ return source;
+}
+
+sp<AnotherPacketSource> LiveSession::getMetadataSource(
+ sp<AnotherPacketSource> sources[kNumSources], uint32_t streamMask, bool newUri) {
+ // todo: One case where the following strategy can fail is when audio and video
+ // are in separate playlists, both are transport streams, and the metadata
+ // is actually contained in the audio stream.
+ ALOGV("[timed_id3] getMetadataSourceForUri streamMask %x newUri %s",
+ streamMask, newUri ? "true" : "false");
+
+ if ((sources[kVideoIndex] != NULL) // video fetcher; or ...
+ || (!(streamMask & STREAMTYPE_VIDEO) && sources[kAudioIndex] != NULL)) {
+ // ... audio fetcher for audio only variant
+ return getPacketSourceForStreamIndex(kMetaDataIndex, newUri);
+ }
+
+ return NULL;
+}
+
bool LiveSession::resumeFetcher(
const AString &uri, uint32_t streamMask, int64_t timeUs, bool newUri) {
ssize_t index = mFetcherInfos.indexOfKey(uri);
@@ -1151,28 +1164,26 @@
}
bool resume = false;
- sp<AnotherPacketSource> sources[kMaxStreams];
+ sp<AnotherPacketSource> sources[kNumSources];
for (size_t i = 0; i < kMaxStreams; ++i) {
- if ((streamMask & indexToType(i))
- && ((!newUri && uri == mStreams[i].mUri)
- || (newUri && uri == mStreams[i].mNewUri))) {
+ if ((streamMask & indexToType(i)) && UriIsSameAsIndex(uri, i, newUri)) {
resume = true;
- if (newUri) {
- sources[i] = mPacketSources2.valueFor(indexToType(i));
- sources[i]->clear();
- } else {
- sources[i] = mPacketSources.valueFor(indexToType(i));
- }
+ sources[i] = getPacketSourceForStreamIndex(i, newUri);
}
}
if (resume) {
- ALOGV("resuming fetcher %s, timeUs %lld", uri.c_str(), (long long)timeUs);
+ sp<PlaylistFetcher> &fetcher = mFetcherInfos.editValueAt(index).mFetcher;
SeekMode seekMode = newUri ? kSeekModeNextSample : kSeekModeExactPosition;
- mFetcherInfos.editValueAt(index).mFetcher->startAsync(
+
+ ALOGV("resuming fetcher-%d, timeUs=%lld, seekMode=%d",
+ fetcher->getFetcherID(), (long long)timeUs, seekMode);
+
+ fetcher->startAsync(
sources[kAudioIndex],
sources[kVideoIndex],
sources[kSubtitleIndex],
+ getMetadataSource(sources, streamMask, newUri),
timeUs, -1, -1, seekMode);
}
@@ -1349,16 +1360,10 @@
return audioTime < videoTime ? videoTime : audioTime;
}
-status_t LiveSession::onSeek(const sp<AMessage> &msg) {
+void LiveSession::onSeek(const sp<AMessage> &msg) {
int64_t timeUs;
CHECK(msg->findInt64("timeUs", &timeUs));
-
- if (!mReconfigurationInProgress) {
- changeConfiguration(timeUs);
- return OK;
- } else {
- return -EWOULDBLOCK;
- }
+ changeConfiguration(timeUs);
}
status_t LiveSession::getDuration(int64_t *durationUs) const {
@@ -1389,7 +1394,7 @@
if (mPlaylist == NULL) {
return 0;
} else {
- return mPlaylist->getTrackCount();
+ return mPlaylist->getTrackCount() + (mHasMetadata ? 1 : 0);
}
}
@@ -1397,6 +1402,13 @@
if (mPlaylist == NULL) {
return NULL;
} else {
+ if (trackIndex == mPlaylist->getTrackCount() && mHasMetadata) {
+ sp<AMessage> format = new AMessage();
+ format->setInt32("type", MEDIA_TRACK_TYPE_METADATA);
+ format->setString("language", "und");
+ format->setString("mime", MEDIA_MIMETYPE_DATA_METADATA);
+ return format;
+ }
return mPlaylist->getTrackInfo(trackIndex);
}
}
@@ -1406,6 +1418,9 @@
return INVALID_OPERATION;
}
+ ALOGV("selectTrack: index=%zu, select=%d, mSubtitleGen=%d++",
+ index, select, mSubtitleGeneration);
+
++mSubtitleGeneration;
status_t err = mPlaylist->selectTrack(index, select);
if (err == OK) {
@@ -1426,6 +1441,9 @@
void LiveSession::changeConfiguration(
int64_t timeUs, ssize_t bandwidthIndex, bool pickTrack) {
+ ALOGV("changeConfiguration: timeUs=%lld us, bwIndex=%zd, pickTrack=%d",
+ (long long)timeUs, bandwidthIndex, pickTrack);
+
cancelBandwidthSwitch();
CHECK(!mReconfigurationInProgress);
@@ -1433,6 +1451,10 @@
if (bandwidthIndex >= 0) {
mOrigBandwidthIndex = mCurBandwidthIndex;
mCurBandwidthIndex = bandwidthIndex;
+ if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+ ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
+ mOrigBandwidthIndex, mCurBandwidthIndex);
+ }
}
CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size());
const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex);
@@ -1478,21 +1500,23 @@
}
if (discardFetcher) {
+ ALOGV("discarding fetcher-%d", fetcher->getFetcherID());
fetcher->stopAsync();
} else {
- float threshold = -1.0f; // always finish fetching by default
+ float threshold = 0.0f; // default to pause after current block (47Kbytes)
+ bool disconnect = false;
if (timeUs >= 0ll) {
// seeking, no need to finish fetching
- threshold = 0.0f;
+ disconnect = true;
} else if (delayRemoval) {
// adapting, abort if remaining of current segment is over threshold
threshold = getAbortThreshold(
mOrigBandwidthIndex, mCurBandwidthIndex);
}
- ALOGV("Pausing with threshold %.3f", threshold);
-
- fetcher->pauseAsync(threshold);
+ ALOGV("pausing fetcher-%d, threshold=%.2f",
+ fetcher->getFetcherID(), threshold);
+ fetcher->pauseAsync(threshold, disconnect);
}
}
@@ -1526,6 +1550,8 @@
}
void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) {
+ ALOGV("onChangeConfiguration");
+
if (!mReconfigurationInProgress) {
int32_t pickTrack = 0;
msg->findInt32("pickTrack", &pickTrack);
@@ -1536,6 +1562,8 @@
}
void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
+ ALOGV("onChangeConfiguration2");
+
mContinuation.clear();
// All fetchers are either suspended or have been removed now.
@@ -1547,13 +1575,14 @@
if (timeUs >= 0) {
mLastSeekTimeUs = timeUs;
+ mLastDequeuedTimeUs = timeUs;
for (size_t i = 0; i < mPacketSources.size(); i++) {
mPacketSources.editValueAt(i)->clear();
}
for (size_t i = 0; i < kMaxStreams; ++i) {
- mStreams[i].mCurDiscontinuitySeq = 0;
+ mStreams[i].reset();
}
mDiscontinuityOffsetTimesUs.clear();
@@ -1599,8 +1628,10 @@
ALOGV("stream %zu changed: oldURI %s, newURI %s", i,
mStreams[i].mUri.c_str(), URIs[i].c_str());
sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i));
- source->queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ if (source->getLatestDequeuedMeta() != NULL) {
+ source->queueDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ }
}
// Determine which decoders to shutdown on the player side,
// a decoder has to be shutdown if its streamtype was active
@@ -1660,16 +1691,17 @@
// and resume audio.
mSwapMask = mNewStreamMask & mStreamMask & ~resumeMask;
switching = (mSwapMask != 0);
- if (!switching) {
- ALOGV("#### Finishing Bandwidth Switch Early: %zd => %zd",
- mOrigBandwidthIndex, mCurBandwidthIndex);
- }
}
mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs;
} else {
mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
}
+ ALOGV("onChangeConfiguration3: timeUs=%lld, switching=%d, pickTrack=%d, "
+ "mStreamMask=0x%x, mNewStreamMask=0x%x, mSwapMask=0x%x",
+ (long long)timeUs, switching, pickTrack,
+ mStreamMask, mNewStreamMask, mSwapMask);
+
for (size_t i = 0; i < kMaxStreams; ++i) {
if (streamMask & indexToType(i)) {
if (switching) {
@@ -1687,6 +1719,9 @@
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
const AString &uri = mFetcherInfos.keyAt(i);
if (!resumeFetcher(uri, resumeMask, timeUs)) {
+ ALOGV("marking fetcher-%d to be removed",
+ mFetcherInfos[i].mFetcher->getFetcherID());
+
mFetcherInfos.editValueAt(i).mToBeRemoved = true;
}
}
@@ -1711,7 +1746,7 @@
HLSTime startTime;
SeekMode seekMode = kSeekModeExactPosition;
- sp<AnotherPacketSource> sources[kMaxStreams];
+ sp<AnotherPacketSource> sources[kNumSources];
if (i == kSubtitleIndex || (!pickTrack && !switching)) {
startTime = latestMediaSegmentStartTime();
@@ -1740,8 +1775,8 @@
}
}
- if (j != kSubtitleIndex && meta != NULL
- && !meta->findInt32("discontinuity", &type)) {
+ if ((j == kAudioIndex || j == kVideoIndex)
+ && meta != NULL && !meta->findInt32("discontinuity", &type)) {
HLSTime tmpTime(meta);
if (startTime < tmpTime) {
startTime = tmpTime;
@@ -1776,6 +1811,14 @@
}
}
+ ALOGV("[fetcher-%d] startAsync: startTimeUs %lld mLastSeekTimeUs %lld "
+ "segmentStartTimeUs %lld seekMode %d",
+ fetcher->getFetcherID(),
+ (long long)startTime.mTimeUs,
+ (long long)mLastSeekTimeUs,
+ (long long)startTime.getSegmentTimeUs(),
+ seekMode);
+
// Set the target segment start time to the middle point of the
// segment where the last sample was.
// This gives a better guess if segments of the two variants are not
@@ -1786,8 +1829,9 @@
sources[kAudioIndex],
sources[kVideoIndex],
sources[kSubtitleIndex],
+ getMetadataSource(sources, mNewStreamMask, switching),
startTime.mTimeUs < 0 ? mLastSeekTimeUs : startTime.mTimeUs,
- startTime.getSegmentTimeUs(true /* midpoint */),
+ startTime.getSegmentTimeUs(),
startTime.mSeq,
seekMode);
}
@@ -1795,22 +1839,28 @@
// All fetchers have now been started, the configuration change
// has completed.
- ALOGV("XXX configuration change completed.");
mReconfigurationInProgress = false;
if (switching) {
mSwitchInProgress = true;
} else {
mStreamMask = mNewStreamMask;
- mOrigBandwidthIndex = mCurBandwidthIndex;
+ if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+ ALOGV("#### Finished Bandwidth Switch Early: %zd => %zd",
+ mOrigBandwidthIndex, mCurBandwidthIndex);
+ mOrigBandwidthIndex = mCurBandwidthIndex;
+ }
}
+ ALOGV("onChangeConfiguration3: mSwitchInProgress %d, mStreamMask 0x%x",
+ mSwitchInProgress, mStreamMask);
+
if (mDisconnectReplyID != NULL) {
finishDisconnect();
}
}
void LiveSession::swapPacketSource(StreamType stream) {
- ALOGV("swapPacketSource: stream = %d", stream);
+ ALOGV("[%s] swapPacketSource", getNameForStream(stream));
// transfer packets from source2 to source
sp<AnotherPacketSource> &aps = mPacketSources.editValueFor(stream);
@@ -1858,7 +1908,7 @@
mFetcherInfos.editValueAt(index).mFetcher->stopAsync(false /* clear */);
- ALOGV("tryToFinishBandwidthSwitch: mSwapMask=%x", mSwapMask);
+ ALOGV("tryToFinishBandwidthSwitch: mSwapMask=0x%x", mSwapMask);
if (mSwapMask != 0) {
return;
}
@@ -1925,11 +1975,19 @@
bool underflow, ready, down, up;
if (checkBuffering(underflow, ready, down, up)) {
- if (mInPreparationPhase && ready) {
- postPrepared(OK);
+ if (mInPreparationPhase) {
+ // Allow down switch even if we're still preparing.
+ //
+ // Some streams have a high bandwidth index as default,
+ // when bandwidth is low, it takes a long time to buffer
+ // to ready mark, then it immediately pauses after start
+ // as we have to do a down switch. It's better experience
+ // to restart from a lower index, if we detect low bw.
+ if (!switchBandwidthIfNeeded(false /* up */, down) && ready) {
+ postPrepared(OK);
+ }
}
- // don't switch before we report prepared
if (!mInPreparationPhase) {
if (ready) {
stopBufferingIfNecessary();
@@ -1937,8 +1995,7 @@
startBufferingIfNecessary();
}
switchBandwidthIfNeeded(up, down);
- }
-
+ }
}
schedulePollBuffering();
@@ -1983,7 +2040,7 @@
}
ALOGI("#### Canceled Bandwidth Switch: %zd => %zd",
- mCurBandwidthIndex, mOrigBandwidthIndex);
+ mOrigBandwidthIndex, mCurBandwidthIndex);
mSwitchGeneration++;
mSwitchInProgress = false;
@@ -2020,15 +2077,19 @@
continue;
}
+ status_t finalResult;
int64_t bufferedDurationUs =
- mPacketSources[i]->getEstimatedDurationUs();
- ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs);
+ mPacketSources[i]->getBufferedDurationUs(&finalResult);
+ ALOGV("[%s] buffered %lld us",
+ getNameForStream(mPacketSources.keyAt(i)),
+ (long long)bufferedDurationUs);
if (durationUs >= 0) {
int32_t percent;
if (mPacketSources[i]->isFinished(0 /* duration */)) {
percent = 100;
} else {
- percent = (int32_t)(100.0 * (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
+ percent = (int32_t)(100.0 *
+ (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
}
if (minBufferPercent < 0 || percent < minBufferPercent) {
minBufferPercent = percent;
@@ -2111,47 +2172,58 @@
notify->post();
}
-void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
+/*
+ * returns true if a bandwidth switch is actually needed (and started),
+ * returns false otherwise
+ */
+bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
// no need to check bandwidth if we only have 1 bandwidth settings
if (mSwitchInProgress || mBandwidthItems.size() < 2) {
- return;
+ return false;
}
int32_t bandwidthBps;
- if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps)) {
+ bool isStable;
+ if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps, &isStable)) {
ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
mLastBandwidthBps = bandwidthBps;
} else {
ALOGV("no bandwidth estimate.");
- return;
+ return false;
}
int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth;
// canSwithDown and canSwitchUp can't both be true.
// we only want to switch up when measured bw is 120% higher than current variant,
// and we only want to switch down when measured bw is below current variant.
- bool canSwithDown = bufferLow
+ bool canSwitchDown = bufferLow
&& (bandwidthBps < (int32_t)curBandwidth);
bool canSwitchUp = bufferHigh
&& (bandwidthBps > (int32_t)curBandwidth * 12 / 10);
- if (canSwithDown || canSwitchUp) {
+ if (canSwitchDown || canSwitchUp) {
+ // bandwidth estimating has some delay, if we have to downswitch when
+ // it hasn't stabilized, be very conservative on bandwidth.
+ if (!isStable && canSwitchDown) {
+ bandwidthBps /= 2;
+ }
+
ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps);
// it's possible that we're checking for canSwitchUp case, but the returned
// bandwidthIndex is < mCurBandwidthIndex, as getBandwidthIndex() only uses 70%
// of measured bw. In that case we don't want to do anything, since we have
// both enough buffer and enough bw.
- if (bandwidthIndex == mCurBandwidthIndex
- || (canSwitchUp && bandwidthIndex < mCurBandwidthIndex)
- || (canSwithDown && bandwidthIndex > mCurBandwidthIndex)) {
- return;
+ if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
+ || (canSwitchDown && bandwidthIndex < mCurBandwidthIndex)) {
+ // if not yet prepared, just restart again with new bw index.
+ // this is faster and playback experience is cleaner.
+ changeConfiguration(
+ mInPreparationPhase ? 0 : -1ll, bandwidthIndex);
+ return true;
}
-
- ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
- mCurBandwidthIndex, bandwidthIndex);
- changeConfiguration(-1, bandwidthIndex, false);
}
+ return false;
}
void LiveSession::postError(status_t err) {
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index b5e31c9..21be413 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -23,18 +23,21 @@
#include <utils/String8.h>
+#include "mpeg2ts/ATSParser.h"
+
namespace android {
struct ABuffer;
struct AReplyToken;
struct AnotherPacketSource;
-struct DataSource;
+class DataSource;
struct HTTPBase;
struct IMediaHTTPService;
struct LiveDataSource;
struct M3UParser;
struct PlaylistFetcher;
struct HLSTime;
+struct HTTPDownloader;
struct LiveSession : public AHandler {
enum Flags {
@@ -47,12 +50,15 @@
kVideoIndex = 1,
kSubtitleIndex = 2,
kMaxStreams = 3,
+ kMetaDataIndex = 3,
+ kNumSources = 4,
};
enum StreamType {
STREAMTYPE_AUDIO = 1 << kAudioIndex,
STREAMTYPE_VIDEO = 1 << kVideoIndex,
STREAMTYPE_SUBTITLES = 1 << kSubtitleIndex,
+ STREAMTYPE_METADATA = 1 << kMetaDataIndex,
};
enum SeekMode {
@@ -66,11 +72,12 @@
uint32_t flags,
const sp<IMediaHTTPService> &httpService);
+ int64_t calculateMediaTimeUs(int64_t firstTimeUs, int64_t timeUs, int32_t discontinuitySeq);
status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit);
status_t getStreamFormat(StreamType stream, sp<AMessage> *format);
- sp<HTTPBase> getHTTPDataSource();
+ sp<HTTPDownloader> getHTTPDownloader();
void connectAsync(
const char *url,
@@ -91,6 +98,8 @@
bool hasDynamicDuration() const;
static const char *getKeyForStream(StreamType type);
+ static const char *getNameForStream(StreamType type);
+ static ATSParser::SourceType getSourceTypeForStream(StreamType type);
enum {
kWhatStreamsChanged,
@@ -100,6 +109,7 @@
kWhatBufferingStart,
kWhatBufferingEnd,
kWhatBufferingUpdate,
+ kWhatMetadataDetected,
};
protected:
@@ -118,7 +128,6 @@
kWhatChangeConfiguration = 'chC0',
kWhatChangeConfiguration2 = 'chC2',
kWhatChangeConfiguration3 = 'chC3',
- kWhatFinishDisconnect2 = 'fin2',
kWhatPollBuffering = 'poll',
};
@@ -157,10 +166,14 @@
: StreamItem("") {}
StreamItem(const char *type)
: mType(type),
- mSeekMode(kSeekModeExactPosition),
- mCurDiscontinuitySeq(0),
- mLastDequeuedTimeUs(0),
- mLastSampleDurationUs(0) {}
+ mSeekMode(kSeekModeExactPosition) {
+ reset();
+ }
+ void reset() {
+ mCurDiscontinuitySeq = 0;
+ mLastDequeuedTimeUs = -1ll;
+ mLastSampleDurationUs = 0ll;
+ }
AString uriKey() {
AString key(mType);
key.append("URI");
@@ -178,7 +191,6 @@
int32_t mPollBufferingGeneration;
int32_t mPrevBufferPercentage;
- sp<HTTPBase> mHTTPDataSource;
KeyedVector<String8, String8> mExtraHeaders;
AString mMasterURL;
@@ -190,6 +202,8 @@
sp<BandwidthEstimator> mBandwidthEstimator;
sp<M3UParser> mPlaylist;
+ int32_t mMaxWidth;
+ int32_t mMaxHeight;
sp<ALooper> mFetcherLooper;
KeyedVector<AString, FetcherInfo> mFetcherInfos;
@@ -230,40 +244,21 @@
bool mFirstTimeUsValid;
int64_t mFirstTimeUs;
int64_t mLastSeekTimeUs;
+ bool mHasMetadata;
+
KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs;
KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs;
sp<PlaylistFetcher> addFetcher(const char *uri);
void onConnect(const sp<AMessage> &msg);
- status_t onSeek(const sp<AMessage> &msg);
- void onFinishDisconnect2();
+ void onMasterPlaylistFetched(const sp<AMessage> &msg);
+ void onSeek(const sp<AMessage> &msg);
- // If given a non-zero block_size (default 0), it is used to cap the number of
- // bytes read in from the DataSource. If given a non-NULL buffer, new content
- // is read into the end.
- //
- // The DataSource we read from is responsible for signaling error or EOF to help us
- // break out of the read loop. The DataSource can be returned to the caller, so
- // that the caller can reuse it for subsequent fetches (within the initially
- // requested range).
- //
- // For reused HTTP sources, the caller must download a file sequentially without
- // any overlaps or gaps to prevent reconnection.
- ssize_t fetchFile(
- const char *url, sp<ABuffer> *out,
- /* request/open a file starting at range_offset for range_length bytes */
- int64_t range_offset = 0, int64_t range_length = -1,
- /* download block size */
- uint32_t block_size = 0,
- /* reuse DataSource if doing partial fetch */
- sp<DataSource> *source = NULL,
- String8 *actualUrl = NULL,
- /* force connect http even when resuing DataSource */
- bool forceConnectHTTP = false);
-
- sp<M3UParser> fetchPlaylist(
- const char *url, uint8_t *curPlaylistHash, bool *unchanged);
+ bool UriIsSameAsIndex( const AString &uri, int32_t index, bool newUri);
+ sp<AnotherPacketSource> getPacketSourceForStreamIndex(size_t trackIndex, bool newUri);
+ sp<AnotherPacketSource> getMetadataSource(
+ sp<AnotherPacketSource> sources[kNumSources], uint32_t streamMask, bool newUri);
bool resumeFetcher(
const AString &uri, uint32_t streamMask,
@@ -291,7 +286,7 @@
bool checkSwitchProgress(
sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil);
- void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
+ bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
void schedulePollBuffering();
void cancelPollBuffering();
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 7bb7f2c..ff2bb27 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -250,6 +250,9 @@
mIsVariantPlaylist(false),
mIsComplete(false),
mIsEvent(false),
+ mFirstSeqNumber(-1),
+ mLastSeqNumber(-1),
+ mTargetDurationUs(-1ll),
mDiscontinuitySeq(0),
mDiscontinuityCount(0),
mSelectedIndex(-1) {
@@ -283,6 +286,19 @@
return mDiscontinuitySeq;
}
+int64_t M3UParser::getTargetDuration() const {
+ return mTargetDurationUs;
+}
+
+int32_t M3UParser::getFirstSeqNumber() const {
+ return mFirstSeqNumber;
+}
+
+void M3UParser::getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const {
+ *firstSeq = mFirstSeqNumber;
+ *lastSeq = mLastSeqNumber;
+}
+
sp<AMessage> M3UParser::meta() {
return mMeta;
}
@@ -664,11 +680,22 @@
}
// error checking of all fields that's required to appear once
- // (currently only checking "target-duration")
- int32_t targetDurationSecs;
- if (!mIsVariantPlaylist && (mMeta == NULL || !mMeta->findInt32(
- "target-duration", &targetDurationSecs))) {
- return ERROR_MALFORMED;
+ // (currently only checking "target-duration"), and
+ // initialization of playlist properties (eg. mTargetDurationUs)
+ if (!mIsVariantPlaylist) {
+ int32_t targetDurationSecs;
+ if (mMeta == NULL || !mMeta->findInt32(
+ "target-duration", &targetDurationSecs)) {
+ ALOGE("Media playlist missing #EXT-X-TARGETDURATION");
+ return ERROR_MALFORMED;
+ }
+ mTargetDurationUs = targetDurationSecs * 1000000ll;
+
+ mFirstSeqNumber = 0;
+ if (mMeta != NULL) {
+ mMeta->findInt32("media-sequence", &mFirstSeqNumber);
+ }
+ mLastSeqNumber = mFirstSeqNumber + mItems.size() - 1;
}
return OK;
@@ -808,6 +835,29 @@
*meta = new AMessage;
}
(*meta)->setString(key.c_str(), codecs.c_str());
+ } else if (!strcasecmp("resolution", key.c_str())) {
+ const char *s = val.c_str();
+ char *end;
+ unsigned long width = strtoul(s, &end, 10);
+
+ if (end == s || *end != 'x') {
+ // malformed
+ continue;
+ }
+
+ s = end + 1;
+ unsigned long height = strtoul(s, &end, 10);
+
+ if (end == s || *end != '\0') {
+ // malformed
+ continue;
+ }
+
+ if (meta->get() == NULL) {
+ *meta = new AMessage;
+ }
+ (*meta)->setInt32("width", width);
+ (*meta)->setInt32("height", height);
} else if (!strcasecmp("audio", key.c_str())
|| !strcasecmp("video", key.c_str())
|| !strcasecmp("subtitles", key.c_str())) {
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index fef361f..fa648ed 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -36,6 +36,9 @@
bool isComplete() const;
bool isEvent() const;
size_t getDiscontinuitySeq() const;
+ int64_t getTargetDuration() const;
+ int32_t getFirstSeqNumber() const;
+ void getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const;
sp<AMessage> meta();
@@ -70,6 +73,9 @@
bool mIsVariantPlaylist;
bool mIsComplete;
bool mIsEvent;
+ int32_t mFirstSeqNumber;
+ int32_t mLastSeqNumber;
+ int64_t mTargetDurationUs;
size_t mDiscontinuitySeq;
int32_t mDiscontinuityCount;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 368612d..4851528 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -17,25 +17,19 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "PlaylistFetcher"
#include <utils/Log.h>
+#include <utils/misc.h>
#include "PlaylistFetcher.h"
-
-#include "LiveDataSource.h"
+#include "HTTPDownloader.h"
#include "LiveSession.h"
#include "M3UParser.h"
-
#include "include/avc_utils.h"
-#include "include/HTTPBase.h"
#include "include/ID3.h"
#include "mpeg2ts/AnotherPacketSource.h"
-#include <media/IStreamSource.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AUtils.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
@@ -43,7 +37,10 @@
#include <ctype.h>
#include <inttypes.h>
#include <openssl/aes.h>
-#include <openssl/md5.h>
+
+#define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__)
+#define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \
+ LiveSession::getNameForStream(stream), ##__VA_ARGS__)
namespace android {
@@ -143,16 +140,19 @@
const sp<AMessage> ¬ify,
const sp<LiveSession> &session,
const char *uri,
+ int32_t id,
int32_t subtitleGeneration)
: mNotify(notify),
mSession(session),
mURI(uri),
+ mFetcherID(id),
mStreamTypeMask(0),
mStartTimeUs(-1ll),
mSegmentStartTimeUs(-1ll),
mDiscontinuitySeq(-1ll),
mStartTimeUsRelative(false),
mLastPlaylistFetchTimeUs(-1ll),
+ mPlaylistTimeUs(-1ll),
mSeqNumber(-1),
mNumRetries(0),
mStartup(true),
@@ -168,25 +168,25 @@
mFirstTimeUs(-1ll),
mVideoBuffer(new AnotherPacketSource(NULL)),
mThresholdRatio(-1.0f),
- mDownloadState(new DownloadState()) {
+ mDownloadState(new DownloadState()),
+ mHasMetadata(false) {
memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
- mHTTPDataSource = mSession->getHTTPDataSource();
+ mHTTPDownloader = mSession->getHTTPDownloader();
}
PlaylistFetcher::~PlaylistFetcher() {
}
+int32_t PlaylistFetcher::getFetcherID() const {
+ return mFetcherID;
+}
+
int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const {
CHECK(mPlaylist != NULL);
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
- int32_t lastSeqNumberInPlaylist =
- firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+ int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
+ mPlaylist->getSeqNumberRange(
+ &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
CHECK_GE(seqNumber, firstSeqNumberInPlaylist);
CHECK_LE(seqNumber, lastSeqNumberInPlaylist);
@@ -210,14 +210,9 @@
int64_t PlaylistFetcher::getSegmentDurationUs(int32_t seqNumber) const {
CHECK(mPlaylist != NULL);
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
- int32_t lastSeqNumberInPlaylist =
- firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+ int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
+ mPlaylist->getSeqNumberRange(
+ &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
CHECK_GE(seqNumber, firstSeqNumberInPlaylist);
CHECK_LE(seqNumber, lastSeqNumberInPlaylist);
@@ -245,10 +240,7 @@
return (~0llu >> 1);
}
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
+ int64_t targetDurationUs = mPlaylist->getTargetDuration();
int64_t minPlaylistAgeUs;
@@ -338,9 +330,11 @@
if (index >= 0) {
key = mAESKeyForURI.valueAt(index);
} else {
- ssize_t err = mSession->fetchFile(keyURI.c_str(), &key);
+ ssize_t err = mHTTPDownloader->fetchFile(keyURI.c_str(), &key);
- if (err < 0) {
+ if (err == ERROR_NOT_CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ } else if (err < 0) {
ALOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
return ERROR_IO;
} else if (key->size() != 16) {
@@ -436,7 +430,7 @@
maxDelayUs = minDelayUs;
}
if (delayUs > maxDelayUs) {
- ALOGV("Need to refresh playlist in %" PRId64 , maxDelayUs);
+ FLOGV("Need to refresh playlist in %lld", (long long)maxDelayUs);
delayUs = maxDelayUs;
}
sp<AMessage> msg = new AMessage(kWhatMonitorQueue, this);
@@ -448,18 +442,39 @@
++mMonitorQueueGeneration;
}
-void PlaylistFetcher::setStoppingThreshold(float thresholdRatio) {
- AutoMutex _l(mThresholdLock);
- if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
- return;
+void PlaylistFetcher::setStoppingThreshold(float thresholdRatio, bool disconnect) {
+ {
+ AutoMutex _l(mThresholdLock);
+ mThresholdRatio = thresholdRatio;
}
- mThresholdRatio = thresholdRatio;
+ if (disconnect) {
+ mHTTPDownloader->disconnect();
+ }
+}
+
+void PlaylistFetcher::resetStoppingThreshold(bool disconnect) {
+ {
+ AutoMutex _l(mThresholdLock);
+ mThresholdRatio = -1.0f;
+ }
+ if (disconnect) {
+ mHTTPDownloader->disconnect();
+ } else {
+ // allow reconnect
+ mHTTPDownloader->reconnect();
+ }
+}
+
+float PlaylistFetcher::getStoppingThreshold() {
+ AutoMutex _l(mThresholdLock);
+ return mThresholdRatio;
}
void PlaylistFetcher::startAsync(
const sp<AnotherPacketSource> &audioSource,
const sp<AnotherPacketSource> &videoSource,
const sp<AnotherPacketSource> &subtitleSource,
+ const sp<AnotherPacketSource> &metadataSource,
int64_t startTimeUs,
int64_t segmentStartTimeUs,
int32_t startDiscontinuitySeq,
@@ -483,6 +498,11 @@
streamTypeMask |= LiveSession::STREAMTYPE_SUBTITLES;
}
+ if (metadataSource != NULL) {
+ msg->setPointer("metadataSource", metadataSource.get());
+ // metadataSource does not affect streamTypeMask.
+ }
+
msg->setInt32("streamTypeMask", streamTypeMask);
msg->setInt64("startTimeUs", startTimeUs);
msg->setInt64("segmentStartTimeUs", segmentStartTimeUs);
@@ -491,15 +511,15 @@
msg->post();
}
-void PlaylistFetcher::pauseAsync(float thresholdRatio) {
- if (thresholdRatio >= 0.0f) {
- setStoppingThreshold(thresholdRatio);
- }
+void PlaylistFetcher::pauseAsync(
+ float thresholdRatio, bool disconnect) {
+ setStoppingThreshold(thresholdRatio, disconnect);
+
(new AMessage(kWhatPause, this))->post();
}
void PlaylistFetcher::stopAsync(bool clear) {
- setStoppingThreshold(0.0f);
+ setStoppingThreshold(0.0f, true /* disconncect */);
sp<AMessage> msg = new AMessage(kWhatStop, this);
msg->setInt32("clear", clear);
@@ -507,11 +527,17 @@
}
void PlaylistFetcher::resumeUntilAsync(const sp<AMessage> ¶ms) {
+ FLOGV("resumeUntilAsync: params=%s", params->debugString().c_str());
+
AMessage* msg = new AMessage(kWhatResumeUntil, this);
msg->setMessage("params", params);
msg->post();
}
+void PlaylistFetcher::fetchPlaylistAsync() {
+ (new AMessage(kWhatFetchPlaylist, this))->post();
+}
+
void PlaylistFetcher::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatStart:
@@ -549,6 +575,19 @@
break;
}
+ case kWhatFetchPlaylist:
+ {
+ bool unchanged;
+ sp<M3UParser> playlist = mHTTPDownloader->fetchPlaylist(
+ mURI.c_str(), NULL /* curPlaylistHash */, &unchanged);
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatPlaylistFetched);
+ notify->setObject("playlist", playlist);
+ notify->post();
+ break;
+ }
+
case kWhatMonitorQueue:
case kWhatDownloadNext:
{
@@ -625,6 +664,15 @@
static_cast<AnotherPacketSource *>(ptr));
}
+ void *ptr;
+ // metadataSource is not part of streamTypeMask
+ if ((streamTypeMask & (LiveSession::STREAMTYPE_AUDIO | LiveSession::STREAMTYPE_VIDEO))
+ && msg->findPointer("metadataSource", &ptr)) {
+ mPacketSources.add(
+ LiveSession::STREAMTYPE_METADATA,
+ static_cast<AnotherPacketSource *>(ptr));
+ }
+
mStreamTypeMask = streamTypeMask;
mSegmentStartTimeUs = segmentStartTimeUs;
@@ -659,7 +707,7 @@
cancelMonitorQueue();
mLastDiscontinuitySeq = mDiscontinuitySeq;
- setStoppingThreshold(-1.0f);
+ resetStoppingThreshold(false /* disconnect */);
}
void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
@@ -674,14 +722,11 @@
}
}
- // close off the connection after use
- mHTTPDataSource->disconnect();
-
mDownloadState->resetState();
mPacketSources.clear();
mStreamTypeMask = 0;
- setStoppingThreshold(-1.0f);
+ resetStoppingThreshold(true /* disconnect */);
}
// Resume until we have reached the boundary timestamps listed in `msg`; when
@@ -721,24 +766,15 @@
}
void PlaylistFetcher::onMonitorQueue() {
- bool downloadMore = false;
-
// in the middle of an unfinished download, delay
// playlist refresh as it'll change seq numbers
if (!mDownloadState->hasSavedState()) {
refreshPlaylist();
}
- int32_t targetDurationSecs;
int64_t targetDurationUs = kMinBufferedDurationUs;
if (mPlaylist != NULL) {
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "target-duration", &targetDurationSecs)) {
- ALOGE("Playlist is missing required EXT-X-TARGETDURATION tag");
- notifyError(ERROR_MALFORMED);
- return;
- }
- targetDurationUs = targetDurationSecs * 1000000ll;
+ targetDurationUs = mPlaylist->getTargetDuration();
}
int64_t bufferedDurationUs = 0ll;
@@ -763,8 +799,9 @@
int64_t bufferedStreamDurationUs =
mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult);
- ALOGV("buffered %" PRId64 " for stream %d",
- bufferedStreamDurationUs, mPacketSources.keyAt(i));
+
+ FSLOGV(mPacketSources.keyAt(i), "buffered %lld", (long long)bufferedStreamDurationUs);
+
if (bufferedDurationUs == -1ll
|| bufferedStreamDurationUs < bufferedDurationUs) {
bufferedDurationUs = bufferedStreamDurationUs;
@@ -776,8 +813,9 @@
}
if (finalResult == OK && bufferedDurationUs < kMinBufferedDurationUs) {
- ALOGV("monitoring, buffered=%" PRId64 " < %" PRId64 "",
- bufferedDurationUs, kMinBufferedDurationUs);
+ FLOGV("monitoring, buffered=%lld < %lld",
+ (long long)bufferedDurationUs, (long long)kMinBufferedDurationUs);
+
// delay the next download slightly; hopefully this gives other concurrent fetchers
// a better chance to run.
// onDownloadNext();
@@ -792,8 +830,12 @@
if (delayUs > targetDurationUs / 2) {
delayUs = targetDurationUs / 2;
}
- ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "",
- delayUs, bufferedDurationUs, kMinBufferedDurationUs);
+
+ FLOGV("pausing for %lld, buffered=%lld > %lld",
+ (long long)delayUs,
+ (long long)bufferedDurationUs,
+ (long long)kMinBufferedDurationUs);
+
postMonitorQueue(delayUs);
}
}
@@ -801,7 +843,7 @@
status_t PlaylistFetcher::refreshPlaylist() {
if (delayUsToRefreshPlaylist() <= 0) {
bool unchanged;
- sp<M3UParser> playlist = mSession->fetchPlaylist(
+ sp<M3UParser> playlist = mHTTPDownloader->fetchPlaylist(
mURI.c_str(), mPlaylistHash, &unchanged);
if (playlist == NULL) {
@@ -830,6 +872,7 @@
if (!mPlaylist->isComplete()) {
updateTargetDuration();
}
+ mPlaylistTimeUs = ALooper::GetNowUs();
}
mLastPlaylistFetchTimeUs = ALooper::GetNowUs();
@@ -849,20 +892,12 @@
}
// Calculate threshold to abort current download
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
- int64_t thresholdUs = -1;
- {
- AutoMutex _l(mThresholdLock);
- thresholdUs = (mThresholdRatio < 0.0f) ?
- -1ll : mThresholdRatio * targetDurationUs;
- }
+ float thresholdRatio = getStoppingThreshold();
- if (thresholdUs < 0) {
+ if (thresholdRatio < 0.0f) {
// never abort
return false;
- } else if (thresholdUs == 0) {
+ } else if (thresholdRatio == 0.0f) {
// immediately abort
return true;
}
@@ -891,6 +926,15 @@
}
}
lastEnqueueUs -= mSegmentFirstPTS;
+
+ int64_t targetDurationUs = mPlaylist->getTargetDuration();
+ int64_t thresholdUs = thresholdRatio * targetDurationUs;
+
+ FLOGV("%spausing now, thresholdUs %lld, remaining %lld",
+ targetDurationUs - lastEnqueueUs > thresholdUs ? "" : "not ",
+ (long long)thresholdUs,
+ (long long)(targetDurationUs - lastEnqueueUs));
+
if (targetDurationUs - lastEnqueueUs > thresholdUs) {
return true;
}
@@ -908,12 +952,8 @@
bool discontinuity = false;
if (mPlaylist != NULL) {
- if (mPlaylist->meta() != NULL) {
- mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist);
- }
-
- lastSeqNumberInPlaylist =
- firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+ mPlaylist->getSeqNumberRange(
+ &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
if (mDiscontinuitySeq < 0) {
mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq();
@@ -940,19 +980,26 @@
mStartTimeUs -= getSegmentStartTimeUs(mSeqNumber);
}
mStartTimeUsRelative = true;
- ALOGV("Initial sequence number for time %" PRId64 " is %d from (%d .. %d)",
- mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
+ FLOGV("Initial sequence number for time %lld is %d from (%d .. %d)",
+ (long long)mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
lastSeqNumberInPlaylist);
} else {
// When adapting or track switching, mSegmentStartTimeUs (relative
// to media time 0) is used to determine the start segment; mStartTimeUs (absolute
// timestamps coming from the media container) is used to determine the position
// inside a segments.
- mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES
&& mSeekMode != LiveSession::kSeekModeNextSample) {
// avoid double fetch/decode
- mSeqNumber += 1;
+ // Use (mSegmentStartTimeUs + 1/2 * targetDurationUs) to search
+ // for the starting segment in new variant.
+ // If the two variants' segments are aligned, this gives the
+ // next segment. If they're not aligned, this gives the segment
+ // that overlaps no more than 1/2 * targetDurationUs.
+ mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs
+ + mPlaylist->getTargetDuration() / 2);
+ } else {
+ mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
}
ssize_t minSeq = getSeqNumberForDiscontinuity(mDiscontinuitySeq);
if (mSeqNumber < minSeq) {
@@ -966,7 +1013,7 @@
if (mSeqNumber > lastSeqNumberInPlaylist) {
mSeqNumber = lastSeqNumberInPlaylist;
}
- ALOGV("Initial sequence number for live event %d from (%d .. %d)",
+ FLOGV("Initial sequence number is %d from (%d .. %d)",
mSeqNumber, firstSeqNumberInPlaylist,
lastSeqNumberInPlaylist);
}
@@ -986,19 +1033,17 @@
// refresh in increasing fraction (1/2, 1/3, ...) of the
// playlist's target duration or 3 seconds, whichever is less
int64_t delayUs = kMaxMonitorDelayUs;
- if (mPlaylist != NULL && mPlaylist->meta() != NULL) {
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- delayUs = mPlaylist->size() * targetDurationSecs *
- 1000000ll / (1 + mNumRetries);
+ if (mPlaylist != NULL) {
+ delayUs = mPlaylist->size() * mPlaylist->getTargetDuration()
+ / (1 + mNumRetries);
}
if (delayUs > kMaxMonitorDelayUs) {
delayUs = kMaxMonitorDelayUs;
}
- ALOGV("sequence number high: %d from (%d .. %d), "
- "monitor in %" PRId64 " (retry=%d)",
+ FLOGV("sequence number high: %d from (%d .. %d), "
+ "monitor in %lld (retry=%d)",
mSeqNumber, firstSeqNumberInPlaylist,
- lastSeqNumberInPlaylist, delayUs, mNumRetries);
+ lastSeqNumberInPlaylist, (long long)delayUs, mNumRetries);
postMonitorQueue(delayUs);
return false;
}
@@ -1037,6 +1082,16 @@
mSeqNumber, firstSeqNumberInPlaylist,
firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1);
+ if (mTSParser != NULL) {
+ mTSParser->signalEOS(ERROR_END_OF_STREAM);
+ // Use an empty buffer; we don't have any new data, just want to extract
+ // potential new access units after flush. Reset mSeqNumber to
+ // lastSeqNumberInPlaylist such that we set the correct access unit
+ // properties in extractAndQueueAccessUnitsFromTs.
+ sp<ABuffer> buffer = new ABuffer(0);
+ mSeqNumber = lastSeqNumberInPlaylist;
+ extractAndQueueAccessUnitsFromTs(buffer);
+ }
notifyError(ERROR_END_OF_STREAM);
} else {
// It's possible that we were never able to download the playlist.
@@ -1067,9 +1122,9 @@
// Seek jumped to a new discontinuity sequence. We need to signal
// a format change to decoder. Decoder needs to shutdown and be
// created again if seamless format change is unsupported.
- ALOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
+ FLOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
"mDiscontinuitySeq %d, mStartTimeUs %lld",
- mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
+ mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
discontinuity = true;
}
mLastDiscontinuitySeq = -1;
@@ -1081,7 +1136,9 @@
junk->setRange(0, 16);
status_t err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, junk,
true /* first */);
- if (err != OK) {
+ if (err == ERROR_NOT_CONNECTED) {
+ return false;
+ } else if (err != OK) {
notifyError(err);
return false;
}
@@ -1117,7 +1174,7 @@
}
queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE,
+ ATSParser::DISCONTINUITY_FORMAT_ONLY,
NULL /* extra */);
if (mStartup && mStartTimeUsRelative && mFirstPTSValid) {
@@ -1131,10 +1188,12 @@
// set mStartTimeUs=0, and take all samples from now on.
mStartTimeUs = 0;
mFirstPTSValid = false;
+ mIDRFound = false;
+ mVideoBuffer->clear();
}
}
- ALOGV("fetching segment %d from (%d .. %d)",
+ FLOGV("fetching segment %d from (%d .. %d)",
mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist);
return true;
}
@@ -1157,7 +1216,7 @@
firstSeqNumberInPlaylist,
lastSeqNumberInPlaylist);
connectHTTP = false;
- ALOGV("resuming: '%s'", uri.c_str());
+ FLOGV("resuming: '%s'", uri.c_str());
} else {
if (!initDownloadState(
uri,
@@ -1166,7 +1225,7 @@
lastSeqNumberInPlaylist)) {
return;
}
- ALOGV("fetching: '%s'", uri.c_str());
+ FLOGV("fetching: '%s'", uri.c_str());
}
int64_t range_offset, range_length;
@@ -1180,12 +1239,21 @@
bool shouldPause = false;
ssize_t bytesRead;
do {
- sp<DataSource> source = mHTTPDataSource;
-
int64_t startUs = ALooper::GetNowUs();
- bytesRead = mSession->fetchFile(
+ bytesRead = mHTTPDownloader->fetchBlock(
uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize,
- &source, NULL, connectHTTP);
+ NULL /* actualURL */, connectHTTP);
+ int64_t delayUs = ALooper::GetNowUs() - startUs;
+
+ if (bytesRead == ERROR_NOT_CONNECTED) {
+ return;
+ }
+ if (bytesRead < 0) {
+ status_t err = bytesRead;
+ ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
+ notifyError(err);
+ return;
+ }
// add sample for bandwidth estimation, excluding samples from subtitles (as
// its too small), or during startup/resumeUntil (when we could have more than
@@ -1194,19 +1262,15 @@
&& (mStreamTypeMask
& (LiveSession::STREAMTYPE_AUDIO
| LiveSession::STREAMTYPE_VIDEO))) {
- int64_t delayUs = ALooper::GetNowUs() - startUs;
mSession->addBandwidthMeasurement(bytesRead, delayUs);
+ if (delayUs > 2000000ll) {
+ FLOGV("bytesRead %zd took %.2f seconds - abnormal bandwidth dip",
+ bytesRead, (double)delayUs / 1.0e6);
+ }
}
connectHTTP = false;
- if (bytesRead < 0) {
- status_t err = bytesRead;
- ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
- notifyError(err);
- return;
- }
-
CHECK(buffer != NULL);
size_t size = buffer->size();
@@ -1286,11 +1350,11 @@
if (bufferStartsWithTsSyncByte(buffer)) {
// If we don't see a stream in the program table after fetching a full ts segment
// mark it as nonexistent.
- const size_t kNumTypes = ATSParser::NUM_SOURCE_TYPES;
- ATSParser::SourceType srcTypes[kNumTypes] =
+ ATSParser::SourceType srcTypes[] =
{ ATSParser::VIDEO, ATSParser::AUDIO };
- LiveSession::StreamType streamTypes[kNumTypes] =
+ LiveSession::StreamType streamTypes[] =
{ LiveSession::STREAMTYPE_VIDEO, LiveSession::STREAMTYPE_AUDIO };
+ const size_t kNumTypes = NELEM(srcTypes);
for (size_t i = 0; i < kNumTypes; i++) {
ATSParser::SourceType srcType = srcTypes[i];
@@ -1362,42 +1426,73 @@
}
}
-int32_t PlaylistFetcher::getSeqNumberWithAnchorTime(
- int64_t anchorTimeUs, int64_t targetDiffUs) const {
- int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL
- || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
- lastSeqNumberInPlaylist = firstSeqNumberInPlaylist + mPlaylist->size() - 1;
+/*
+ * returns true if we need to adjust mSeqNumber
+ */
+bool PlaylistFetcher::adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs) {
+ int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber();
- int32_t index = mSeqNumber - firstSeqNumberInPlaylist - 1;
- // adjust anchorTimeUs to within targetDiffUs from mStartTimeUs
- while (index >= 0 && anchorTimeUs - mStartTimeUs > targetDiffUs) {
- sp<AMessage> itemMeta;
- CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
-
- int64_t itemDurationUs;
- CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
-
- anchorTimeUs -= itemDurationUs;
- --index;
- }
-
- int32_t newSeqNumber = firstSeqNumberInPlaylist + index + 1;
- if (newSeqNumber <= lastSeqNumberInPlaylist) {
- return newSeqNumber;
+ int64_t minDiffUs, maxDiffUs;
+ if (mSeekMode == LiveSession::kSeekModeNextSample) {
+ // if the previous fetcher paused in the middle of a segment, we
+ // want to start at a segment that overlaps the last sample
+ minDiffUs = -mPlaylist->getTargetDuration();
+ maxDiffUs = 0ll;
} else {
- return lastSeqNumberInPlaylist;
+ // if the previous fetcher paused at the end of a segment, ideally
+ // we want to start at the segment that's roughly aligned with its
+ // next segment, but if the two variants are not well aligned we
+ // adjust the diff to within (-T/2, T/2)
+ minDiffUs = -mPlaylist->getTargetDuration() / 2;
+ maxDiffUs = mPlaylist->getTargetDuration() / 2;
}
+
+ int32_t oldSeqNumber = mSeqNumber;
+ ssize_t index = mSeqNumber - firstSeqNumberInPlaylist;
+
+ // adjust anchorTimeUs to within (minDiffUs, maxDiffUs) from mStartTimeUs
+ int64_t diffUs = anchorTimeUs - mStartTimeUs;
+ if (diffUs > maxDiffUs) {
+ while (index > 0 && diffUs > maxDiffUs) {
+ --index;
+
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+ diffUs -= itemDurationUs;
+ }
+ } else if (diffUs < minDiffUs) {
+ while (index + 1 < (ssize_t) mPlaylist->size()
+ && diffUs < minDiffUs) {
+ ++index;
+
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+ diffUs += itemDurationUs;
+ }
+ }
+
+ mSeqNumber = firstSeqNumberInPlaylist + index;
+
+ if (mSeqNumber != oldSeqNumber) {
+ FLOGV("guessed wrong seg number: diff %lld out of [%lld, %lld]",
+ (long long) anchorTimeUs - mStartTimeUs,
+ (long long) minDiffUs,
+ (long long) maxDiffUs);
+ return true;
+ }
+ return false;
}
int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const {
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
+ int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber();
size_t index = 0;
while (index < mPlaylist->size()) {
@@ -1419,12 +1514,6 @@
}
int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const {
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
size_t index = 0;
int64_t segmentStartUs = 0;
while (index < mPlaylist->size()) {
@@ -1447,7 +1536,7 @@
index = mPlaylist->size() - 1;
}
- return firstSeqNumberInPlaylist + index;
+ return mPlaylist->getFirstSeqNumber() + index;
}
const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties(
@@ -1462,17 +1551,37 @@
accessUnit->meta()->setInt32("discard", discard);
}
- int32_t targetDurationSecs;
- if (mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)) {
- accessUnit->meta()->setInt32("targetDuration", targetDurationSecs);
- }
-
accessUnit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
accessUnit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
+ accessUnit->meta()->setInt64("segmentFirstTimeUs", mSegmentFirstPTS);
accessUnit->meta()->setInt64("segmentDurationUs", getSegmentDurationUs(mSeqNumber));
+ if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) {
+ accessUnit->meta()->setInt64("playlistTimeUs", mPlaylistTimeUs);
+ }
return accessUnit;
}
+bool PlaylistFetcher::isStartTimeReached(int64_t timeUs) {
+ if (!mFirstPTSValid) {
+ mFirstTimeUs = timeUs;
+ mFirstPTSValid = true;
+ }
+ bool startTimeReached = true;
+ if (mStartTimeUsRelative) {
+ FLOGV("startTimeUsRelative, timeUs (%lld) - %lld = %lld",
+ (long long)timeUs,
+ (long long)mFirstTimeUs,
+ (long long)(timeUs - mFirstTimeUs));
+ timeUs -= mFirstTimeUs;
+ if (timeUs < 0) {
+ FLOGV("clamp negative timeUs to 0");
+ timeUs = 0;
+ }
+ startTimeReached = (timeUs >= mStartTimeUs);
+ }
+ return startTimeReached;
+}
+
status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer) {
if (mTSParser == NULL) {
// Use TS_TIMESTAMPS_ARE_ABSOLUTE so pts carry over between fetchers.
@@ -1510,6 +1619,62 @@
// setRange to indicate consumed bytes.
buffer->setRange(buffer->offset() + offset, buffer->size() - offset);
+ if (mSegmentFirstPTS < 0ll) {
+ // get the smallest first PTS from all streams present in this parser
+ for (size_t i = mPacketSources.size(); i-- > 0;) {
+ const LiveSession::StreamType stream = mPacketSources.keyAt(i);
+ if (stream == LiveSession::STREAMTYPE_SUBTITLES) {
+ ALOGE("MPEG2 Transport streams do not contain subtitles.");
+ return ERROR_MALFORMED;
+ }
+ if (stream == LiveSession::STREAMTYPE_METADATA) {
+ continue;
+ }
+ ATSParser::SourceType type =LiveSession::getSourceTypeForStream(stream);
+ sp<AnotherPacketSource> source =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(type).get());
+
+ if (source == NULL) {
+ continue;
+ }
+ sp<AMessage> meta = source->getMetaAfterLastDequeued(0);
+ if (meta != NULL) {
+ int64_t timeUs;
+ CHECK(meta->findInt64("timeUs", &timeUs));
+ if (mSegmentFirstPTS < 0ll || timeUs < mSegmentFirstPTS) {
+ mSegmentFirstPTS = timeUs;
+ }
+ }
+ }
+ if (mSegmentFirstPTS < 0ll) {
+ // didn't find any TS packet, can return early
+ return OK;
+ }
+ if (!mStartTimeUsRelative) {
+ // mStartup
+ // mStartup is true until we have queued a packet for all the streams
+ // we are fetching. We queue packets whose timestamps are greater than
+ // mStartTimeUs.
+ // mSegmentStartTimeUs >= 0
+ // mSegmentStartTimeUs is non-negative when adapting or switching tracks
+ // adjustSeqNumberWithAnchorTime(timeUs) == true
+ // we guessed a seq number that's either too large or too small.
+ // If this happens, we'll adjust mSeqNumber and restart fetching from new
+ // location. Note that we only want to adjust once, so set mSegmentStartTimeUs
+ // to -1 so that we don't enter this chunk next time.
+ if (mStartup && mSegmentStartTimeUs >= 0
+ && adjustSeqNumberWithAnchorTime(mSegmentFirstPTS)) {
+ mStartTimeUsNotify = mNotify->dup();
+ mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+ mStartTimeUsNotify->setString("uri", mURI);
+ mIDRFound = false;
+ mSegmentStartTimeUs = -1;
+ return -EAGAIN;
+ }
+ }
+ }
+
status_t err = OK;
for (size_t i = mPacketSources.size(); i-- > 0;) {
sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
@@ -1519,10 +1684,9 @@
ALOGE("MPEG2 Transport streams do not contain subtitles.");
return ERROR_MALFORMED;
}
+
const char *key = LiveSession::getKeyForStream(stream);
- ATSParser::SourceType type =
- (stream == LiveSession::STREAMTYPE_AUDIO) ?
- ATSParser::AUDIO : ATSParser::VIDEO;
+ ATSParser::SourceType type =LiveSession::getSourceTypeForStream(stream);
sp<AnotherPacketSource> source =
static_cast<AnotherPacketSource *>(
@@ -1545,82 +1709,26 @@
int64_t timeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
- if (mSegmentFirstPTS < 0ll) {
- mSegmentFirstPTS = timeUs;
- if (!mStartTimeUsRelative) {
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
- // mStartup
- // mStartup is true until we have queued a packet for all the streams
- // we are fetching. We queue packets whose timestamps are greater than
- // mStartTimeUs.
- // mSegmentStartTimeUs >= 0
- // mSegmentStartTimeUs is non-negative when adapting or switching tracks
- // mSeqNumber > firstSeqNumberInPlaylist
- // don't decrement mSeqNumber if it already points to the 1st segment
- // timeUs - mStartTimeUs > targetDurationUs:
- // This and the 2 above conditions should only happen when adapting in a live
- // stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher
- // would start fetching after timeUs, which should be greater than mStartTimeUs;
- // the old fetcher would then continue fetching data until timeUs. We don't want
- // timeUs to be too far ahead of mStartTimeUs because we want the old fetcher to
- // stop as early as possible. The definition of being "too far ahead" is
- // arbitrary; here we use targetDurationUs as threshold.
- int64_t targetDiffUs = (mSeekMode == LiveSession::kSeekModeNextSample
- ? 0 : targetDurationUs);
- if (mStartup && mSegmentStartTimeUs >= 0
- && mSeqNumber > firstSeqNumberInPlaylist
- && timeUs - mStartTimeUs > targetDiffUs) {
- // we just guessed a starting timestamp that is too high when adapting in a
- // live stream; re-adjust based on the actual timestamp extracted from the
- // media segment; if we didn't move backward after the re-adjustment
- // (newSeqNumber), start at least 1 segment prior.
- int32_t newSeqNumber = getSeqNumberWithAnchorTime(
- timeUs, targetDiffUs);
- if (newSeqNumber >= mSeqNumber) {
- --mSeqNumber;
- } else {
- mSeqNumber = newSeqNumber;
- }
- mStartTimeUsNotify = mNotify->dup();
- mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
- mStartTimeUsNotify->setString("uri", mURI);
- mIDRFound = false;
- return -EAGAIN;
- }
- }
- }
if (mStartup) {
- if (!mFirstPTSValid) {
- mFirstTimeUs = timeUs;
- mFirstPTSValid = true;
- }
- bool startTimeReached = true;
- if (mStartTimeUsRelative) {
- timeUs -= mFirstTimeUs;
- if (timeUs < 0) {
- timeUs = 0;
- }
- startTimeReached = (timeUs >= mStartTimeUs);
- }
+ bool startTimeReached = isStartTimeReached(timeUs);
if (!startTimeReached || (isAvc && !mIDRFound)) {
// buffer up to the closest preceding IDR frame in the next segement,
// or the closest succeeding IDR frame after the exact position
+ FSLOGV(stream, "timeUs(%lld)-mStartTimeUs(%lld)=%lld, mIDRFound=%d",
+ (long long)timeUs,
+ (long long)mStartTimeUs,
+ (long long)timeUs - mStartTimeUs,
+ mIDRFound);
if (isAvc) {
if (IsIDR(accessUnit)) {
mVideoBuffer->clear();
+ FSLOGV(stream, "found IDR, clear mVideoBuffer");
mIDRFound = true;
}
if (mIDRFound && mStartTimeUsRelative && !startTimeReached) {
mVideoBuffer->queueAccessUnit(accessUnit);
+ FSLOGV(stream, "saving AVC video AccessUnit");
}
}
if (!startTimeReached || (isAvc && !mIDRFound)) {
@@ -1632,18 +1740,21 @@
if (mStartTimeUsNotify != NULL) {
uint32_t streamMask = 0;
mStartTimeUsNotify->findInt32("streamMask", (int32_t *) &streamMask);
- if (!(streamMask & mPacketSources.keyAt(i))) {
+ if ((mStreamTypeMask & mPacketSources.keyAt(i))
+ && !(streamMask & mPacketSources.keyAt(i))) {
streamMask |= mPacketSources.keyAt(i);
mStartTimeUsNotify->setInt32("streamMask", streamMask);
+ FSLOGV(stream, "found start point, timeUs=%lld, streamMask becomes %x",
+ (long long)timeUs, streamMask);
if (streamMask == mStreamTypeMask) {
+ FLOGV("found start point for all streams");
mStartup = false;
}
}
}
if (mStopParams != NULL) {
- // Queue discontinuity in original stream.
int32_t discontinuitySeq;
int64_t stopTimeUs;
if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq)
@@ -1651,13 +1762,13 @@
|| !mStopParams->findInt64(key, &stopTimeUs)
|| (discontinuitySeq == mDiscontinuitySeq
&& timeUs >= stopTimeUs)) {
+ FSLOGV(stream, "reached stop point, timeUs=%lld", (long long)timeUs);
mStreamTypeMask &= ~stream;
mPacketSources.removeItemsAt(i);
break;
}
}
- // Note that we do NOT dequeue any discontinuities except for format change.
if (stream == LiveSession::STREAMTYPE_VIDEO) {
const bool discard = true;
status_t status;
@@ -1666,11 +1777,21 @@
mVideoBuffer->dequeueAccessUnit(&videoBuffer);
setAccessUnitProperties(videoBuffer, source, discard);
packetSource->queueAccessUnit(videoBuffer);
+ int64_t bufferTimeUs;
+ CHECK(videoBuffer->meta()->findInt64("timeUs", &bufferTimeUs));
+ FSLOGV(stream, "queueAccessUnit (saved), timeUs=%lld",
+ (long long)bufferTimeUs);
}
+ } else if (stream == LiveSession::STREAMTYPE_METADATA && !mHasMetadata) {
+ mHasMetadata = true;
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatMetadataDetected);
+ notify->post();
}
setAccessUnitProperties(accessUnit, source);
packetSource->queueAccessUnit(accessUnit);
+ FSLOGV(stream, "queueAccessUnit, timeUs=%lld", (long long)timeUs);
}
if (err != OK) {
@@ -1688,7 +1809,7 @@
if (!mStreamTypeMask) {
// Signal gap is filled between original and new stream.
- ALOGV("ERROR OUT OF RANGE");
+ FLOGV("reached stop point for all streams");
return ERROR_OUT_OF_RANGE;
}
@@ -1739,7 +1860,6 @@
buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
buffer->meta()->setInt32("subtitleGeneration", mSubtitleGeneration);
-
packetSource->queueAccessUnit(buffer);
return OK;
}
@@ -1850,6 +1970,18 @@
mFirstTimeUs = timeUs;
}
+ if (mSegmentFirstPTS < 0ll) {
+ mSegmentFirstPTS = timeUs;
+ if (!mStartTimeUsRelative) {
+ // Duplicated logic from how we handle .ts playlists.
+ if (mStartup && mSegmentStartTimeUs >= 0
+ && adjustSeqNumberWithAnchorTime(timeUs)) {
+ mSegmentStartTimeUs = -1;
+ return -EAGAIN;
+ }
+ }
+ }
+
size_t offset = 0;
while (offset < buffer->size()) {
const uint8_t *adtsHeader = buffer->data() + offset;
@@ -1893,32 +2025,12 @@
}
if (mStartTimeUsNotify != NULL) {
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-
- int64_t targetDiffUs =(mSeekMode == LiveSession::kSeekModeNextSample
- ? 0 : targetDurationUs);
- // Duplicated logic from how we handle .ts playlists.
- if (mStartup && mSegmentStartTimeUs >= 0
- && timeUs - mStartTimeUs > targetDiffUs) {
- int32_t newSeqNumber = getSeqNumberWithAnchorTime(
- timeUs, targetDiffUs);
- if (newSeqNumber >= mSeqNumber) {
- --mSeqNumber;
- } else {
- mSeqNumber = newSeqNumber;
- }
- return -EAGAIN;
- }
-
mStartTimeUsNotify->setInt32("streamMask", LiveSession::STREAMTYPE_AUDIO);
mStartup = false;
}
}
if (mStopParams != NULL) {
- // Queue discontinuity in original stream.
int32_t discontinuitySeq;
int64_t stopTimeUs;
if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq)
@@ -1962,13 +2074,9 @@
}
void PlaylistFetcher::updateTargetDuration() {
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-
sp<AMessage> msg = mNotify->dup();
msg->setInt32("what", kWhatTargetDurationUpdate);
- msg->setInt64("targetDurationUs", targetDurationUs);
+ msg->setInt64("targetDurationUs", mPlaylist->getTargetDuration());
msg->post();
}
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index dab56df..c8ca457 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -27,7 +27,7 @@
struct ABuffer;
struct AnotherPacketSource;
-struct DataSource;
+class DataSource;
struct HTTPBase;
struct LiveDataSource;
struct M3UParser;
@@ -49,32 +49,38 @@
kWhatPreparationFailed,
kWhatStartedAt,
kWhatStopReached,
+ kWhatPlaylistFetched,
+ kWhatMetadataDetected,
};
PlaylistFetcher(
const sp<AMessage> ¬ify,
const sp<LiveSession> &session,
const char *uri,
+ int32_t id,
int32_t subtitleGeneration);
- sp<DataSource> getDataSource();
+ int32_t getFetcherID() const;
void startAsync(
const sp<AnotherPacketSource> &audioSource,
const sp<AnotherPacketSource> &videoSource,
const sp<AnotherPacketSource> &subtitleSource,
+ const sp<AnotherPacketSource> &metadataSource,
int64_t startTimeUs = -1ll, // starting timestamps
int64_t segmentStartTimeUs = -1ll, // starting position within playlist
// startTimeUs!=segmentStartTimeUs only when playlist is live
int32_t startDiscontinuitySeq = -1,
LiveSession::SeekMode seekMode = LiveSession::kSeekModeExactPosition);
- void pauseAsync(float thresholdRatio);
+ void pauseAsync(float thresholdRatio, bool disconnect);
void stopAsync(bool clear = true);
void resumeUntilAsync(const sp<AMessage> ¶ms);
+ void fetchPlaylistAsync();
+
uint32_t getStreamTypeMask() const {
return mStreamTypeMask;
}
@@ -95,6 +101,7 @@
kWhatMonitorQueue = 'moni',
kWhatResumeUntil = 'rsme',
kWhatDownloadNext = 'dlnx',
+ kWhatFetchPlaylist = 'flst'
};
struct DownloadState;
@@ -109,10 +116,12 @@
sp<AMessage> mNotify;
sp<AMessage> mStartTimeUsNotify;
- sp<HTTPBase> mHTTPDataSource;
+ sp<HTTPDownloader> mHTTPDownloader;
sp<LiveSession> mSession;
AString mURI;
+ int32_t mFetcherID;
+
uint32_t mStreamTypeMask;
int64_t mStartTimeUs;
@@ -131,6 +140,7 @@
KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
int64_t mLastPlaylistFetchTimeUs;
+ int64_t mPlaylistTimeUs;
sp<M3UParser> mPlaylist;
int32_t mSeqNumber;
int32_t mNumRetries;
@@ -172,6 +182,8 @@
sp<DownloadState> mDownloadState;
+ bool mHasMetadata;
+
// Set first to true if decrypting the first segment of a playlist segment. When
// first is true, reset the initialization vector based on the available
// information in the manifest; otherwise, use the initialization vector as
@@ -187,7 +199,9 @@
void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0);
void cancelMonitorQueue();
- void setStoppingThreshold(float thresholdRatio);
+ void setStoppingThreshold(float thresholdRatio, bool disconnect);
+ void resetStoppingThreshold(bool disconnect);
+ float getStoppingThreshold();
bool shouldPauseDownload();
int64_t delayUsToRefreshPlaylist() const;
@@ -217,6 +231,7 @@
const sp<ABuffer> &accessUnit,
const sp<AnotherPacketSource> &source,
bool discard = false);
+ bool isStartTimeReached(int64_t timeUs);
status_t extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer);
status_t extractAndQueueAccessUnits(
@@ -228,8 +243,7 @@
void queueDiscontinuity(
ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
- int32_t getSeqNumberWithAnchorTime(
- int64_t anchorTimeUs, int64_t targetDurationUs) const;
+ bool adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs);
int32_t getSeqNumberForDiscontinuity(size_t discontinuitySeq) const;
int32_t getSeqNumberForTime(int64_t timeUs) const;
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 2194c38..68bd017 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -4,7 +4,8 @@
LOCAL_SRC_FILES := \
ID3.cpp
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE := libstagefright_id3
@@ -17,7 +18,8 @@
LOCAL_SRC_FILES := \
testid3.cpp
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
LOCAL_SHARED_LIBRARIES := \
libstagefright libutils liblog libbinder libstagefright_foundation
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 77d65e0..758b2c9 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -21,6 +21,7 @@
#include "HTTPBase.h"
#include "TimedEventQueue.h"
+#include <media/AudioResamplerPublic.h>
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/OMXClient.h>
@@ -31,20 +32,20 @@
namespace android {
-struct AudioPlayer;
+class AudioPlayer;
struct ClockEstimator;
-struct DataSource;
-struct MediaBuffer;
+class IDataSource;
+class MediaBuffer;
struct MediaExtractor;
struct MediaSource;
struct NuCachedSource2;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
class DrmManagerClinet;
class DecryptHandle;
class TimedTextDriver;
-struct WVMExtractor;
+class WVMExtractor;
struct AwesomeRenderer : public RefBase {
AwesomeRenderer() {}
@@ -93,6 +94,8 @@
status_t setParameter(int key, const Parcel &request);
status_t getParameter(int key, Parcel *reply);
+ status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+ status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
status_t invoke(const Parcel &request, Parcel *reply);
status_t setCacheStatCollectFreq(const Parcel &request);
@@ -180,6 +183,7 @@
sp<MediaSource> mOmxSource;
sp<MediaSource> mAudioSource;
AudioPlayer *mAudioPlayer;
+ AudioPlaybackRate mPlaybackSettings;
int64_t mDurationUs;
int32_t mDisplayWidth;
diff --git a/media/libstagefright/include/CallbackDataSource.h b/media/libstagefright/include/CallbackDataSource.h
new file mode 100644
index 0000000..1a21dd3
--- /dev/null
+++ b/media/libstagefright/include/CallbackDataSource.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CALLBACKDATASOURCE_H
+#define ANDROID_CALLBACKDATASOURCE_H
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+class IDataSource;
+class IMemory;
+
+// A stagefright DataSource that wraps a binder IDataSource. It's a "Callback"
+// DataSource because it calls back to the IDataSource for data.
+class CallbackDataSource : public DataSource {
+public:
+ CallbackDataSource(const sp<IDataSource>& iDataSource);
+ virtual ~CallbackDataSource();
+
+ // DataSource implementation.
+ virtual status_t initCheck() const;
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+ virtual status_t getSize(off64_t *size);
+
+private:
+ sp<IDataSource> mIDataSource;
+ sp<IMemory> mMemory;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CallbackDataSource);
+};
+
+
+// A caching DataSource that wraps a CallbackDataSource. For reads smaller
+// than kCacheSize it will read up to kCacheSize ahead and cache it.
+// This reduces the number of binder round trips to the IDataSource and has a significant
+// impact on time taken for filetype sniffing and metadata extraction.
+class TinyCacheSource : public DataSource {
+public:
+ TinyCacheSource(const sp<DataSource>& source);
+
+ virtual status_t initCheck() const;
+ virtual ssize_t readAt(off64_t offset, void* data, size_t size);
+ virtual status_t getSize(off64_t* size);
+ virtual uint32_t flags();
+
+private:
+ // 2kb comes from experimenting with the time-to-first-frame from a MediaPlayer
+ // with an in-memory MediaDataSource source on a Nexus 5. Beyond 2kb there was
+ // no improvement.
+ enum {
+ kCacheSize = 2048,
+ };
+
+ sp<DataSource> mSource;
+ uint8_t mCache[kCacheSize];
+ off64_t mCachedOffset;
+ size_t mCachedSize;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TinyCacheSource);
+};
+
+}; // namespace android
+
+#endif // ANDROID_CALLBACKDATASOURCE_H
diff --git a/media/libstagefright/include/MPEG2PSExtractor.h b/media/libstagefright/include/MPEG2PSExtractor.h
index fb76564..22cb02d 100644
--- a/media/libstagefright/include/MPEG2PSExtractor.h
+++ b/media/libstagefright/include/MPEG2PSExtractor.h
@@ -28,7 +28,7 @@
struct ABuffer;
struct AMessage;
struct Track;
-struct String8;
+class String8;
struct MPEG2PSExtractor : public MediaExtractor {
MPEG2PSExtractor(const sp<DataSource> &source);
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index db1187d..4dd340c 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -30,7 +30,7 @@
struct ATSParser;
class DataSource;
struct MPEG2TSSource;
-struct String8;
+class String8;
struct MPEG2TSExtractor : public MediaExtractor {
MPEG2TSExtractor(const sp<DataSource> &source);
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 8c16251..3067c3d 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -104,11 +104,15 @@
String8 mLastCommentName;
String8 mLastCommentData;
+ KeyedVector<uint32_t, AString> mMetaKeyMap;
+
status_t readMetaData();
status_t parseChunk(off64_t *offset, int depth);
status_t parseITunesMetaData(off64_t offset, size_t size);
status_t parse3GPPMetaData(off64_t offset, size_t size, int depth);
void parseID3v2MetaData(off64_t offset);
+ status_t parseQTMetaKey(off64_t data_offset, size_t data_size);
+ status_t parseQTMetaVal(int32_t keyId, off64_t data_offset, size_t data_size);
status_t updateAudioTrackInfoFromESDS_MPEG4Audio(
const void *esds_data, size_t esds_size);
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index e8c4970..c183208 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -24,7 +24,7 @@
namespace android {
struct OMXMaster;
-class OMXNodeInstance;
+struct OMXNodeInstance;
class OMX : public BnOMX,
public IBinder::DeathRecipient {
@@ -95,6 +95,14 @@
node_id node, OMX_U32 port_index,
sp<IGraphicBufferProducer> *bufferProducer);
+ virtual status_t createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer);
+
+ virtual status_t setInputSurface(
+ node_id node, OMX_U32 port_index,
+ const sp<IGraphicBufferConsumer> &bufferConsumer);
+
virtual status_t signalEndOfInputStream(node_id node);
virtual status_t allocateBuffer(
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 104dcfc..ad1e181 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -27,7 +27,9 @@
class IOMXObserver;
struct OMXMaster;
-struct GraphicBufferSource;
+class GraphicBufferSource;
+
+status_t StatusFromOMXError(OMX_ERRORTYPE err);
struct OMXNodeInstance {
OMXNodeInstance(
@@ -81,6 +83,13 @@
status_t createInputSurface(
OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer);
+ static status_t createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer);
+
+ status_t setInputSurface(
+ OMX_U32 portIndex, const sp<IGraphicBufferConsumer> &bufferConsumer);
+
status_t signalEndOfInputStream();
status_t allocateBuffer(
@@ -202,6 +211,8 @@
OMX_BUFFERHEADERTYPE *header,
OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr);
+ status_t createGraphicBufferSource(
+ OMX_U32 portIndex, sp<IGraphicBufferConsumer> consumer = NULL);
sp<GraphicBufferSource> getGraphicBufferSource();
void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource);
diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h
index 60c9e7e..7053247 100644
--- a/media/libstagefright/include/SampleIterator.h
+++ b/media/libstagefright/include/SampleIterator.h
@@ -18,7 +18,7 @@
namespace android {
-struct SampleTable;
+class SampleTable;
struct SampleIterator {
SampleIterator(SampleTable *table);
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index 6632c27..fd739d0 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -25,7 +25,7 @@
namespace android {
-struct DataSource;
+class DataSource;
class MediaExtractor;
struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
@@ -38,6 +38,7 @@
const KeyedVector<String8, String8> *headers);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual status_t setDataSource(const sp<DataSource>& source);
virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option);
virtual MediaAlbumArt *extractAlbumArt();
@@ -53,6 +54,8 @@
MediaAlbumArt *mAlbumArt;
void parseMetaData();
+ // Delete album art and clear metadata.
+ void clearMetadata();
StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
index 2963150..890f7e8 100644
--- a/media/libstagefright/include/TimedEventQueue.h
+++ b/media/libstagefright/include/TimedEventQueue.h
@@ -46,7 +46,7 @@
virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
private:
- friend class TimedEventQueue;
+ friend struct TimedEventQueue;
event_id mEventID;
diff --git a/media/libstagefright/include/VBRISeeker.h b/media/libstagefright/include/VBRISeeker.h
index 1a2bf9f..c57d571 100644
--- a/media/libstagefright/include/VBRISeeker.h
+++ b/media/libstagefright/include/VBRISeeker.h
@@ -24,7 +24,7 @@
namespace android {
-struct DataSource;
+class DataSource;
struct VBRISeeker : public MP3Seeker {
static sp<VBRISeeker> CreateFromSource(
diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h
index c408576..cce04f0 100644
--- a/media/libstagefright/include/XINGSeeker.h
+++ b/media/libstagefright/include/XINGSeeker.h
@@ -22,7 +22,7 @@
namespace android {
-struct DataSource;
+class DataSource;
struct XINGSeeker : public MP3Seeker {
static sp<XINGSeeker> CreateFromSource(
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
index 446ff8c..1e8c2b2 100644
--- a/media/libstagefright/matroska/Android.mk
+++ b/media/libstagefright/matroska/Android.mk
@@ -8,7 +8,8 @@
$(TOP)/external/libvpx/libwebm \
$(TOP)/frameworks/native/include/media/openmax \
-LOCAL_CFLAGS += -Wno-multichar -Werror
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE:= libstagefright_matroska
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 0712bf0..70d2c69 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -925,6 +925,11 @@
ALOGV("codec id = %s", codecID);
ALOGV("codec name = %s", track->GetCodecNameAsUTF8());
+ if (codecID == NULL) {
+ ALOGW("unknown codecID is not supported.");
+ continue;
+ }
+
size_t codecPrivateSize;
const unsigned char *codecPrivate =
track->GetCodecPrivate(codecPrivateSize);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 0a868bc..e8b2219 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -132,6 +132,7 @@
bool isAudio() const;
bool isVideo() const;
+ bool isMeta() const;
protected:
virtual ~Stream();
@@ -146,6 +147,7 @@
sp<ABuffer> mBuffer;
sp<AnotherPacketSource> mSource;
bool mPayloadStarted;
+ bool mEOSReached;
uint64_t mPrevPTS;
@@ -300,9 +302,13 @@
// The two checks below shouldn't happen,
// we already checked above the stream count matches
ssize_t index = newType2PIDs.indexOfKey(temp[i]->type());
- CHECK(index >= 0);
+ if (index < 0) {
+ return false;
+ }
Vector<int32_t> &newPIDs = newType2PIDs.editValueAt(index);
- CHECK(newPIDs.size() > 0);
+ if (newPIDs.isEmpty()) {
+ return false;
+ }
// get the next PID for temp[i]->type() in the new PID map
Vector<int32_t>::iterator it = newPIDs.begin();
@@ -333,13 +339,11 @@
return ERROR_MALFORMED;
}
- CHECK_EQ(br->getBits(1), 0u);
+ br->skipBits(1); // '0'
MY_LOGV(" reserved = %u", br->getBits(2));
unsigned section_length = br->getBits(12);
ALOGV(" section_length = %u", section_length);
- CHECK_EQ(section_length & 0xc00, 0u);
- CHECK_LE(section_length, 1021u);
MY_LOGV(" program_number = %u", br->getBits(16));
MY_LOGV(" reserved = %u", br->getBits(2));
@@ -356,7 +360,6 @@
unsigned program_info_length = br->getBits(12);
ALOGV(" program_info_length = %u", program_info_length);
- CHECK_EQ(program_info_length & 0xc00, 0u);
br->skipBits(program_info_length * 8); // skip descriptors
@@ -367,8 +370,7 @@
// final CRC.
size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
- while (infoBytesRemaining > 0) {
- CHECK_GE(infoBytesRemaining, 5u);
+ while (infoBytesRemaining >= 5) {
unsigned streamType = br->getBits(8);
ALOGV(" stream_type = 0x%02x", streamType);
@@ -382,9 +384,6 @@
unsigned ES_info_length = br->getBits(12);
ALOGV(" ES_info_length = %u", ES_info_length);
- CHECK_EQ(ES_info_length & 0xc00, 0u);
-
- CHECK_GE(infoBytesRemaining - 5, ES_info_length);
#if 0
br->skipBits(ES_info_length * 8); // skip descriptors
@@ -396,13 +395,13 @@
unsigned descLength = br->getBits(8);
ALOGV(" len = %u", descLength);
- CHECK_GE(info_bytes_remaining, 2 + descLength);
-
+ if (info_bytes_remaining < descLength) {
+ return ERROR_MALFORMED;
+ }
br->skipBits(descLength * 8);
info_bytes_remaining -= descLength + 2;
}
- CHECK_EQ(info_bytes_remaining, 0u);
#endif
StreamInfo info;
@@ -413,7 +412,9 @@
infoBytesRemaining -= 5 + ES_info_length;
}
- CHECK_EQ(infoBytesRemaining, 0u);
+ if (infoBytesRemaining != 0) {
+ ALOGW("Section data remains unconsumed");
+ }
MY_LOGV(" CRC = 0x%08x", br->getBits(32));
bool PIDsChanged = false;
@@ -567,6 +568,7 @@
mPCR_PID(PCR_PID),
mExpectedContinuityCounter(-1),
mPayloadStarted(false),
+ mEOSReached(false),
mPrevPTS(0),
mQueue(NULL) {
switch (mStreamType) {
@@ -602,6 +604,11 @@
ElementaryStreamQueue::AC3);
break;
+ case STREAMTYPE_METADATA:
+ mQueue = new ElementaryStreamQueue(
+ ElementaryStreamQueue::METADATA);
+ break;
+
default:
break;
}
@@ -672,7 +679,10 @@
}
size_t payloadSizeBits = br->numBitsLeft();
- CHECK_EQ(payloadSizeBits % 8, 0u);
+ if (payloadSizeBits % 8 != 0u) {
+ ALOGE("Wrong value");
+ return BAD_VALUE;
+ }
size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
if (mBuffer->capacity() < neededSize) {
@@ -720,6 +730,13 @@
}
}
+bool ATSParser::Stream::isMeta() const {
+ if (mStreamType == STREAMTYPE_METADATA) {
+ return true;
+ }
+ return false;
+}
+
void ATSParser::Stream::signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra) {
mExpectedContinuityCounter = -1;
@@ -729,6 +746,7 @@
}
mPayloadStarted = false;
+ mEOSReached = false;
mBuffer->setRange(0, 0);
bool clearFormat = false;
@@ -766,6 +784,8 @@
if (mSource != NULL) {
mSource->signalEOS(finalResult);
}
+ mEOSReached = true;
+ flush();
}
status_t ATSParser::Stream::parsePES(ABitReader *br) {
@@ -780,8 +800,6 @@
return ERROR_MALFORMED;
}
- CHECK_EQ(packet_startcode_prefix, 0x000001u);
-
unsigned stream_id = br->getBits(8);
ALOGV("stream_id = 0x%02x", stream_id);
@@ -796,7 +814,9 @@
&& stream_id != 0xff // program_stream_directory
&& stream_id != 0xf2 // DSMCC
&& stream_id != 0xf8) { // H.222.1 type E
- CHECK_EQ(br->getBits(2), 2u);
+ if (br->getBits(2) != 2u) {
+ return ERROR_MALFORMED;
+ }
MY_LOGV("PES_scrambling_control = %u", br->getBits(2));
MY_LOGV("PES_priority = %u", br->getBits(1));
@@ -830,34 +850,51 @@
uint64_t PTS = 0, DTS = 0;
if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
- CHECK_GE(optional_bytes_remaining, 5u);
+ if (optional_bytes_remaining < 5u) {
+ return ERROR_MALFORMED;
+ }
if (br->getBits(4) != PTS_DTS_flags) {
- ALOGE("PES data Error!");
return ERROR_MALFORMED;
}
PTS = ((uint64_t)br->getBits(3)) << 30;
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
PTS |= ((uint64_t)br->getBits(15)) << 15;
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
PTS |= br->getBits(15);
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ALOGV("PTS = 0x%016" PRIx64 " (%.2f)", PTS, PTS / 90000.0);
optional_bytes_remaining -= 5;
if (PTS_DTS_flags == 3) {
- CHECK_GE(optional_bytes_remaining, 5u);
+ if (optional_bytes_remaining < 5u) {
+ return ERROR_MALFORMED;
+ }
- CHECK_EQ(br->getBits(4), 1u);
+ if (br->getBits(4) != 1u) {
+ return ERROR_MALFORMED;
+ }
DTS = ((uint64_t)br->getBits(3)) << 30;
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
DTS |= ((uint64_t)br->getBits(15)) << 15;
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
DTS |= br->getBits(15);
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ALOGV("DTS = %" PRIu64, DTS);
@@ -866,31 +903,47 @@
}
if (ESCR_flag) {
- CHECK_GE(optional_bytes_remaining, 6u);
+ if (optional_bytes_remaining < 6u) {
+ return ERROR_MALFORMED;
+ }
br->getBits(2);
uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30;
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ESCR |= ((uint64_t)br->getBits(15)) << 15;
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ESCR |= br->getBits(15);
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ALOGV("ESCR = %" PRIu64, ESCR);
MY_LOGV("ESCR_extension = %u", br->getBits(9));
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
optional_bytes_remaining -= 6;
}
if (ES_rate_flag) {
- CHECK_GE(optional_bytes_remaining, 3u);
+ if (optional_bytes_remaining < 3u) {
+ return ERROR_MALFORMED;
+ }
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
MY_LOGV("ES_rate = %u", br->getBits(22));
- CHECK_EQ(br->getBits(1), 1u);
+ if (br->getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
optional_bytes_remaining -= 3;
}
@@ -900,7 +953,9 @@
// ES data follows.
if (PES_packet_length != 0) {
- CHECK_GE(PES_packet_length, PES_header_data_length + 3);
+ if (PES_packet_length < PES_header_data_length + 3) {
+ return ERROR_MALFORMED;
+ }
unsigned dataLength =
PES_packet_length - 3 - PES_header_data_length;
@@ -913,7 +968,9 @@
return ERROR_MALFORMED;
}
- CHECK_GE(br->numBitsLeft(), dataLength * 8);
+ if (br->numBitsLeft() < dataLength * 8) {
+ return ERROR_MALFORMED;
+ }
onPayloadData(
PTS_DTS_flags, PTS, DTS, br->data(), dataLength);
@@ -925,15 +982,21 @@
br->data(), br->numBitsLeft() / 8);
size_t payloadSizeBits = br->numBitsLeft();
- CHECK_EQ(payloadSizeBits % 8, 0u);
+ if (payloadSizeBits % 8 != 0u) {
+ return ERROR_MALFORMED;
+ }
ALOGV("There's %zu bytes of payload.", payloadSizeBits / 8);
}
} else if (stream_id == 0xbe) { // padding_stream
- CHECK_NE(PES_packet_length, 0u);
+ if (PES_packet_length == 0u) {
+ return ERROR_MALFORMED;
+ }
br->skipBits(PES_packet_length * 8);
} else {
- CHECK_NE(PES_packet_length, 0u);
+ if (PES_packet_length == 0u) {
+ return ERROR_MALFORMED;
+ }
br->skipBits(PES_packet_length * 8);
}
@@ -976,6 +1039,10 @@
status_t err = mQueue->appendData(data, size, timeUs);
+ if (mEOSReached) {
+ mQueue->signalEOS();
+ }
+
if (err != OK) {
return;
}
@@ -1029,6 +1096,14 @@
break;
}
+ case META:
+ {
+ if (isMeta()) {
+ return mSource;
+ }
+ break;
+ }
+
default:
break;
}
@@ -1053,7 +1128,10 @@
}
status_t ATSParser::feedTSPacket(const void *data, size_t size) {
- CHECK_EQ(size, kTSPacketSize);
+ if (size != kTSPacketSize) {
+ ALOGE("Wrong TS packet size");
+ return BAD_VALUE;
+ }
ABitReader br((const uint8_t *)data, kTSPacketSize);
return parseTS(&br);
@@ -1079,14 +1157,23 @@
}
} else if (type == DISCONTINUITY_ABSOLUTE_TIME) {
int64_t timeUs;
- CHECK(extra->findInt64("timeUs", &timeUs));
+ if (!extra->findInt64("timeUs", &timeUs)) {
+ ALOGE("timeUs not found");
+ return;
+ }
- CHECK(mPrograms.empty());
+ if (!mPrograms.empty()) {
+ ALOGE("mPrograms is not empty");
+ return;
+ }
mAbsoluteTimeAnchorUs = timeUs;
return;
} else if (type == DISCONTINUITY_TIME_OFFSET) {
int64_t offset;
- CHECK(extra->findInt64("offset", &offset));
+ if (!extra->findInt64("offset", &offset)) {
+ ALOGE("offset not found");
+ return;
+ }
mTimeOffsetValid = true;
mTimeOffsetUs = offset;
@@ -1099,7 +1186,10 @@
}
void ATSParser::signalEOS(status_t finalResult) {
- CHECK_NE(finalResult, (status_t)OK);
+ if (finalResult == (status_t) OK) {
+ ALOGE("finalResult not OK");
+ return;
+ }
for (size_t i = 0; i < mPrograms.size(); ++i) {
mPrograms.editItemAt(i)->signalEOS(finalResult);
@@ -1115,14 +1205,12 @@
}
unsigned section_syntax_indictor = br->getBits(1);
ALOGV(" section_syntax_indictor = %u", section_syntax_indictor);
- CHECK_EQ(section_syntax_indictor, 1u);
- CHECK_EQ(br->getBits(1), 0u);
+ br->skipBits(1); // '0'
MY_LOGV(" reserved = %u", br->getBits(2));
unsigned section_length = br->getBits(12);
ALOGV(" section_length = %u", section_length);
- CHECK_EQ(section_length & 0xc00, 0u);
MY_LOGV(" transport_stream_id = %u", br->getBits(16));
MY_LOGV(" reserved = %u", br->getBits(2));
@@ -1132,7 +1220,6 @@
MY_LOGV(" last_section_number = %u", br->getBits(8));
size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);
- CHECK_EQ((numProgramBytes % 4), 0u);
for (size_t i = 0; i < numProgramBytes / 4; ++i) {
unsigned program_number = br->getBits(16);
@@ -1192,7 +1279,9 @@
br->skipBits(skip * 8);
}
- CHECK((br->numBitsLeft() % 8) == 0);
+ if (br->numBitsLeft() % 8 != 0) {
+ return ERROR_MALFORMED;
+ }
status_t err = section->append(br->data(), br->numBitsLeft() / 8);
if (err != OK) {
@@ -1262,7 +1351,7 @@
return OK;
}
-void ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
+status_t ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
unsigned adaptation_field_length = br->getBits(8);
if (adaptation_field_length > 0) {
@@ -1278,6 +1367,9 @@
size_t numBitsRead = 4;
if (PCR_flag) {
+ if (adaptation_field_length * 8 < 52) {
+ return ERROR_MALFORMED;
+ }
br->skipBits(4);
uint64_t PCR_base = br->getBits(32);
PCR_base = (PCR_base << 1) | br->getBits(1);
@@ -1308,10 +1400,9 @@
numBitsRead += 52;
}
- CHECK_GE(adaptation_field_length * 8, numBitsRead);
-
br->skipBits(adaptation_field_length * 8 - numBitsRead);
}
+ return OK;
}
status_t ATSParser::parseTS(ABitReader *br) {
@@ -1346,15 +1437,16 @@
// ALOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter);
- if (adaptation_field_control == 2 || adaptation_field_control == 3) {
- parseAdaptationField(br, PID);
- }
-
status_t err = OK;
- if (adaptation_field_control == 1 || adaptation_field_control == 3) {
- err = parsePID(
- br, PID, continuity_counter, payload_unit_start_indicator);
+ if (adaptation_field_control == 2 || adaptation_field_control == 3) {
+ err = parseAdaptationField(br, PID);
+ }
+ if (err == OK) {
+ if (adaptation_field_control == 1 || adaptation_field_control == 3) {
+ err = parsePID(
+ br, PID, continuity_counter, payload_unit_start_indicator);
+ }
}
++mNumTSPacketsParsed;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index a1405bd..4def333 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -74,7 +74,8 @@
enum SourceType {
VIDEO = 0,
AUDIO = 1,
- NUM_SOURCE_TYPES = 2
+ META = 2,
+ NUM_SOURCE_TYPES = 3
};
sp<MediaSource> getSource(SourceType type);
bool hasSource(SourceType type) const;
@@ -90,6 +91,7 @@
STREAMTYPE_MPEG2_AUDIO = 0x04,
STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f,
STREAMTYPE_MPEG4_VIDEO = 0x10,
+ STREAMTYPE_METADATA = 0x15,
STREAMTYPE_H264 = 0x1b,
// From ATSC A/53 Part 3:2009, 6.7.1
@@ -131,7 +133,7 @@
unsigned continuity_counter,
unsigned payload_unit_start_indicator);
- void parseAdaptationField(ABitReader *br, unsigned PID);
+ status_t parseAdaptationField(ABitReader *br, unsigned PID);
status_t parseTS(ABitReader *br);
void updatePCR(unsigned PID, uint64_t PCR, size_t byteOffsetFromStart);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index c17a0b7..16b0160 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -13,7 +13,8 @@
$(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE:= libstagefright_mpeg2ts
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index c5bb41b..0878a1b 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -46,9 +46,10 @@
mLastQueuedTimeUs(0),
mEOSResult(OK),
mLatestEnqueuedMeta(NULL),
- mLatestDequeuedMeta(NULL),
- mQueuedDiscontinuityCount(0) {
+ mLatestDequeuedMeta(NULL) {
setFormat(meta);
+
+ mDiscontinuitySegments.push_back(DiscontinuitySegment());
}
void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
@@ -73,7 +74,7 @@
} else if (!strncasecmp("video/", mime, 6)) {
mIsVideo = true;
} else {
- CHECK(!strncasecmp("text/", mime, 5));
+ CHECK(!strncasecmp("text/", mime, 5) || !strncasecmp("application/", mime, 12));
}
}
@@ -129,11 +130,20 @@
mFormat.clear();
}
- --mQueuedDiscontinuityCount;
+ mDiscontinuitySegments.erase(mDiscontinuitySegments.begin());
+ // CHECK(!mDiscontinuitySegments.empty());
return INFO_DISCONTINUITY;
}
+ // CHECK(!mDiscontinuitySegments.empty());
+ DiscontinuitySegment &seg = *mDiscontinuitySegments.begin();
+
+ int64_t timeUs;
mLatestDequeuedMeta = (*buffer)->meta()->dup();
+ CHECK(mLatestDequeuedMeta->findInt64("timeUs", &timeUs));
+ if (timeUs > seg.mMaxDequeTimeUs) {
+ seg.mMaxDequeTimeUs = timeUs;
+ }
sp<RefBase> object;
if ((*buffer)->meta()->findObject("format", &object)) {
@@ -146,6 +156,12 @@
return mEOSResult;
}
+void AnotherPacketSource::requeueAccessUnit(const sp<ABuffer> &buffer) {
+ // TODO: update corresponding book keeping info.
+ Mutex::Autolock autoLock(mLock);
+ mBuffers.push_front(buffer);
+}
+
status_t AnotherPacketSource::read(
MediaBuffer **out, const ReadOptions *) {
*out = NULL;
@@ -166,6 +182,8 @@
mFormat.clear();
}
+ mDiscontinuitySegments.erase(mDiscontinuitySegments.begin());
+ // CHECK(!mDiscontinuitySegments.empty());
return INFO_DISCONTINUITY;
}
@@ -178,11 +196,26 @@
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+ // CHECK(!mDiscontinuitySegments.empty());
+ DiscontinuitySegment &seg = *mDiscontinuitySegments.begin();
+ if (timeUs > seg.mMaxDequeTimeUs) {
+ seg.mMaxDequeTimeUs = timeUs;
+ }
MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+ int32_t isSync;
+ if (buffer->meta()->findInt32("isSync", &isSync)) {
+ mediaBuffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
+ }
+
+ sp<ABuffer> sei;
+ if (buffer->meta()->findBuffer("sei", &sei) && sei != NULL) {
+ mediaBuffer->meta_data()->setData(kKeySEI, 0, sei->data(), sei->size());
+ }
+
*out = mediaBuffer;
return OK;
}
@@ -215,12 +248,14 @@
mCondition.signal();
int32_t discontinuity;
- if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
- // discontinuity handling needs to be consistent with queueDiscontinuity()
- ++mQueuedDiscontinuityCount;
+ if (buffer->meta()->findInt32("discontinuity", &discontinuity)){
+ ALOGV("queueing a discontinuity with queueAccessUnit");
+
mLastQueuedTimeUs = 0ll;
mEOSResult = OK;
mLatestEnqueuedMeta = NULL;
+
+ mDiscontinuitySegments.push_back(DiscontinuitySegment());
return;
}
@@ -230,6 +265,15 @@
ALOGV("queueAccessUnit timeUs=%" PRIi64 " us (%.2f secs)",
mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6);
+ // CHECK(!mDiscontinuitySegments.empty());
+ DiscontinuitySegment &tailSeg = *(--mDiscontinuitySegments.end());
+ if (lastQueuedTimeUs > tailSeg.mMaxEnqueTimeUs) {
+ tailSeg.mMaxEnqueTimeUs = lastQueuedTimeUs;
+ }
+ if (tailSeg.mMaxDequeTimeUs == -1) {
+ tailSeg.mMaxDequeTimeUs = lastQueuedTimeUs;
+ }
+
if (mLatestEnqueuedMeta == NULL) {
mLatestEnqueuedMeta = buffer->meta()->dup();
} else {
@@ -253,7 +297,9 @@
mBuffers.clear();
mEOSResult = OK;
- mQueuedDiscontinuityCount = 0;
+
+ mDiscontinuitySegments.clear();
+ mDiscontinuitySegments.push_back(DiscontinuitySegment());
mFormat = NULL;
mLatestEnqueuedMeta = NULL;
@@ -280,6 +326,14 @@
++it;
}
+
+ for (List<DiscontinuitySegment>::iterator it2 = mDiscontinuitySegments.begin();
+ it2 != mDiscontinuitySegments.end();
+ ++it2) {
+ DiscontinuitySegment &seg = *it2;
+ seg.clear();
+ }
+
}
mEOSResult = OK;
@@ -290,7 +344,8 @@
return;
}
- ++mQueuedDiscontinuityCount;
+ mDiscontinuitySegments.push_back(DiscontinuitySegment());
+
sp<ABuffer> buffer = new ABuffer(0);
buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
buffer->meta()->setMessage("extra", extra);
@@ -341,84 +396,19 @@
int64_t AnotherPacketSource::getBufferedDurationUs(status_t *finalResult) {
Mutex::Autolock autoLock(mLock);
- return getBufferedDurationUs_l(finalResult);
-}
-
-int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) {
*finalResult = mEOSResult;
- if (mBuffers.empty()) {
- return 0;
- }
-
- int64_t time1 = -1;
- int64_t time2 = -1;
int64_t durationUs = 0;
-
- List<sp<ABuffer> >::iterator it = mBuffers.begin();
- while (it != mBuffers.end()) {
- const sp<ABuffer> &buffer = *it;
-
- int64_t timeUs;
- if (buffer->meta()->findInt64("timeUs", &timeUs)) {
- if (time1 < 0 || timeUs < time1) {
- time1 = timeUs;
- }
-
- if (time2 < 0 || timeUs > time2) {
- time2 = timeUs;
- }
- } else {
- // This is a discontinuity, reset everything.
- durationUs += time2 - time1;
- time1 = time2 = -1;
- }
-
- ++it;
+ for (List<DiscontinuitySegment>::iterator it = mDiscontinuitySegments.begin();
+ it != mDiscontinuitySegments.end();
+ ++it) {
+ const DiscontinuitySegment &seg = *it;
+ // dequeued access units should be a subset of enqueued access units
+ // CHECK(seg.maxEnqueTimeUs >= seg.mMaxDequeTimeUs);
+ durationUs += (seg.mMaxEnqueTimeUs - seg.mMaxDequeTimeUs);
}
- return durationUs + (time2 - time1);
-}
-
-// A cheaper but less precise version of getBufferedDurationUs that we would like to use in
-// LiveSession::dequeueAccessUnit to trigger downwards adaptation.
-int64_t AnotherPacketSource::getEstimatedDurationUs() {
- Mutex::Autolock autoLock(mLock);
- if (mBuffers.empty()) {
- return 0;
- }
-
- if (mQueuedDiscontinuityCount > 0) {
- status_t finalResult;
- return getBufferedDurationUs_l(&finalResult);
- }
-
- List<sp<ABuffer> >::iterator it = mBuffers.begin();
- sp<ABuffer> buffer = *it;
-
- int64_t startTimeUs;
- buffer->meta()->findInt64("timeUs", &startTimeUs);
- if (startTimeUs < 0) {
- return 0;
- }
-
- it = mBuffers.end();
- --it;
- buffer = *it;
-
- int64_t endTimeUs;
- buffer->meta()->findInt64("timeUs", &endTimeUs);
- if (endTimeUs < 0) {
- return 0;
- }
-
- int64_t diffUs;
- if (endTimeUs > startTimeUs) {
- diffUs = endTimeUs - startTimeUs;
- } else {
- diffUs = startTimeUs - endTimeUs;
- }
- return diffUs;
+ return durationUs;
}
status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
@@ -514,18 +504,19 @@
}
HLSTime stopTime(meta);
- ALOGV("trimBuffersAfterMeta: discontinuitySeq %zu, timeUs %lld",
+ ALOGV("trimBuffersAfterMeta: discontinuitySeq %d, timeUs %lld",
stopTime.mSeq, (long long)stopTime.mTimeUs);
List<sp<ABuffer> >::iterator it;
+ List<DiscontinuitySegment >::iterator it2;
sp<AMessage> newLatestEnqueuedMeta = NULL;
int64_t newLastQueuedTimeUs = 0;
- size_t newDiscontinuityCount = 0;
- for (it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+ for (it = mBuffers.begin(), it2 = mDiscontinuitySegments.begin(); it != mBuffers.end(); ++it) {
const sp<ABuffer> &buffer = *it;
int32_t discontinuity;
if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
- newDiscontinuityCount++;
+ // CHECK(it2 != mDiscontinuitySegments.end());
+ ++it2;
continue;
}
@@ -538,10 +529,18 @@
newLatestEnqueuedMeta = buffer->meta();
newLastQueuedTimeUs = curTime.mTimeUs;
}
+
mBuffers.erase(it, mBuffers.end());
mLatestEnqueuedMeta = newLatestEnqueuedMeta;
mLastQueuedTimeUs = newLastQueuedTimeUs;
- mQueuedDiscontinuityCount = newDiscontinuityCount;
+
+ DiscontinuitySegment &seg = *it2;
+ if (newLatestEnqueuedMeta != NULL) {
+ seg.mMaxEnqueTimeUs = newLastQueuedTimeUs;
+ } else {
+ seg.clear();
+ }
+ mDiscontinuitySegments.erase(++it2, mDiscontinuitySegments.end());
}
/*
@@ -554,10 +553,11 @@
sp<AMessage> AnotherPacketSource::trimBuffersBeforeMeta(
const sp<AMessage> &meta) {
HLSTime startTime(meta);
- ALOGV("trimBuffersBeforeMeta: discontinuitySeq %zu, timeUs %lld",
+ ALOGV("trimBuffersBeforeMeta: discontinuitySeq %d, timeUs %lld",
startTime.mSeq, (long long)startTime.mTimeUs);
sp<AMessage> firstMeta;
+ int64_t firstTimeUs = -1;
Mutex::Autolock autoLock(mLock);
if (mBuffers.empty()) {
return NULL;
@@ -567,14 +567,14 @@
bool isAvc = false;
List<sp<ABuffer> >::iterator it;
- size_t discontinuityCount = 0;
for (it = mBuffers.begin(); it != mBuffers.end(); ++it) {
const sp<ABuffer> &buffer = *it;
int32_t discontinuity;
if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
+ mDiscontinuitySegments.erase(mDiscontinuitySegments.begin());
+ // CHECK(!mDiscontinuitySegments.empty());
format = NULL;
isAvc = false;
- discontinuityCount++;
continue;
}
if (format == NULL) {
@@ -596,12 +596,21 @@
ALOGV("trimming from beginning to %lld (not inclusive)",
(long long)curTime.mTimeUs);
firstMeta = buffer->meta();
+ firstTimeUs = curTime.mTimeUs;
break;
}
}
mBuffers.erase(mBuffers.begin(), it);
- mQueuedDiscontinuityCount -= discontinuityCount;
mLatestDequeuedMeta = NULL;
+
+ // CHECK(!mDiscontinuitySegments.empty());
+ DiscontinuitySegment &seg = *mDiscontinuitySegments.begin();
+ if (firstTimeUs >= 0) {
+ seg.mMaxDequeTimeUs = firstTimeUs;
+ } else {
+ seg.clear();
+ }
+
return firstMeta;
}
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index fa7dd6a..eb9dc9b 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -53,8 +53,6 @@
// presentation timestamps since the last discontinuity (if any).
int64_t getBufferedDurationUs(status_t *finalResult);
- int64_t getEstimatedDurationUs();
-
status_t nextBufferTime(int64_t *timeUs);
void queueAccessUnit(const sp<ABuffer> &buffer);
@@ -67,6 +65,7 @@
void signalEOS(status_t result);
status_t dequeueAccessUnit(sp<ABuffer> *buffer);
+ void requeueAccessUnit(const sp<ABuffer> &buffer);
bool isFinished(int64_t duration) const;
@@ -83,6 +82,25 @@
virtual ~AnotherPacketSource();
private:
+
+ struct DiscontinuitySegment {
+ int64_t mMaxDequeTimeUs, mMaxEnqueTimeUs;
+ DiscontinuitySegment()
+ : mMaxDequeTimeUs(-1),
+ mMaxEnqueTimeUs(-1) {
+ };
+
+ void clear() {
+ mMaxDequeTimeUs = mMaxEnqueTimeUs = -1;
+ }
+ };
+
+ // Discontinuity segments are consecutive access units between
+ // discontinuity markers. There should always be at least _ONE_
+ // discontinuity segment, hence the various CHECKs in
+ // AnotherPacketSource.cpp for non-empty()-ness.
+ List<DiscontinuitySegment> mDiscontinuitySegments;
+
Mutex mLock;
Condition mCondition;
@@ -96,10 +114,7 @@
sp<AMessage> mLatestEnqueuedMeta;
sp<AMessage> mLatestDequeuedMeta;
- size_t mQueuedDiscontinuityCount;
-
bool wasFormatChange(int32_t discontinuityType) const;
- int64_t getBufferedDurationUs_l(status_t *finalResult);
DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource);
};
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index b17985c..baf3b15 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -38,7 +38,8 @@
ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags)
: mMode(mode),
- mFlags(flags) {
+ mFlags(flags),
+ mEOSReached(false) {
}
sp<MetaData> ElementaryStreamQueue::getFormat() {
@@ -55,6 +56,8 @@
if (clearFormat) {
mFormat.clear();
}
+
+ mEOSReached = false;
}
// Parse AC3 header assuming the current ptr is start position of syncframe,
@@ -244,6 +247,11 @@
status_t ElementaryStreamQueue::appendData(
const void *data, size_t size, int64_t timeUs) {
+
+ if (mEOSReached) {
+ ALOGE("appending data after EOS");
+ return ERROR_MALFORMED;
+ }
if (mBuffer == NULL || mBuffer->size() == 0) {
switch (mMode) {
case H264:
@@ -409,13 +417,14 @@
}
case PCM_AUDIO:
+ case METADATA:
{
break;
}
default:
- TRESPASS();
- break;
+ ALOGE("Unknown mode: %d", mMode);
+ return ERROR_MALFORMED;
}
}
@@ -493,8 +502,13 @@
return dequeueAccessUnitMPEG4Video();
case PCM_AUDIO:
return dequeueAccessUnitPCMAudio();
+ case METADATA:
+ return dequeueAccessUnitMetadata();
default:
- CHECK_EQ((unsigned)mMode, (unsigned)MPEG_AUDIO);
+ if (mMode != MPEG_AUDIO) {
+ ALOGE("Unknown mode");
+ return NULL;
+ }
return dequeueAccessUnitMPEGAudio();
}
}
@@ -531,8 +545,12 @@
memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
- CHECK_GE(timeUs, 0ll);
+ if (timeUs < 0ll) {
+ ALOGE("negative timeUs");
+ return NULL;
+ }
accessUnit->meta()->setInt64("timeUs", timeUs);
+ accessUnit->meta()->setInt32("isSync", 1);
memmove(
mBuffer->data(),
@@ -550,15 +568,24 @@
}
ABitReader bits(mBuffer->data(), 4);
- CHECK_EQ(bits.getBits(8), 0xa0);
+ if (bits.getBits(8) != 0xa0) {
+ ALOGE("Unexpected bit values");
+ return NULL;
+ }
unsigned numAUs = bits.getBits(8);
bits.skipBits(8);
unsigned quantization_word_length __unused = bits.getBits(2);
unsigned audio_sampling_frequency = bits.getBits(3);
unsigned num_channels = bits.getBits(3);
- CHECK_EQ(audio_sampling_frequency, 2); // 48kHz
- CHECK_EQ(num_channels, 1u); // stereo!
+ if (audio_sampling_frequency != 2) {
+ ALOGE("Wrong sampling freq");
+ return NULL;
+ }
+ if (num_channels != 1u) {
+ ALOGE("Wrong channel #");
+ return NULL;
+ }
if (mFormat == NULL) {
mFormat = new MetaData;
@@ -580,8 +607,12 @@
memcpy(accessUnit->data(), mBuffer->data() + 4, payloadSize);
int64_t timeUs = fetchTimestamp(payloadSize + 4);
- CHECK_GE(timeUs, 0ll);
+ if (timeUs < 0ll) {
+ ALOGE("Negative timeUs");
+ return NULL;
+ }
accessUnit->meta()->setInt64("timeUs", timeUs);
+ accessUnit->meta()->setInt32("isSync", 1);
int16_t *ptr = (int16_t *)accessUnit->data();
for (size_t i = 0; i < payloadSize / sizeof(int16_t); ++i) {
@@ -603,14 +634,19 @@
return NULL;
}
- CHECK(!mRangeInfos.empty());
+ if (mRangeInfos.empty()) {
+ return NULL;
+ }
const RangeInfo &info = *mRangeInfos.begin();
if (mBuffer->size() < info.mLength) {
return NULL;
}
- CHECK_GE(info.mTimestampUs, 0ll);
+ if (info.mTimestampUs < 0ll) {
+ ALOGE("Negative info.mTimestampUs");
+ return NULL;
+ }
// The idea here is consume all AAC frames starting at offsets before
// info.mLength so we can assign a meaningful timestamp without
@@ -627,17 +663,26 @@
// adts_fixed_header
- CHECK_EQ(bits.getBits(12), 0xfffu);
+ if (bits.getBits(12) != 0xfffu) {
+ ALOGE("Wrong atds_fixed_header");
+ return NULL;
+ }
bits.skipBits(3); // ID, layer
bool protection_absent __unused = bits.getBits(1) != 0;
if (mFormat == NULL) {
unsigned profile = bits.getBits(2);
- CHECK_NE(profile, 3u);
+ if (profile == 3u) {
+ ALOGE("profile should not be 3");
+ return NULL;
+ }
unsigned sampling_freq_index = bits.getBits(4);
bits.getBits(1); // private_bit
unsigned channel_configuration = bits.getBits(3);
- CHECK_NE(channel_configuration, 0u);
+ if (channel_configuration == 0u) {
+ ALOGE("channel_config should not be 0");
+ return NULL;
+ }
bits.skipBits(2); // original_copy, home
mFormat = MakeAACCodecSpecificData(
@@ -647,8 +692,14 @@
int32_t sampleRate;
int32_t numChannels;
- CHECK(mFormat->findInt32(kKeySampleRate, &sampleRate));
- CHECK(mFormat->findInt32(kKeyChannelCount, &numChannels));
+ if (!mFormat->findInt32(kKeySampleRate, &sampleRate)) {
+ ALOGE("SampleRate not found");
+ return NULL;
+ }
+ if (!mFormat->findInt32(kKeyChannelCount, &numChannels)) {
+ ALOGE("ChannelCount not found");
+ return NULL;
+ }
ALOGI("found AAC codec config (%d Hz, %d channels)",
sampleRate, numChannels);
@@ -671,7 +722,8 @@
if (number_of_raw_data_blocks_in_frame != 0) {
// To be implemented.
- TRESPASS();
+ ALOGE("Should not reach here.");
+ return NULL;
}
if (offset + aac_frame_length > mBuffer->size()) {
@@ -693,6 +745,7 @@
mBuffer->setRange(0, mBuffer->size() - offset);
accessUnit->meta()->setInt64("timeUs", timeUs);
+ accessUnit->meta()->setInt32("isSync", 1);
return accessUnit;
}
@@ -702,7 +755,9 @@
bool first = true;
while (size > 0) {
- CHECK(!mRangeInfos.empty());
+ if (mRangeInfos.empty()) {
+ return timeUs;
+ }
RangeInfo *info = &*mRangeInfos.begin();
@@ -743,6 +798,7 @@
const uint8_t *nalStart;
size_t nalSize;
bool foundSlice = false;
+ bool foundIDR = false;
while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
if (nalSize == 0) continue;
@@ -750,6 +806,9 @@
bool flush = false;
if (nalType == 1 || nalType == 5) {
+ if (nalType == 5) {
+ foundIDR = true;
+ }
if (foundSlice) {
ABitReader br(nalStart + 1, nalSize);
unsigned first_mb_in_slice = parseUE(&br);
@@ -797,7 +856,10 @@
unsigned nalType = mBuffer->data()[pos.nalOffset] & 0x1f;
if (nalType == 6 && pos.nalSize > 0) {
- CHECK_LT(seiIndex, sei->size() / sizeof(NALPosition));
+ if (seiIndex >= sei->size() / sizeof(NALPosition)) {
+ ALOGE("Wrong seiIndex");
+ return NULL;
+ }
NALPosition &seiPos = ((NALPosition *)sei->data())[seiIndex++];
seiPos.nalOffset = dstOffset + 4;
seiPos.nalSize = pos.nalSize;
@@ -835,9 +897,15 @@
mBuffer->setRange(0, mBuffer->size() - nextScan);
int64_t timeUs = fetchTimestamp(nextScan);
- CHECK_GE(timeUs, 0ll);
+ if (timeUs < 0ll) {
+ ALOGE("Negative timeUs");
+ return NULL;
+ }
accessUnit->meta()->setInt64("timeUs", timeUs);
+ if (foundIDR) {
+ accessUnit->meta()->setInt32("isSync", 1);
+ }
if (mFormat == NULL) {
mFormat = MakeAVCCodecSpecificData(accessUnit);
@@ -854,7 +922,10 @@
totalSize += nalSize;
}
- CHECK_EQ(err, (status_t)-EAGAIN);
+ if (err != (status_t)-EAGAIN) {
+ ALOGE("Unexpeted err");
+ return NULL;
+ }
return NULL;
}
@@ -871,9 +942,12 @@
size_t frameSize;
int samplingRate, numChannels, bitrate, numSamples;
- CHECK(GetMPEGAudioFrameSize(
+ if (!GetMPEGAudioFrameSize(
header, &frameSize, &samplingRate, &numChannels,
- &bitrate, &numSamples));
+ &bitrate, &numSamples)) {
+ ALOGE("Failed to get audio frame size");
+ return NULL;
+ }
if (size < frameSize) {
return NULL;
@@ -891,9 +965,13 @@
mBuffer->setRange(0, mBuffer->size() - frameSize);
int64_t timeUs = fetchTimestamp(frameSize);
- CHECK_GE(timeUs, 0ll);
+ if (timeUs < 0ll) {
+ ALOGE("Negative timeUs");
+ return NULL;
+ }
accessUnit->meta()->setInt64("timeUs", timeUs);
+ accessUnit->meta()->setInt32("isSync", 1);
if (mFormat == NULL) {
mFormat = new MetaData;
@@ -912,7 +990,7 @@
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
break;
default:
- TRESPASS();
+ return NULL;
}
mFormat->setInt32(kKeySampleRate, samplingRate);
@@ -923,7 +1001,10 @@
}
static void EncodeSize14(uint8_t **_ptr, size_t size) {
- CHECK_LE(size, 0x3fff);
+ if (size > 0x3fff) {
+ ALOGE("Wrong size");
+ return;
+ }
uint8_t *ptr = *_ptr;
@@ -970,6 +1051,9 @@
int pprevStartCode = -1;
int prevStartCode = -1;
int currentStartCode = -1;
+ bool gopFound = false;
+ bool isClosedGop = false;
+ bool brokenLink = false;
size_t offset = 0;
while (offset + 3 < size) {
@@ -995,7 +1079,10 @@
// seqHeader without/with extension
if (mFormat == NULL) {
- CHECK_GE(size, 7u);
+ if (size < 7u) {
+ ALOGE("Size too small");
+ return NULL;
+ }
unsigned width =
(data[4] << 4) | data[5] >> 4;
@@ -1032,6 +1119,13 @@
}
}
+ if (mFormat != NULL && currentStartCode == 0xb8) {
+ // GOP layer
+ gopFound = true;
+ isClosedGop = (data[offset + 7] & 0x40) != 0;
+ brokenLink = (data[offset + 7] & 0x20) != 0;
+ }
+
if (mFormat != NULL && currentStartCode == 0x00) {
// Picture start
@@ -1048,11 +1142,17 @@
mBuffer->setRange(0, mBuffer->size() - offset);
int64_t timeUs = fetchTimestamp(offset);
- CHECK_GE(timeUs, 0ll);
+ if (timeUs < 0ll) {
+ ALOGE("Negative timeUs");
+ return NULL;
+ }
offset = 0;
accessUnit->meta()->setInt64("timeUs", timeUs);
+ if (gopFound && (!brokenLink || isClosedGop)) {
+ accessUnit->meta()->setInt32("isSync", 1);
+ }
ALOGV("returning MPEG video access unit at time %" PRId64 " us",
timeUs);
@@ -1078,7 +1178,7 @@
}
if (memcmp(kStartCode, data, 3)) {
- TRESPASS();
+ return -EAGAIN;
}
size_t offset = 3;
@@ -1138,25 +1238,37 @@
case EXPECT_VISUAL_OBJECT_START:
{
- CHECK_EQ(chunkType, 0xb5);
+ if (chunkType != 0xb5) {
+ ALOGE("Unexpected chunkType");
+ return NULL;
+ }
state = EXPECT_VO_START;
break;
}
case EXPECT_VO_START:
{
- CHECK_LE(chunkType, 0x1f);
+ if (chunkType > 0x1f) {
+ ALOGE("Unexpected chunkType");
+ return NULL;
+ }
state = EXPECT_VOL_START;
break;
}
case EXPECT_VOL_START:
{
- CHECK((chunkType & 0xf0) == 0x20);
+ if ((chunkType & 0xf0) != 0x20) {
+ ALOGE("Wrong chunkType");
+ return NULL;
+ }
- CHECK(ExtractDimensionsFromVOLHeader(
+ if (!ExtractDimensionsFromVOLHeader(
&data[offset], chunkSize,
- &width, &height));
+ &width, &height)) {
+ ALOGE("Failed to get dimension");
+ return NULL;
+ }
state = WAIT_FOR_VOP_START;
break;
@@ -1197,6 +1309,8 @@
case SKIP_TO_VOP_START:
{
if (chunkType == 0xb6) {
+ int vopCodingType = (data[offset + 4] & 0xc0) >> 6;
+
offset += chunkSize;
sp<ABuffer> accessUnit = new ABuffer(offset);
@@ -1207,11 +1321,17 @@
mBuffer->setRange(0, size);
int64_t timeUs = fetchTimestamp(offset);
- CHECK_GE(timeUs, 0ll);
+ if (timeUs < 0ll) {
+ ALOGE("Negative timeus");
+ return NULL;
+ }
offset = 0;
accessUnit->meta()->setInt64("timeUs", timeUs);
+ if (vopCodingType == 0) { // intra-coded VOP
+ accessUnit->meta()->setInt32("isSync", 1);
+ }
ALOGV("returning MPEG4 video access unit at time %" PRId64 " us",
timeUs);
@@ -1228,7 +1348,8 @@
}
default:
- TRESPASS();
+ ALOGE("Unknown state: %d", state);
+ return NULL;
}
if (discard) {
@@ -1245,4 +1366,37 @@
return NULL;
}
+void ElementaryStreamQueue::signalEOS() {
+ if (!mEOSReached) {
+ if (mMode == MPEG_VIDEO) {
+ const char *theEnd = "\x00\x00\x01\x00";
+ appendData(theEnd, 4, 0);
+ }
+ mEOSReached = true;
+ } else {
+ ALOGW("EOS already signaled");
+ }
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMetadata() {
+ size_t size = mBuffer->size();
+ if (!size) {
+ return NULL;
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(size);
+ int64_t timeUs = fetchTimestamp(size);
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+
+ memcpy(accessUnit->data(), mBuffer->data(), size);
+ mBuffer->setRange(0, 0);
+
+ if (mFormat == NULL) {
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_DATA_METADATA);
+ }
+
+ return accessUnit;
+}
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 45b4624..e9f96b7 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -37,6 +37,7 @@
MPEG_VIDEO,
MPEG4_VIDEO,
PCM_AUDIO,
+ METADATA,
};
enum Flags {
@@ -46,6 +47,7 @@
ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
status_t appendData(const void *data, size_t size, int64_t timeUs);
+ void signalEOS();
void clear(bool clearFormat);
sp<ABuffer> dequeueAccessUnit();
@@ -60,6 +62,7 @@
Mode mMode;
uint32_t mFlags;
+ bool mEOSReached;
sp<ABuffer> mBuffer;
List<RangeInfo> mRangeInfos;
@@ -73,6 +76,7 @@
sp<ABuffer> dequeueAccessUnitMPEGVideo();
sp<ABuffer> dequeueAccessUnitMPEG4Video();
sp<ABuffer> dequeueAccessUnitPCMAudio();
+ sp<ABuffer> dequeueAccessUnitMetadata();
// consume a logical (compressed) access unit of size "size",
// returns its timestamp in us (or -1 if no time information).
diff --git a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
index 85859f7..6d9fe9d 100644
--- a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
@@ -265,7 +265,10 @@
}
unsigned PES_packet_length = U16_AT(mBuffer->data() + 4);
- CHECK_NE(PES_packet_length, 0u);
+ if (PES_packet_length == 0u) {
+ ALOGE("PES_packet_length is 0");
+ return -EAGAIN;
+ }
size_t n = PES_packet_length + 6;
@@ -286,7 +289,10 @@
return ERROR_MALFORMED;
}
- CHECK_EQ(packet_startcode_prefix, 0x000001u);
+ if (packet_startcode_prefix != 0x000001u) {
+ ALOGE("Wrong PES prefix");
+ return ERROR_MALFORMED;
+ }
unsigned stream_id = br.getBits(8);
ALOGV("stream_id = 0x%02x", stream_id);
@@ -366,8 +372,7 @@
&& stream_id != 0xff // program_stream_directory
&& stream_id != 0xf2 // DSMCC
&& stream_id != 0xf8) { // H.222.1 type E
- CHECK_EQ(br.getBits(2), 2u);
-
+ /* unsigned PES_marker_bits = */br.getBits(2); // should be 0x2(hex)
/* unsigned PES_scrambling_control = */br.getBits(2);
/* unsigned PES_priority = */br.getBits(1);
/* unsigned data_alignment_indicator = */br.getBits(1);
@@ -400,16 +405,26 @@
uint64_t PTS = 0, DTS = 0;
if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
- CHECK_GE(optional_bytes_remaining, 5u);
+ if (optional_bytes_remaining < 5u) {
+ return ERROR_MALFORMED;
+ }
- CHECK_EQ(br.getBits(4), PTS_DTS_flags);
+ if (br.getBits(4) != PTS_DTS_flags) {
+ return ERROR_MALFORMED;
+ }
PTS = ((uint64_t)br.getBits(3)) << 30;
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
PTS |= ((uint64_t)br.getBits(15)) << 15;
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
PTS |= br.getBits(15);
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ALOGV("PTS = %" PRIu64, PTS);
// ALOGI("PTS = %.2f secs", PTS / 90000.0f);
@@ -417,16 +432,26 @@
optional_bytes_remaining -= 5;
if (PTS_DTS_flags == 3) {
- CHECK_GE(optional_bytes_remaining, 5u);
+ if (optional_bytes_remaining < 5u) {
+ return ERROR_MALFORMED;
+ }
- CHECK_EQ(br.getBits(4), 1u);
+ if (br.getBits(4) != 1u) {
+ return ERROR_MALFORMED;
+ }
DTS = ((uint64_t)br.getBits(3)) << 30;
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
DTS |= ((uint64_t)br.getBits(15)) << 15;
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
DTS |= br.getBits(15);
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ALOGV("DTS = %" PRIu64, DTS);
@@ -435,40 +460,62 @@
}
if (ESCR_flag) {
- CHECK_GE(optional_bytes_remaining, 6u);
+ if (optional_bytes_remaining < 6u) {
+ return ERROR_MALFORMED;
+ }
br.getBits(2);
uint64_t ESCR = ((uint64_t)br.getBits(3)) << 30;
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ESCR |= ((uint64_t)br.getBits(15)) << 15;
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ESCR |= br.getBits(15);
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
ALOGV("ESCR = %" PRIu64, ESCR);
/* unsigned ESCR_extension = */br.getBits(9);
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
optional_bytes_remaining -= 6;
}
if (ES_rate_flag) {
- CHECK_GE(optional_bytes_remaining, 3u);
+ if (optional_bytes_remaining < 3u) {
+ return ERROR_MALFORMED;
+ }
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
/* unsigned ES_rate = */br.getBits(22);
- CHECK_EQ(br.getBits(1), 1u);
+ if (br.getBits(1) != 1u) {
+ return ERROR_MALFORMED;
+ }
optional_bytes_remaining -= 3;
}
+ if (br.numBitsLeft() < optional_bytes_remaining * 8) {
+ return ERROR_MALFORMED;
+ }
+
br.skipBits(optional_bytes_remaining * 8);
// ES data follows.
- CHECK_GE(PES_packet_length, PES_header_data_length + 3);
+ if (PES_packet_length < PES_header_data_length + 3) {
+ return ERROR_MALFORMED;
+ }
unsigned dataLength =
PES_packet_length - 3 - PES_header_data_length;
@@ -481,7 +528,9 @@
return ERROR_MALFORMED;
}
- CHECK_GE(br.numBitsLeft(), dataLength * 8);
+ if (br.numBitsLeft() < dataLength * 8) {
+ return ERROR_MALFORMED;
+ }
ssize_t index = mTracks.indexOfKey(stream_id);
if (index < 0 && mScanning) {
@@ -521,10 +570,14 @@
return err;
}
} else if (stream_id == 0xbe) { // padding_stream
- CHECK_NE(PES_packet_length, 0u);
+ if (PES_packet_length == 0u) {
+ return ERROR_MALFORMED;
+ }
br.skipBits(PES_packet_length * 8);
} else {
- CHECK_NE(PES_packet_length, 0u);
+ if (PES_packet_length == 0u) {
+ return ERROR_MALFORMED;
+ }
br.skipBits(PES_packet_length * 8);
}
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 1f43d6d..f5c33cf 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -85,12 +85,6 @@
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
- int64_t seekTimeUs;
- ReadOptions::SeekMode seekMode;
- if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
- return ERROR_UNSUPPORTED;
- }
-
status_t finalResult;
while (!mImpl->hasBufferAvailable(&finalResult)) {
if (finalResult != OK) {
@@ -103,6 +97,17 @@
}
}
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode seekMode;
+ if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+ // A seek was requested, but we don't actually support seeking and so can only "seek" to
+ // the current position
+ int64_t nextBufTimeUs;
+ if (mImpl->nextBufferTime(&nextBufTimeUs) != OK || seekTimeUs != nextBufTimeUs) {
+ return ERROR_UNSUPPORTED;
+ }
+ }
+
return mImpl->read(out, options);
}
@@ -126,7 +131,10 @@
bool seekable = true;
if (mSourceImpls.size() > 1) {
- CHECK_EQ(mSourceImpls.size(), 2u);
+ if (mSourceImpls.size() != 2u) {
+ ALOGE("Wrong size");
+ return NULL;
+ }
sp<MetaData> meta = mSourceImpls.editItemAt(index)->getFormat();
const char *mime;
@@ -199,6 +207,9 @@
ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
if (n < (ssize_t)kTSPacketSize) {
+ if (n >= 0) {
+ mParser->signalEOS(ERROR_END_OF_STREAM);
+ }
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
}
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 07ea605..5f0f567 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -31,6 +31,8 @@
libdl
LOCAL_MODULE:= libstagefright_omx
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/omx/FrameDropper.cpp b/media/libstagefright/omx/FrameDropper.cpp
index 9fba0b7..9a4952e 100644
--- a/media/libstagefright/omx/FrameDropper.cpp
+++ b/media/libstagefright/omx/FrameDropper.cpp
@@ -50,20 +50,23 @@
if (mDesiredMinTimeUs < 0) {
mDesiredMinTimeUs = timeUs + mMinIntervalUs;
- ALOGV("first frame %lld, next desired frame %lld", timeUs, mDesiredMinTimeUs);
+ ALOGV("first frame %lld, next desired frame %lld",
+ (long long)timeUs, (long long)mDesiredMinTimeUs);
return false;
}
if (timeUs < (mDesiredMinTimeUs - kMaxJitterUs)) {
ALOGV("drop frame %lld, desired frame %lld, diff %lld",
- timeUs, mDesiredMinTimeUs, mDesiredMinTimeUs - timeUs);
+ (long long)timeUs, (long long)mDesiredMinTimeUs,
+ (long long)(mDesiredMinTimeUs - timeUs));
return true;
}
int64_t n = (timeUs - mDesiredMinTimeUs + kMaxJitterUs) / mMinIntervalUs;
mDesiredMinTimeUs += (n + 1) * mMinIntervalUs;
ALOGV("keep frame %lld, next desired frame %lld, diff %lld",
- timeUs, mDesiredMinTimeUs, mDesiredMinTimeUs - timeUs);
+ (long long)timeUs, (long long)mDesiredMinTimeUs,
+ (long long)(mDesiredMinTimeUs - timeUs));
return false;
}
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 477cfc6..01cd8f0 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -38,13 +38,19 @@
static const bool EXTRA_CHECK = true;
-GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance,
- uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount,
- bool useGraphicBufferInMeta) :
+GraphicBufferSource::GraphicBufferSource(
+ OMXNodeInstance* nodeInstance,
+ uint32_t bufferWidth,
+ uint32_t bufferHeight,
+ uint32_t bufferCount,
+ bool useGraphicBufferInMeta,
+ const sp<IGraphicBufferConsumer> &consumer) :
mInitCheck(UNKNOWN_ERROR),
mNodeInstance(nodeInstance),
mExecuting(false),
mSuspended(false),
+ mIsPersistent(false),
+ mConsumer(consumer),
mNumFramesAvailable(0),
mEndOfStream(false),
mEndOfStreamSent(false),
@@ -74,20 +80,22 @@
return;
}
- String8 name("GraphicBufferSource");
+ if (mConsumer == NULL) {
+ String8 name("GraphicBufferSource");
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mConsumer->setConsumerName(name);
- mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight);
- mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER);
-
- mInitCheck = mConsumer->setMaxAcquiredBufferCount(bufferCount);
- if (mInitCheck != NO_ERROR) {
- ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
- bufferCount, mInitCheck);
- return;
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mConsumer->setConsumerName(name);
+ mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER);
+ mInitCheck = mConsumer->setMaxAcquiredBufferCount(bufferCount);
+ if (mInitCheck != NO_ERROR) {
+ ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+ bufferCount, mInitCheck);
+ return;
+ }
+ } else {
+ mIsPersistent = true;
}
-
+ mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight);
// Note that we can't create an sp<...>(this) in a ctor that will not keep a
// reference once the ctor ends, as that would cause the refcount of 'this'
// dropping to 0 at the end of the ctor. Since all we need is a wp<...>
@@ -107,7 +115,7 @@
GraphicBufferSource::~GraphicBufferSource() {
ALOGV("~GraphicBufferSource");
- if (mConsumer != NULL) {
+ if (mConsumer != NULL && !mIsPersistent) {
status_t err = mConsumer->consumerDisconnect();
if (err != NO_ERROR) {
ALOGW("consumerDisconnect failed: %d", err);
@@ -292,8 +300,16 @@
if (id == mLatestBufferId) {
CHECK_GT(mLatestBufferUseCount--, 0);
} else {
- mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ if (mIsPersistent) {
+ mConsumer->detachBuffer(id);
+ int outSlot;
+ mConsumer->attachBuffer(&outSlot, mBufferSlot[id]);
+ mConsumer->releaseBuffer(outSlot, 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ } else {
+ mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ }
}
} else {
ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
@@ -375,8 +391,15 @@
--mNumFramesAvailable;
- mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+ if (mIsPersistent) {
+ mConsumer->detachBuffer(item.mBuf);
+ mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
+ mConsumer->releaseBuffer(item.mBuf, 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ } else {
+ mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+ }
}
return;
}
@@ -463,8 +486,15 @@
if (err != OK) {
ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
- mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ if (mIsPersistent) {
+ mConsumer->detachBuffer(item.mBuf);
+ mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
+ mConsumer->releaseBuffer(item.mBuf, 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ } else {
+ mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ }
} else {
ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
setLatestBuffer_l(item, dropped);
@@ -540,12 +570,19 @@
if (mLatestBufferId >= 0) {
if (mLatestBufferUseCount == 0) {
- mConsumer->releaseBuffer(
- mLatestBufferId,
- mLatestBufferFrameNum,
- EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR,
- Fence::NO_FENCE);
+ if (mIsPersistent) {
+ mConsumer->detachBuffer(mLatestBufferId);
+
+ int outSlot;
+ mConsumer->attachBuffer(&outSlot, mBufferSlot[mLatestBufferId]);
+
+ mConsumer->releaseBuffer(outSlot, 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ } else {
+ mConsumer->releaseBuffer(
+ mLatestBufferId, mLatestBufferFrameNum,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ }
}
}
@@ -787,8 +824,16 @@
ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mBuf);
mBufferSlot[item.mBuf] = item.mGraphicBuffer;
}
- mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+
+ if (mIsPersistent) {
+ mConsumer->detachBuffer(item.mBuf);
+ mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
+ mConsumer->releaseBuffer(item.mBuf, 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ } else {
+ mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+ }
}
return;
}
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 1067472..1047fb3 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -30,7 +30,7 @@
namespace android {
-class FrameDropper;
+struct FrameDropper;
/*
* This class is used to feed OMX codecs from a Surface via BufferQueue.
@@ -50,9 +50,15 @@
*/
class GraphicBufferSource : public BufferQueue::ConsumerListener {
public:
- GraphicBufferSource(OMXNodeInstance* nodeInstance,
- uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount,
- bool useGraphicBufferInMeta = false);
+ GraphicBufferSource(
+ OMXNodeInstance* nodeInstance,
+ uint32_t bufferWidth,
+ uint32_t bufferHeight,
+ uint32_t bufferCount,
+ bool useGraphicBufferInMeta = false,
+ const sp<IGraphicBufferConsumer> &consumer = NULL
+ );
+
virtual ~GraphicBufferSource();
// We can't throw an exception if the constructor fails, so we just set
@@ -219,6 +225,7 @@
// Our BufferQueue interfaces. mProducer is passed to the producer through
// getIGraphicBufferProducer, and mConsumer is used internally to retrieve
// the buffers queued by the producer.
+ bool mIsPersistent;
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
@@ -240,7 +247,7 @@
Vector<CodecBuffer> mCodecBuffers;
////
- friend class AHandlerReflector<GraphicBufferSource>;
+ friend struct AHandlerReflector<GraphicBufferSource>;
enum {
kWhatRepeatLastFrame,
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index f8d38ff..a1ceb2e 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -32,6 +32,7 @@
#include "OMXMaster.h"
+#include <OMX_AsString.h>
#include <OMX_Component.h>
namespace android {
@@ -233,11 +234,11 @@
instance, &handle);
if (err != OMX_ErrorNone) {
- ALOGE("FAILED to allocate omx component '%s'", name);
+ ALOGE("FAILED to allocate omx component '%s' err=%s(%#x)", name, asString(err), err);
instance->onGetHandleFailed();
- return UNKNOWN_ERROR;
+ return StatusFromOMXError(err);
}
*node = makeNodeID(instance);
@@ -377,6 +378,20 @@
port_index, bufferProducer);
}
+status_t OMX::createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer) {
+ return OMXNodeInstance::createPersistentInputSurface(
+ bufferProducer, bufferConsumer);
+}
+
+status_t OMX::setInputSurface(
+ node_id node, OMX_U32 port_index,
+ const sp<IGraphicBufferConsumer> &bufferConsumer) {
+ return findInstance(node)->setInputSurface(port_index, bufferConsumer);
+}
+
+
status_t OMX::signalEndOfInputStream(node_id node) {
return findInstance(node)->signalEndOfInputStream();
}
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 4779d6a..91cee73 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -220,13 +220,15 @@
return mNodeID;
}
-static status_t StatusFromOMXError(OMX_ERRORTYPE err) {
+status_t StatusFromOMXError(OMX_ERRORTYPE err) {
switch (err) {
case OMX_ErrorNone:
return OK;
case OMX_ErrorUnsupportedSetting:
case OMX_ErrorUnsupportedIndex:
return ERROR_UNSUPPORTED;
+ case OMX_ErrorInsufficientResources:
+ return NO_MEMORY;
default:
return UNKNOWN_ERROR;
}
@@ -787,9 +789,8 @@
return OK;
}
-status_t OMXNodeInstance::createInputSurface(
- OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer) {
- Mutex::Autolock autolock(mLock);
+status_t OMXNodeInstance::createGraphicBufferSource(
+ OMX_U32 portIndex, sp<IGraphicBufferConsumer> bufferConsumer) {
status_t err;
const sp<GraphicBufferSource>& surfaceCheck = getGraphicBufferSource();
@@ -827,19 +828,75 @@
return INVALID_OPERATION;
}
- GraphicBufferSource* bufferSource = new GraphicBufferSource(
- this, def.format.video.nFrameWidth, def.format.video.nFrameHeight,
- def.nBufferCountActual, usingGraphicBuffer);
+ sp<GraphicBufferSource> bufferSource = new GraphicBufferSource(this,
+ def.format.video.nFrameWidth,
+ def.format.video.nFrameHeight,
+ def.nBufferCountActual,
+ usingGraphicBuffer,
+ bufferConsumer);
+
if ((err = bufferSource->initCheck()) != OK) {
- delete bufferSource;
return err;
}
setGraphicBufferSource(bufferSource);
- *bufferProducer = bufferSource->getIGraphicBufferProducer();
return OK;
}
+status_t OMXNodeInstance::createInputSurface(
+ OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer) {
+ Mutex::Autolock autolock(mLock);
+ status_t err = createGraphicBufferSource(portIndex);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *bufferProducer = mGraphicBufferSource->getIGraphicBufferProducer();
+ return OK;
+}
+
+//static
+status_t OMXNodeInstance::createPersistentInputSurface(
+ sp<IGraphicBufferProducer> *bufferProducer,
+ sp<IGraphicBufferConsumer> *bufferConsumer) {
+ String8 name("GraphicBufferSource");
+
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ consumer->setConsumerName(name);
+ consumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER);
+
+ status_t err = consumer->setMaxAcquiredBufferCount(
+ BufferQueue::MAX_MAX_ACQUIRED_BUFFERS);
+ if (err != NO_ERROR) {
+ ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+ BufferQueue::MAX_MAX_ACQUIRED_BUFFERS, err);
+ return err;
+ }
+
+ sp<BufferQueue::ProxyConsumerListener> proxy =
+ new BufferQueue::ProxyConsumerListener(NULL);
+ err = consumer->consumerConnect(proxy, false);
+ if (err != NO_ERROR) {
+ ALOGE("Error connecting to BufferQueue: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+
+ *bufferProducer = producer;
+ *bufferConsumer = consumer;
+
+ return OK;
+}
+
+status_t OMXNodeInstance::setInputSurface(
+ OMX_U32 portIndex, const sp<IGraphicBufferConsumer> &bufferConsumer) {
+ Mutex::Autolock autolock(mLock);
+ return createGraphicBufferSource(portIndex, bufferConsumer);
+}
+
status_t OMXNodeInstance::signalEndOfInputStream() {
// For non-Surface input, the MediaCodec should convert the call to a
// pair of requests (dequeue input buffer, queue input buffer with EOS
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 801a1bd..04303c4 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -598,7 +598,7 @@
if (port->mTransition == PortInfo::DISABLING) {
if (port->mBuffers.empty()) {
- ALOGV("Port %d now disabled.", i);
+ ALOGV("Port %zu now disabled.", i);
port->mTransition = PortInfo::NONE;
notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL);
@@ -607,7 +607,7 @@
}
} else if (port->mTransition == PortInfo::ENABLING) {
if (port->mDef.bPopulated == OMX_TRUE) {
- ALOGV("Port %d now enabled.", i);
+ ALOGV("Port %zu now enabled.", i);
port->mTransition = PortInfo::NONE;
port->mDef.bEnabled = OMX_TRUE;
@@ -628,14 +628,14 @@
info->mTransition = PortInfo::NONE;
}
-void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex) {
+void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex __unused) {
}
-void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex) {
+void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex __unused) {
}
void SimpleSoftOMXComponent::onPortEnableCompleted(
- OMX_U32 portIndex, bool enabled) {
+ OMX_U32 portIndex __unused, bool enabled __unused) {
}
List<SimpleSoftOMXComponent::BufferInfo *> &
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
old mode 100644
new mode 100755
index 9b6958a..0f9c00c
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -40,11 +40,12 @@
{ "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
{ "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
- { "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
- { "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" },
+ { "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" },
+ { "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" },
{ "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },
{ "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
{ "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
+ { "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" },
{ "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
{ "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
{ "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
@@ -85,7 +86,7 @@
void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
if (libHandle == NULL) {
- ALOGE("unable to dlopen %s", libName.c_str());
+ ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror());
return OMX_ErrorComponentNotFound;
}
diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
index 9be637a..02e97f1 100644
--- a/media/libstagefright/omx/tests/Android.mk
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -11,7 +11,8 @@
$(TOP)/frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE := omx_tests
@@ -37,4 +38,7 @@
LOCAL_C_INCLUDES := \
frameworks/av/media/libstagefright/omx \
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
include $(BUILD_NATIVE_TEST)
diff --git a/media/libstagefright/omx/tests/FrameDropper_test.cpp b/media/libstagefright/omx/tests/FrameDropper_test.cpp
index 4ac72c4..f966b5e 100644
--- a/media/libstagefright/omx/tests/FrameDropper_test.cpp
+++ b/media/libstagefright/omx/tests/FrameDropper_test.cpp
@@ -100,7 +100,8 @@
for (size_t i = 0; i < size; ++i) {
int jitter = GetJitter(i);
int64_t testTimeUs = frames[i].timeUs + jitter;
- printf("time %lld, testTime %lld, jitter %d\n", frames[i].timeUs, testTimeUs, jitter);
+ printf("time %lld, testTime %lld, jitter %d\n",
+ (long long)frames[i].timeUs, (long long)testTimeUs, jitter);
EXPECT_EQ(frames[i].shouldDrop, mFrameDropper->shouldDrop(testTimeUs));
}
}
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index fdc8d23..be8bc13 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -32,7 +32,7 @@
namespace android {
struct ABuffer;
-struct MediaBuffer;
+class MediaBuffer;
struct ARTPWriter : public MediaWriter {
ARTPWriter(int fd);
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 9fedb71..c5e8c35 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -31,7 +31,8 @@
LOCAL_CFLAGS += -Wno-psabi
endif
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
@@ -54,7 +55,8 @@
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 0642343..ba17e90 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -651,7 +651,7 @@
int32_t result;
CHECK(msg->findInt32("result", &result));
- ALOGI("SETUP(%d) completed with result %d (%s)",
+ ALOGI("SETUP(%zu) completed with result %d (%s)",
index, result, strerror(-result));
if (result == OK) {
@@ -1012,7 +1012,7 @@
int32_t eos;
if (msg->findInt32("eos", &eos)) {
- ALOGI("received BYE on track index %d", trackIndex);
+ ALOGI("received BYE on track index %zu", trackIndex);
if (!mAllTracksHaveTime && dataReceivedOnAllChannels()) {
ALOGI("No time established => fake existing data");
@@ -1564,7 +1564,7 @@
new APacketSource(mSessionDesc, index);
if (source->initCheck() != OK) {
- ALOGW("Unsupported format. Ignoring track #%d.", index);
+ ALOGW("Unsupported format. Ignoring track #%zu.", index);
sp<AMessage> reply = new AMessage('setu', this);
reply->setSize("index", index);
@@ -1606,7 +1606,7 @@
info->mTimeScale = timescale;
info->mEOSReceived = false;
- ALOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str());
+ ALOGV("track #%zu URL=%s", mTracks.size(), trackURL.c_str());
AString request = "SETUP ";
request.append(trackURL);
@@ -1673,21 +1673,11 @@
}
size_t n = strlen(baseURL);
- if (baseURL[n - 1] == '/') {
- out->setTo(baseURL);
- out->append(url);
- } else {
- const char *slashPos = strrchr(baseURL, '/');
-
- if (slashPos > &baseURL[6]) {
- out->setTo(baseURL, slashPos - baseURL);
- } else {
- out->setTo(baseURL);
- }
-
+ out->setTo(baseURL);
+ if (baseURL[n - 1] != '/') {
out->append("/");
- out->append(url);
}
+ out->append(url);
return true;
}
@@ -1731,8 +1721,8 @@
}
void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
- ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx",
- trackIndex, rtpTime, ntpTime);
+ ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = %#016llx",
+ trackIndex, rtpTime, (long long)ntpTime);
int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
@@ -1851,8 +1841,8 @@
return false;
}
- ALOGV("track %d rtpTime=%d mediaTimeUs = %lld us (%.2f secs)",
- trackIndex, rtpTime, mediaTimeUs, mediaTimeUs / 1E6);
+ ALOGV("track %d rtpTime=%u mediaTimeUs = %lld us (%.2f secs)",
+ trackIndex, rtpTime, (long long)mediaTimeUs, mediaTimeUs / 1E6);
accessUnit->meta()->setInt64("timeUs", mediaTimeUs);
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 8d6ff5b..111e6c5 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -31,6 +31,9 @@
frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/native/include/media/openmax \
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
LOCAL_32_BIT_ONLY := true
include $(BUILD_NATIVE_TEST)
@@ -60,6 +63,39 @@
frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/native/include/media/openmax \
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := MediaCodecListOverrides_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ MediaCodecListOverrides_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libmedia \
+ libstagefright \
+ libstagefright_foundation \
+ libstagefright_omx \
+ libutils \
+ liblog
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/media/libstagefright \
+ frameworks/av/media/libstagefright/include \
+ frameworks/native/include/media/openmax \
+
+LOCAL_32_BIT_ONLY := true
+
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
include $(BUILD_NATIVE_TEST)
# Include subdirectory makefiles
diff --git a/media/libstagefright/tests/DummyRecorder.h b/media/libstagefright/tests/DummyRecorder.h
index 1cbea1b..cd4d0ee 100644
--- a/media/libstagefright/tests/DummyRecorder.h
+++ b/media/libstagefright/tests/DummyRecorder.h
@@ -24,7 +24,7 @@
namespace android {
-class MediaSource;
+struct MediaSource;
class MediaBuffer;
class DummyRecorder {
diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
new file mode 100644
index 0000000..cee62a3
--- /dev/null
+++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecListOverrides_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodecList.h>
+
+namespace android {
+
+static const char kTestOverridesStr[] =
+"<MediaCodecs>\n"
+" <Settings>\n"
+" <Setting name=\"supports-multiple-secure-codecs\" value=\"false\" />\n"
+" <Setting name=\"supports-secure-with-non-secure-codec\" value=\"true\" />\n"
+" </Settings>\n"
+" <Encoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" </Encoders>\n"
+" <Decoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"1\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+" </MediaCodec>\n"
+" </Decoders>\n"
+"</MediaCodecs>\n";
+
+class MediaCodecListOverridesTest : public ::testing::Test {
+public:
+ MediaCodecListOverridesTest() {}
+
+ void addMaxInstancesSetting(
+ const AString &key,
+ const AString &value,
+ KeyedVector<AString, CodecSettings> *results) {
+ CodecSettings settings;
+ settings.add("max-supported-instances", value);
+ results->add(key, settings);
+ }
+
+ void verifyProfileResults(const KeyedVector<AString, CodecSettings> &results) {
+ EXPECT_LT(0u, results.size());
+ for (size_t i = 0; i < results.size(); ++i) {
+ AString key = results.keyAt(i);
+ CodecSettings settings = results.valueAt(i);
+ EXPECT_EQ(1u, settings.size());
+ EXPECT_TRUE(settings.keyAt(0) == "max-supported-instances");
+ AString valueS = settings.valueAt(0);
+ int32_t value = strtol(valueS.c_str(), NULL, 10);
+ EXPECT_LT(0, value);
+ ALOGV("profileCodecs results %s %s", key.c_str(), valueS.c_str());
+ }
+ }
+
+ void exportTestResultsToXML(const char *fileName) {
+ CodecSettings gR;
+ gR.add("supports-multiple-secure-codecs", "false");
+ gR.add("supports-secure-with-non-secure-codec", "true");
+ KeyedVector<AString, CodecSettings> eR;
+ addMaxInstancesSetting("OMX.qcom.video.encoder.avc video/avc", "4", &eR);
+ addMaxInstancesSetting("OMX.qcom.video.encoder.mpeg4 video/mp4v-es", "4", &eR);
+ KeyedVector<AString, CodecSettings> dR;
+ addMaxInstancesSetting("OMX.qcom.video.decoder.avc.secure video/avc", "1", &dR);
+ addMaxInstancesSetting("OMX.qcom.video.decoder.h263 video/3gpp", "4", &dR);
+ addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg2 video/mpeg2", "3", &dR);
+ addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg4 video/mp4v-es", "3", &dR);
+
+ exportResultsToXML(fileName, gR, eR, dR);
+ }
+};
+
+TEST_F(MediaCodecListOverridesTest, splitString) {
+ AString s = "abc123";
+ AString delimiter = " ";
+ AString s1;
+ AString s2;
+ EXPECT_FALSE(splitString(s, delimiter, &s1, &s2));
+ s = "abc 123";
+ EXPECT_TRUE(splitString(s, delimiter, &s1, &s2));
+ EXPECT_TRUE(s1 == "abc");
+ EXPECT_TRUE(s2 == "123");
+}
+
+// TODO: the codec component never returns OMX_EventCmdComplete in unit test.
+TEST_F(MediaCodecListOverridesTest, DISABLED_profileCodecs) {
+ sp<IMediaCodecList> list = MediaCodecList::getInstance();
+ Vector<sp<MediaCodecInfo>> infos;
+ for (size_t i = 0; i < list->countCodecs(); ++i) {
+ infos.push_back(list->getCodecInfo(i));
+ }
+ CodecSettings global_results;
+ KeyedVector<AString, CodecSettings> encoder_results;
+ KeyedVector<AString, CodecSettings> decoder_results;
+ profileCodecs(
+ infos, &global_results, &encoder_results, &decoder_results, true /* forceToMeasure */);
+ verifyProfileResults(encoder_results);
+ verifyProfileResults(decoder_results);
+}
+
+TEST_F(MediaCodecListOverridesTest, exportTestResultsToXML) {
+ const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml";
+ remove(fileName);
+
+ exportTestResultsToXML(fileName);
+
+ // verify
+ AString overrides;
+ FILE *f = fopen(fileName, "rb");
+ ASSERT_TRUE(f != NULL);
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ rewind(f);
+
+ char *buf = (char *)malloc(size);
+ EXPECT_EQ((size_t)1, fread(buf, size, 1, f));
+ overrides.setTo(buf, size);
+ fclose(f);
+ free(buf);
+
+ EXPECT_TRUE(overrides == kTestOverridesStr);
+
+ remove(fileName);
+}
+
+} // namespace android
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index fd889f9..3860e9b 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -19,6 +19,7 @@
#include <gtest/gtest.h>
#include <utils/String8.h>
+#include <utils/String16.h>
#include <utils/Errors.h>
#include <fcntl.h>
#include <unistd.h>
@@ -466,7 +467,7 @@
// Set up the MediaRecorder which runs in the same process as mediaserver
sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource,
int outputFormat, int videoEncoder, int width, int height, int fps) {
- sp<MediaRecorder> mr = new MediaRecorder();
+ sp<MediaRecorder> mr = new MediaRecorder(String16());
mr->setVideoSource(videoSource);
mr->setOutputFormat(outputFormat);
mr->setVideoEncoder(videoEncoder);
diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp
index 5c323c1..c1e663c 100644
--- a/media/libstagefright/tests/Utils_test.cpp
+++ b/media/libstagefright/tests/Utils_test.cpp
@@ -192,6 +192,87 @@
ASSERT_EQ(max(-4.3, 8.6), 8.6);
ASSERT_EQ(max(8.6, -4.3), 8.6);
+ ASSERT_FALSE(isInRange(-43, 86u, -44));
+ ASSERT_TRUE(isInRange(-43, 87u, -43));
+ ASSERT_TRUE(isInRange(-43, 88u, -1));
+ ASSERT_TRUE(isInRange(-43, 89u, 0));
+ ASSERT_TRUE(isInRange(-43, 90u, 46));
+ ASSERT_FALSE(isInRange(-43, 91u, 48));
+ ASSERT_FALSE(isInRange(-43, 92u, 50));
+
+ ASSERT_FALSE(isInRange(43, 86u, 42));
+ ASSERT_TRUE(isInRange(43, 87u, 43));
+ ASSERT_TRUE(isInRange(43, 88u, 44));
+ ASSERT_TRUE(isInRange(43, 89u, 131));
+ ASSERT_FALSE(isInRange(43, 90u, 133));
+ ASSERT_FALSE(isInRange(43, 91u, 135));
+
+ ASSERT_FALSE(isInRange(43u, 86u, 42u));
+ ASSERT_TRUE(isInRange(43u, 85u, 43u));
+ ASSERT_TRUE(isInRange(43u, 84u, 44u));
+ ASSERT_TRUE(isInRange(43u, 83u, 125u));
+ ASSERT_FALSE(isInRange(43u, 82u, 125u));
+ ASSERT_FALSE(isInRange(43u, 81u, 125u));
+
+ ASSERT_FALSE(isInRange(-43, ~0u, 43));
+ ASSERT_FALSE(isInRange(-43, ~0u, 44));
+ ASSERT_FALSE(isInRange(-43, ~0u, ~0));
+ ASSERT_FALSE(isInRange(-43, ~0u, 41));
+ ASSERT_FALSE(isInRange(-43, ~0u, 40));
+
+ ASSERT_FALSE(isInRange(43u, ~0u, 43u));
+ ASSERT_FALSE(isInRange(43u, ~0u, 41u));
+ ASSERT_FALSE(isInRange(43u, ~0u, 40u));
+ ASSERT_FALSE(isInRange(43u, ~0u, ~0u));
+
+ ASSERT_FALSE(isInRange(-43, 86u, -44, 0u));
+ ASSERT_FALSE(isInRange(-43, 86u, -44, 1u));
+ ASSERT_FALSE(isInRange(-43, 86u, -44, 2u));
+ ASSERT_FALSE(isInRange(-43, 86u, -44, ~0u));
+ ASSERT_TRUE(isInRange(-43, 87u, -43, 0u));
+ ASSERT_TRUE(isInRange(-43, 87u, -43, 1u));
+ ASSERT_TRUE(isInRange(-43, 87u, -43, 86u));
+ ASSERT_TRUE(isInRange(-43, 87u, -43, 87u));
+ ASSERT_FALSE(isInRange(-43, 87u, -43, 88u));
+ ASSERT_FALSE(isInRange(-43, 87u, -43, ~0u));
+ ASSERT_TRUE(isInRange(-43, 88u, -1, 0u));
+ ASSERT_TRUE(isInRange(-43, 88u, -1, 45u));
+ ASSERT_TRUE(isInRange(-43, 88u, -1, 46u));
+ ASSERT_FALSE(isInRange(-43, 88u, -1, 47u));
+ ASSERT_FALSE(isInRange(-43, 88u, -1, ~3u));
+ ASSERT_TRUE(isInRange(-43, 90u, 46, 0u));
+ ASSERT_TRUE(isInRange(-43, 90u, 46, 1u));
+ ASSERT_FALSE(isInRange(-43, 90u, 46, 2u));
+ ASSERT_FALSE(isInRange(-43, 91u, 48, 0u));
+ ASSERT_FALSE(isInRange(-43, 91u, 48, 2u));
+ ASSERT_FALSE(isInRange(-43, 91u, 48, ~6u));
+ ASSERT_FALSE(isInRange(-43, 92u, 50, 0u));
+ ASSERT_FALSE(isInRange(-43, 92u, 50, 1u));
+
+ ASSERT_FALSE(isInRange(43u, 86u, 42u, 0u));
+ ASSERT_FALSE(isInRange(43u, 86u, 42u, 1u));
+ ASSERT_FALSE(isInRange(43u, 86u, 42u, 2u));
+ ASSERT_FALSE(isInRange(43u, 86u, 42u, ~0u));
+ ASSERT_TRUE(isInRange(43u, 87u, 43u, 0u));
+ ASSERT_TRUE(isInRange(43u, 87u, 43u, 1u));
+ ASSERT_TRUE(isInRange(43u, 87u, 43u, 86u));
+ ASSERT_TRUE(isInRange(43u, 87u, 43u, 87u));
+ ASSERT_FALSE(isInRange(43u, 87u, 43u, 88u));
+ ASSERT_FALSE(isInRange(43u, 87u, 43u, ~0u));
+ ASSERT_TRUE(isInRange(43u, 88u, 60u, 0u));
+ ASSERT_TRUE(isInRange(43u, 88u, 60u, 70u));
+ ASSERT_TRUE(isInRange(43u, 88u, 60u, 71u));
+ ASSERT_FALSE(isInRange(43u, 88u, 60u, 72u));
+ ASSERT_FALSE(isInRange(43u, 88u, 60u, ~3u));
+ ASSERT_TRUE(isInRange(43u, 90u, 132u, 0u));
+ ASSERT_TRUE(isInRange(43u, 90u, 132u, 1u));
+ ASSERT_FALSE(isInRange(43u, 90u, 132u, 2u));
+ ASSERT_FALSE(isInRange(43u, 91u, 134u, 0u));
+ ASSERT_FALSE(isInRange(43u, 91u, 134u, 2u));
+ ASSERT_FALSE(isInRange(43u, 91u, 134u, ~6u));
+ ASSERT_FALSE(isInRange(43u, 92u, 136u, 0u));
+ ASSERT_FALSE(isInRange(43u, 92u, 136u, 1u));
+
ASSERT_EQ(periodicError(124, 100), 24);
ASSERT_EQ(periodicError(288, 100), 12);
ASSERT_EQ(periodicError(-345, 100), 45);
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 6a8b9fc..58fb12f 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -9,7 +9,8 @@
TimedTextSRTSource.cpp \
TimedTextPlayer.cpp
-LOCAL_CFLAGS += -Wno-multichar -Werror
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/include/media/stagefright/timedtext \
diff --git a/media/libstagefright/timedtext/test/Android.mk b/media/libstagefright/timedtext/test/Android.mk
index 9a9fde2..e0e0e0d 100644
--- a/media/libstagefright/timedtext/test/Android.mk
+++ b/media/libstagefright/timedtext/test/Android.mk
@@ -26,4 +26,7 @@
libstagefright_foundation \
libutils
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
include $(BUILD_NATIVE_TEST)
diff --git a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
index 3a06d61..211e732 100644
--- a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
+++ b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
@@ -63,10 +63,10 @@
}
virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
- if (offset >= mSize) return 0;
+ if ((size_t)offset >= mSize) return 0;
ssize_t avail = mSize - offset;
- if (avail > size) {
+ if ((size_t)avail > size) {
avail = size;
}
memcpy(data, mData + offset, avail);
diff --git a/media/libstagefright/webm/Android.mk b/media/libstagefright/webm/Android.mk
index 7081463..bc53c56 100644
--- a/media/libstagefright/webm/Android.mk
+++ b/media/libstagefright/webm/Android.mk
@@ -1,8 +1,10 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_CPPFLAGS += -D__STDINT_LIMITS \
- -Werror
+LOCAL_CPPFLAGS += -D__STDINT_LIMITS
+
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
LOCAL_SRC_FILES:= EbmlUtil.cpp \
WebmElement.cpp \
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index f70454a..fb28624 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -30,6 +30,9 @@
libui \
libutils \
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
+
LOCAL_MODULE:= libstagefright_wfd
LOCAL_MODULE_TAGS:= optional
diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp
index 2f4af5b..dbc511c 100644
--- a/media/libstagefright/wifi-display/VideoFormats.cpp
+++ b/media/libstagefright/wifi-display/VideoFormats.cpp
@@ -248,8 +248,8 @@
}
if (bestProfile == -1 || bestLevel == -1) {
- ALOGE("Profile or level not set for resolution type %d, index %d",
- type, index);
+ ALOGE("Profile or level not set for resolution type %d, index %zu",
+ type, index);
bestProfile = PROFILE_CBP;
bestLevel = LEVEL_31;
}
@@ -382,7 +382,6 @@
disableAll();
unsigned native, dummy;
- unsigned res[3];
size_t size = strlen(spec);
size_t offset = 0;
@@ -507,7 +506,7 @@
continue;
}
- ALOGV("type %u, index %u, %u x %u %c%u supported",
+ ALOGV("type %zu, index %zu, %zu x %zu %c%zu supported",
i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond);
uint32_t score = width * height * framesPerSecond;
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index 4e72533..c66a898 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -252,8 +252,6 @@
int64_t timeUs;
CHECK(tsPackets->meta()->findInt64("timeUs", &timeUs));
- const size_t numTSPackets = tsPackets->size() / 188;
-
size_t srcOffset = 0;
while (srcOffset < tsPackets->size()) {
sp<ABuffer> udpPacket =
@@ -672,8 +670,8 @@
default:
{
- ALOGW("Unknown RTCP packet type %u of size %d",
- (unsigned)data[1], headerLength);
+ ALOGW("Unknown RTCP packet type %u of size %zu",
+ (unsigned)data[1], headerLength);
break;
}
}
@@ -764,7 +762,7 @@
return OK;
}
-status_t RTPSender::parseAPP(const uint8_t *data, size_t size) {
+status_t RTPSender::parseAPP(const uint8_t *data, size_t size __unused) {
if (!memcmp("late", &data[8], 4)) {
int64_t avgLatencyUs = (int64_t)U64_AT(&data[12]);
int64_t maxLatencyUs = (int64_t)U64_AT(&data[20]);
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 8368945..471152e 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -747,7 +747,7 @@
buffer->meta()->setInt64("timeUs", timeUs);
ALOGV("[%s] time %lld us (%.2f secs)",
- mIsVideo ? "video" : "audio", timeUs, timeUs / 1E6);
+ mIsVideo ? "video" : "audio", (long long)timeUs, timeUs / 1E6);
memcpy(buffer->data(), outbuf->base() + offset, size);
diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h
index 5876e07..b182990 100644
--- a/media/libstagefright/wifi-display/source/Converter.h
+++ b/media/libstagefright/wifi-display/source/Converter.h
@@ -23,7 +23,7 @@
namespace android {
struct ABuffer;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
struct MediaCodec;
#define ENABLE_SILENCE_DETECTION 0
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 6080943..ed5a404 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -345,12 +345,14 @@
////////////////////////////////////////////////////////////////////////////////
WifiDisplaySource::PlaybackSession::PlaybackSession(
+ const String16 &opPackageName,
const sp<ANetworkSession> &netSession,
const sp<AMessage> ¬ify,
const in_addr &interfaceAddr,
const sp<IHDCP> &hdcp,
const char *path)
- : mNetSession(netSession),
+ : mOpPackageName(opPackageName),
+ mNetSession(netSession),
mNotify(notify),
mInterfaceAddr(interfaceAddr),
mHDCP(hdcp),
@@ -508,7 +510,7 @@
} else if (what == Converter::kWhatEOS) {
CHECK_EQ(what, Converter::kWhatEOS);
- ALOGI("output EOS on track %d", trackIndex);
+ ALOGI("output EOS on track %zu", trackIndex);
ssize_t index = mTracks.indexOfKey(trackIndex);
CHECK_GE(index, 0);
@@ -581,7 +583,7 @@
CHECK(msg->findSize("trackIndex", &trackIndex));
if (what == Track::kWhatStopped) {
- ALOGI("Track %d stopped", trackIndex);
+ ALOGI("Track %zu stopped", trackIndex);
sp<Track> track = mTracks.valueFor(trackIndex);
looper()->unregisterHandler(track->id());
@@ -821,21 +823,27 @@
return;
}
+ int64_t delayUs = 1000000; // default delay is 1 sec
int64_t sampleTimeUs;
status_t err = mExtractor->getSampleTime(&sampleTimeUs);
- int64_t nowUs = ALooper::GetNowUs();
+ if (err == OK) {
+ int64_t nowUs = ALooper::GetNowUs();
- if (mFirstSampleTimeRealUs < 0ll) {
- mFirstSampleTimeRealUs = nowUs;
- mFirstSampleTimeUs = sampleTimeUs;
+ if (mFirstSampleTimeRealUs < 0ll) {
+ mFirstSampleTimeRealUs = nowUs;
+ mFirstSampleTimeUs = sampleTimeUs;
+ }
+
+ int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs;
+ delayUs = whenUs - nowUs;
+ } else {
+ ALOGW("could not get sample time (%d)", err);
}
- int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs;
-
sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, this);
msg->setInt32("generation", mPullExtractorGeneration);
- msg->post(whenUs - nowUs);
+ msg->post(delayUs);
mPullExtractorPending = true;
}
@@ -1063,6 +1071,7 @@
status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) {
sp<AudioSource> audioSource = new AudioSource(
AUDIO_SOURCE_REMOTE_SUBMIX,
+ mOpPackageName,
48000 /* sampleRate */,
2 /* channelCount */);
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 2824143..f6673df 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -22,11 +22,13 @@
#include "VideoFormats.h"
#include "WifiDisplaySource.h"
+#include <utils/String16.h>
+
namespace android {
struct ABuffer;
struct IHDCP;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
struct MediaPuller;
struct MediaSource;
struct MediaSender;
@@ -36,6 +38,7 @@
// display.
struct WifiDisplaySource::PlaybackSession : public AHandler {
PlaybackSession(
+ const String16 &opPackageName,
const sp<ANetworkSession> &netSession,
const sp<AMessage> ¬ify,
const struct in_addr &interfaceAddr,
@@ -96,6 +99,8 @@
kWhatPullExtractorSample,
};
+ String16 mOpPackageName;
+
sp<ANetworkSession> mNetSession;
sp<AMessage> mNotify;
in_addr mInterfaceAddr;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 14d0951..e26165e 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -50,10 +50,12 @@
const AString WifiDisplaySource::sUserAgent = MakeUserAgent();
WifiDisplaySource::WifiDisplaySource(
+ const String16 &opPackageName,
const sp<ANetworkSession> &netSession,
const sp<IRemoteDisplayClient> &client,
const char *path)
- : mState(INITIALIZED),
+ : mOpPackageName(opPackageName),
+ mState(INITIALIZED),
mNetSession(netSession),
mClient(client),
mSessionID(0),
@@ -881,7 +883,7 @@
&framesPerSecond,
&interlaced));
- ALOGI("Picked video resolution %u x %u %c%u",
+ ALOGI("Picked video resolution %zu x %zu %c%zu",
width, height, interlaced ? 'i' : 'p', framesPerSecond);
ALOGI("Picked AVC profile %d, level %d",
@@ -1245,7 +1247,7 @@
sp<PlaybackSession> playbackSession =
new PlaybackSession(
- mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str());
+ mOpPackageName, mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str());
looper()->registerHandler(playbackSession);
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 0f779e4..c25a675 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -25,11 +25,13 @@
#include <netinet/in.h>
+#include <utils/String16.h>
+
namespace android {
struct AReplyToken;
struct IHDCP;
-struct IRemoteDisplayClient;
+class IRemoteDisplayClient;
struct ParsedMessage;
// Represents the RTSP server acting as a wifi display source.
@@ -38,6 +40,7 @@
static const unsigned kWifiDisplayDefaultPort = 7236;
WifiDisplaySource(
+ const String16 &opPackageName,
const sp<ANetworkSession> &netSession,
const sp<IRemoteDisplayClient> &client,
const char *path = NULL);
@@ -114,6 +117,8 @@
static const AString sUserAgent;
+ String16 mOpPackageName;
+
State mState;
VideoFormats mSupportedSourceVideoFormats;
sp<ANetworkSession> mNetSession;
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
index bb86dfc..dc67288 100644
--- a/media/libstagefright/yuv/Android.mk
+++ b/media/libstagefright/yuv/Android.mk
@@ -12,7 +12,7 @@
LOCAL_MODULE:= libstagefright_yuv
-LOCAL_CFLAGS += -Werror
-
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index 0e2e48c..ba47172 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -45,7 +45,8 @@
frameworks/av/services/mediaresourcemanager \
$(call include-path-for, audio-utils) \
frameworks/av/services/soundtrigger \
- frameworks/av/services/radio
+ frameworks/av/services/radio \
+ external/sonic
LOCAL_MODULE:= mediaserver
LOCAL_32_BIT_ONLY := true
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 99572f8..06b3c6e 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -33,6 +33,7 @@
#include "CameraService.h"
#include "MediaLogService.h"
#include "MediaPlayerService.h"
+#include "ResourceManagerService.h"
#include "service/AudioPolicyService.h"
#include "SoundTriggerHwService.h"
#include "RadioService.h"
@@ -128,6 +129,7 @@
ALOGI("ServiceManager: %p", sm.get());
AudioFlinger::instantiate();
MediaPlayerService::instantiate();
+ ResourceManagerService::instantiate();
CameraService::instantiate();
AudioPolicyService::instantiate();
SoundTriggerHwService::instantiate();
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 80c1c2f..cd0c462 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -154,6 +154,10 @@
} else {
mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
}
+ if (mData->mCodec == NULL) { // failed to create codec
+ AMediaCodec_delete(mData);
+ return NULL;
+ }
mData->mHandler = new CodecHandler(mData);
mData->mLooper->registerHandler(mData->mHandler);
mData->mGeneration = 1;
@@ -180,17 +184,21 @@
EXPORT
media_status_t AMediaCodec_delete(AMediaCodec *mData) {
- if (mData->mCodec != NULL) {
- mData->mCodec->release();
- mData->mCodec.clear();
- }
+ if (mData != NULL) {
+ if (mData->mCodec != NULL) {
+ mData->mCodec->release();
+ mData->mCodec.clear();
+ }
- if (mData->mLooper != NULL) {
- mData->mLooper->unregisterHandler(mData->mHandler->id());
- mData->mLooper->stop();
- mData->mLooper.clear();
+ if (mData->mLooper != NULL) {
+ if (mData->mHandler != NULL) {
+ mData->mLooper->unregisterHandler(mData->mHandler->id());
+ }
+ mData->mLooper->stop();
+ mData->mLooper.clear();
+ }
+ delete mData;
}
- delete mData;
return AMEDIA_OK;
}
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index fee2347..debcdf9 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -29,12 +29,6 @@
include $(CLEAR_VARS)
-# Clang++ aborts on AudioMixer.cpp,
-# b/18373866, "do not know how to split this operator."
-ifeq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH))
- LOCAL_CLANG := false
-endif
-
LOCAL_SRC_FILES:= \
AudioFlinger.cpp \
Threads.cpp \
@@ -44,12 +38,13 @@
SpdifStreamOut.cpp \
Effects.cpp \
AudioMixer.cpp.arm \
- PatchPanel.cpp
-
-LOCAL_SRC_FILES += StateQueue.cpp
+ BufferProviders.cpp \
+ PatchPanel.cpp \
+ StateQueue.cpp
LOCAL_C_INCLUDES := \
$(TOPDIR)frameworks/av/services/audiopolicy \
+ $(TOPDIR)external/sonic \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
@@ -68,7 +63,8 @@
libhardware_legacy \
libeffects \
libpowermanager \
- libserviceutility
+ libserviceutility \
+ libsonic
LOCAL_STATIC_LIBRARIES := \
libscheduling_policy \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f3206cb..93b1642 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -45,6 +45,8 @@
#include "AudioFlinger.h"
#include "ServiceUtilities.h"
+#include <media/AudioResamplerPublic.h>
+
#include <media/EffectsFactoryApi.h>
#include <audio_effects/effect_visualizer.h>
#include <audio_effects/effect_ns.h>
@@ -755,8 +757,12 @@
// assigned to HALs which do not have master volume support will apply
// master volume during the mix operation. Threads with HALs which do
// support master volume will simply ignore the setting.
- for (size_t i = 0; i < mPlaybackThreads.size(); i++)
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->isDuplicating()) {
+ continue;
+ }
mPlaybackThreads.valueAt(i)->setMasterVolume(value);
+ }
return NO_ERROR;
}
@@ -873,8 +879,12 @@
// assigned to HALs which do not have master mute support will apply master
// mute during the mix operation. Threads with HALs which do support master
// mute will simply ignore the setting.
- for (size_t i = 0; i < mPlaybackThreads.size(); i++)
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->isDuplicating()) {
+ continue;
+ }
mPlaybackThreads.valueAt(i)->setMasterMute(muted);
+ }
return NO_ERROR;
}
@@ -1011,6 +1021,14 @@
return streamMute_l(stream);
}
+
+void AudioFlinger::broacastParametersToRecordThreads_l(const String8& keyValuePairs)
+{
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ mRecordThreads.valueAt(i)->setParameters(keyValuePairs);
+ }
+}
+
status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
{
ALOGV("setParameters(): io %d, keyvalue %s, calling pid %d",
@@ -1085,9 +1103,7 @@
int value;
if ((param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) &&
(value != 0)) {
- for (size_t i = 0; i < mRecordThreads.size(); i++) {
- mRecordThreads.valueAt(i)->setParameters(keyValuePairs);
- }
+ broacastParametersToRecordThreads_l(keyValuePairs);
}
}
}
@@ -1140,19 +1156,46 @@
if (ret != NO_ERROR) {
return 0;
}
+ if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
+ return 0;
+ }
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
- audio_config_t config;
- memset(&config, 0, sizeof(config));
- config.sample_rate = sampleRate;
- config.channel_mask = channelMask;
- config.format = format;
+ audio_config_t config, proposed;
+ memset(&proposed, 0, sizeof(proposed));
+ proposed.sample_rate = sampleRate;
+ proposed.channel_mask = channelMask;
+ proposed.format = format;
audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
- size_t size = dev->get_input_buffer_size(dev, &config);
+ size_t frames;
+ for (;;) {
+ // Note: config is currently a const parameter for get_input_buffer_size()
+ // but we use a copy from proposed in case config changes from the call.
+ config = proposed;
+ frames = dev->get_input_buffer_size(dev, &config);
+ if (frames != 0) {
+ break; // hal success, config is the result
+ }
+ // change one parameter of the configuration each iteration to a more "common" value
+ // to see if the device will support it.
+ if (proposed.format != AUDIO_FORMAT_PCM_16_BIT) {
+ proposed.format = AUDIO_FORMAT_PCM_16_BIT;
+ } else if (proposed.sample_rate != 44100) { // 44.1 is claimed as must in CDD as well as
+ proposed.sample_rate = 44100; // legacy AudioRecord.java. TODO: Query hw?
+ } else {
+ ALOGW("getInputBufferSize failed with minimum buffer size sampleRate %u, "
+ "format %#x, channelMask 0x%X",
+ sampleRate, format, channelMask);
+ break; // retries failed, break out of loop with frames == 0.
+ }
+ }
mHardwareStatus = AUDIO_HW_IDLE;
- return size;
+ if (frames > 0 && config.sample_rate != sampleRate) {
+ frames = destinationFramesPossible(frames, sampleRate, config.sample_rate);
+ }
+ return frames; // may be converted to bytes at the Java level.
}
uint32_t AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
@@ -1233,11 +1276,11 @@
// the config change is always sent from playback or record threads to avoid deadlock
// with AudioSystem::gLock
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::OUTPUT_OPENED);
+ mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_OPENED);
}
for (size_t i = 0; i < mRecordThreads.size(); i++) {
- mRecordThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::INPUT_OPENED);
+ mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_OPENED);
}
}
}
@@ -1271,14 +1314,13 @@
}
}
-void AudioFlinger::audioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2)
+void AudioFlinger::ioConfigChanged(audio_io_config_event event,
+ const sp<AudioIoDescriptor>& ioDesc)
{
Mutex::Autolock _l(mClientLock);
size_t size = mNotificationClients.size();
for (size_t i = 0; i < size; i++) {
- mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event,
- ioHandle,
- param2);
+ mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc);
}
}
@@ -1387,9 +1429,11 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ const String16& opPackageName,
size_t *frameCount,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int clientUid,
int *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
@@ -1406,7 +1450,7 @@
buffers.clear();
// check calling permissions
- if (!recordingAllowed()) {
+ if (!recordingAllowed(opPackageName)) {
ALOGE("openRecord() permission denied: recording not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
@@ -1419,9 +1463,8 @@
goto Exit;
}
- // we don't yet support anything other than 16-bit PCM
- if (!(audio_is_valid_format(format) &&
- audio_is_linear_pcm(format) && format == AUDIO_FORMAT_PCM_16_BIT)) {
+ // we don't yet support anything other than linear PCM
+ if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
ALOGE("openRecord() invalid format %#x", format);
lStatus = BAD_VALUE;
goto Exit;
@@ -1460,8 +1503,7 @@
// TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
frameCount, lSessionId, notificationFrames,
- IPCThreadState::self()->getCallingUid(),
- flags, tid, &lStatus);
+ clientUid, flags, tid, &lStatus);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
if (lStatus == NO_ERROR) {
@@ -1797,7 +1839,7 @@
*latencyMs = thread->latency();
// notify client processes of the new output creation
- thread->audioConfigChanged(AudioSystem::OUTPUT_OPENED);
+ thread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
// the first primary output opened designates the primary hw device
if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
@@ -1835,7 +1877,7 @@
thread->addOutputTrack(thread2);
mPlaybackThreads.add(id, thread);
// notify client processes of the new output creation
- thread->audioConfigChanged(AudioSystem::OUTPUT_OPENED);
+ thread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
return id;
}
@@ -1860,11 +1902,10 @@
if (thread->type() == ThreadBase::MIXER) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- if (mPlaybackThreads.valueAt(i)->type() == ThreadBase::DUPLICATING) {
+ if (mPlaybackThreads.valueAt(i)->isDuplicating()) {
DuplicatingThread *dupThread =
(DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
dupThread->removeOutputTrack((MixerThread *)thread.get());
-
}
}
}
@@ -1885,13 +1926,15 @@
}
}
}
- audioConfigChanged(AudioSystem::OUTPUT_CLOSED, output, NULL);
+ const sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
+ ioDesc->mIoHandle = output;
+ ioConfigChanged(AUDIO_OUTPUT_CLOSED, ioDesc);
}
thread->exit();
// The thread entity (active unit of execution) is no longer running here,
// but the ThreadBase container still exists.
- if (thread->type() != ThreadBase::DUPLICATING) {
+ if (!thread->isDuplicating()) {
closeOutputFinish(thread);
}
@@ -1963,7 +2006,7 @@
if (thread != 0) {
// notify client processes of the new input creation
- thread->audioConfigChanged(AudioSystem::INPUT_OPENED);
+ thread->ioConfigChanged(AUDIO_INPUT_OPENED);
return NO_ERROR;
}
return NO_INIT;
@@ -2002,11 +2045,11 @@
status, address.string());
// If the input could not be opened with the requested parameters and we can handle the
- // conversion internally, try to open again with the proposed parameters. The AudioFlinger can
- // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs.
+ // conversion internally, try to open again with the proposed parameters.
if (status == BAD_VALUE &&
- config->format == halconfig.format && halconfig.format == AUDIO_FORMAT_PCM_16_BIT &&
- (halconfig.sample_rate <= 2 * config->sample_rate) &&
+ audio_is_linear_pcm(config->format) &&
+ audio_is_linear_pcm(halconfig.format) &&
+ (halconfig.sample_rate <= AUDIO_RESAMPLER_DOWN_RATIO_MAX * config->sample_rate) &&
(audio_channel_count_from_in_mask(halconfig.channel_mask) <= FCC_2) &&
(audio_channel_count_from_in_mask(config->channel_mask) <= FCC_2)) {
// FIXME describe the change proposed by HAL (save old values so we can log them here)
@@ -2146,7 +2189,9 @@
putOrphanEffectChain_l(chain);
}
}
- audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
+ const sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
+ ioDesc->mIoHandle = input;
+ ioConfigChanged(AUDIO_INPUT_CLOSED, ioDesc);
mRecordThreads.removeItem(input);
}
// FIXME: calling thread->exit() without mLock held should not be needed anymore now that
@@ -2337,6 +2382,9 @@
{
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ if(thread->isDuplicating()) {
+ continue;
+ }
AudioStreamOut *output = thread->getOutput();
if (output != NULL && output->audioHwDev == mPrimaryHardwareDev) {
return thread;
@@ -2419,6 +2467,7 @@
int32_t priority,
audio_io_handle_t io,
int sessionId,
+ const String16& opPackageName,
status_t *status,
int *id,
int *enabled)
@@ -2515,7 +2564,7 @@
// check recording permission for visualizer
if ((memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) &&
- !recordingAllowed()) {
+ !recordingAllowed(opPackageName)) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
@@ -2650,7 +2699,7 @@
// Check whether the destination thread has a channel count of FCC_2, which is
// currently required for (most) effects. Prevent moving the effect chain here rather
// than disabling the addEffect_l() call in dstThread below.
- if ((dstThread->type() == ThreadBase::MIXER || dstThread->type() == ThreadBase::DUPLICATING) &&
+ if ((dstThread->type() == ThreadBase::MIXER || dstThread->isDuplicating()) &&
dstThread->mChannelCount != FCC_2) {
ALOGW("moveEffectChain_l() effect chain failed because"
" destination thread %p channel count(%u) != %u",
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c7d9161..51b2610 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -73,18 +73,18 @@
class AudioBuffer;
class AudioResampler;
class FastMixer;
+class PassthruBufferProvider;
class ServerProxy;
// ----------------------------------------------------------------------------
-// AudioFlinger has a hard-coded upper limit of 2 channels for capture and playback.
-// There is support for > 2 channel tracks down-mixed to 2 channel output via a down-mix effect.
-// Adding full support for > 2 channel capture or playback would require more than simply changing
-// this #define. There is an independent hard-coded upper limit in AudioMixer;
-// removing that AudioMixer limit would be necessary but insufficient to support > 2 channels.
-// The macro FCC_2 highlights some (but not all) places where there is are 2-channel assumptions.
+// The macro FCC_2 highlights some (but not all) places where there are are 2-channel assumptions.
+// This is typically due to legacy implementation of stereo input or output.
// Search also for "2", "left", "right", "[0]", "[1]", ">> 16", "<< 16", etc.
#define FCC_2 2 // FCC_2 = Fixed Channel Count 2
+// The macro FCC_8 highlights places where there are 8-channel assumptions.
+// This is typically due to audio mixer and resampler limitations.
+#define FCC_8 8 // FCC_8 = Fixed Channel Count 8
static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
@@ -120,9 +120,11 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ const String16& opPackageName,
size_t *pFrameCount,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int clientUid,
int *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
@@ -216,6 +218,7 @@
int32_t priority,
audio_io_handle_t io,
int sessionId,
+ const String16& opPackageName,
status_t *status /*non-NULL*/,
int *id,
int *enabled);
@@ -543,7 +546,8 @@
// no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
float streamVolume_l(audio_stream_type_t stream) const
{ return mStreamTypes[stream].volume; }
- void audioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
+ void ioConfigChanged(audio_io_config_event event,
+ const sp<AudioIoDescriptor>& ioDesc);
// Allocate an audio_io_handle_t, session ID, effect ID, or audio_module_handle_t.
// They all share the same ID space, but the namespaces are actually independent
@@ -588,6 +592,7 @@
// Return true if the effect was found in mOrphanEffectChains, false otherwise.
bool updateOrphanEffectChains(const sp<EffectModule>& effect);
+ void broacastParametersToRecordThreads_l(const String8& keyValuePairs);
// AudioStreamIn is immutable, so their fields are const.
// For emphasis, we could also make all pointers to them be "const *",
diff --git a/services/audioflinger/AudioHwDevice.cpp b/services/audioflinger/AudioHwDevice.cpp
index 09d86ea..3191598 100644
--- a/services/audioflinger/AudioHwDevice.cpp
+++ b/services/audioflinger/AudioHwDevice.cpp
@@ -44,7 +44,7 @@
AudioStreamOut *outputStream = new AudioStreamOut(this, flags);
// Try to open the HAL first using the current format.
- ALOGV("AudioHwDevice::openOutputStream(), try "
+ ALOGV("openOutputStream(), try "
" sampleRate %d, Format %#x, "
"channelMask %#x",
config->sample_rate,
@@ -59,7 +59,7 @@
// FIXME Look at any modification to the config.
// The HAL might modify the config to suggest a wrapped format.
// Log this so we can see what the HALs are doing.
- ALOGI("AudioHwDevice::openOutputStream(), HAL returned"
+ ALOGI("openOutputStream(), HAL returned"
" sampleRate %d, Format %#x, "
"channelMask %#x, status %d",
config->sample_rate,
@@ -72,16 +72,19 @@
&& ((flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0)
&& ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0);
- // FIXME - Add isEncodingSupported() query to SPDIF wrapper then
- // call it from here.
if (wrapperNeeded) {
- outputStream = new SpdifStreamOut(this, flags);
- status = outputStream->open(handle, devices, &originalConfig, address);
- if (status != NO_ERROR) {
- ALOGE("ERROR - AudioHwDevice::openOutputStream(), SPDIF open returned %d",
- status);
- delete outputStream;
- outputStream = NULL;
+ if (SPDIFEncoder::isFormatSupported(originalConfig.format)) {
+ outputStream = new SpdifStreamOut(this, flags, originalConfig.format);
+ status = outputStream->open(handle, devices, &originalConfig, address);
+ if (status != NO_ERROR) {
+ ALOGE("ERROR - openOutputStream(), SPDIF open returned %d",
+ status);
+ delete outputStream;
+ outputStream = NULL;
+ }
+ } else {
+ ALOGE("ERROR - openOutputStream(), SPDIFEncoder does not support format 0x%08x",
+ originalConfig.format);
}
}
}
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index dddca02..586c737 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -39,9 +39,6 @@
#include <common_time/local_clock.h>
#include <common_time/cc_helper.h>
-#include <media/EffectsFactoryApi.h>
-#include <audio_effects/effect_downmix.h>
-
#include "AudioMixerOps.h"
#include "AudioMixer.h"
@@ -91,323 +88,6 @@
return a < b ? a : b;
}
-AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
- size_t outputFrameSize, size_t bufferFrameCount) :
- mInputFrameSize(inputFrameSize),
- mOutputFrameSize(outputFrameSize),
- mLocalBufferFrameCount(bufferFrameCount),
- mLocalBufferData(NULL),
- mConsumed(0)
-{
- ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
- inputFrameSize, outputFrameSize, bufferFrameCount);
- LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
- "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
- inputFrameSize, outputFrameSize);
- if (mLocalBufferFrameCount) {
- (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
- }
- mBuffer.frameCount = 0;
-}
-
-AudioMixer::CopyBufferProvider::~CopyBufferProvider()
-{
- ALOGV("~CopyBufferProvider(%p)", this);
- if (mBuffer.frameCount != 0) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- }
- free(mLocalBufferData);
-}
-
-status_t AudioMixer::CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
- int64_t pts)
-{
- //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
- // this, pBuffer, pBuffer->frameCount, pts);
- if (mLocalBufferFrameCount == 0) {
- status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
- if (res == OK) {
- copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
- }
- return res;
- }
- if (mBuffer.frameCount == 0) {
- mBuffer.frameCount = pBuffer->frameCount;
- status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
- // At one time an upstream buffer provider had
- // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
- //
- // By API spec, if res != OK, then mBuffer.frameCount == 0.
- // but there may be improper implementations.
- ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
- if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
- pBuffer->raw = NULL;
- pBuffer->frameCount = 0;
- return res;
- }
- mConsumed = 0;
- }
- ALOG_ASSERT(mConsumed < mBuffer.frameCount);
- size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
- count = min(count, pBuffer->frameCount);
- pBuffer->raw = mLocalBufferData;
- pBuffer->frameCount = count;
- copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
- pBuffer->frameCount);
- return OK;
-}
-
-void AudioMixer::CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
-{
- //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
- // this, pBuffer, pBuffer->frameCount);
- if (mLocalBufferFrameCount == 0) {
- mTrackBufferProvider->releaseBuffer(pBuffer);
- return;
- }
- // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
- mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
- if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- ALOG_ASSERT(mBuffer.frameCount == 0);
- }
- pBuffer->raw = NULL;
- pBuffer->frameCount = 0;
-}
-
-void AudioMixer::CopyBufferProvider::reset()
-{
- if (mBuffer.frameCount != 0) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- }
- mConsumed = 0;
-}
-
-AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider(
- audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
- CopyBufferProvider(
- audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
- audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
- bufferFrameCount) // set bufferFrameCount to 0 to do in-place
-{
- ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
- this, inputChannelMask, outputChannelMask, format,
- sampleRate, sessionId);
- if (!sIsMultichannelCapable
- || EffectCreate(&sDwnmFxDesc.uuid,
- sessionId,
- SESSION_ID_INVALID_AND_IGNORED,
- &mDownmixHandle) != 0) {
- ALOGE("DownmixerBufferProvider() error creating downmixer effect");
- mDownmixHandle = NULL;
- return;
- }
- // channel input configuration will be overridden per-track
- mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits
- mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
- mDownmixConfig.inputCfg.format = format;
- mDownmixConfig.outputCfg.format = format;
- mDownmixConfig.inputCfg.samplingRate = sampleRate;
- mDownmixConfig.outputCfg.samplingRate = sampleRate;
- mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
- mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
- // input and output buffer provider, and frame count will not be used as the downmix effect
- // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
- mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
- EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
- mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
-
- int cmdStatus;
- uint32_t replySize = sizeof(int);
-
- // Configure downmixer
- status_t status = (*mDownmixHandle)->command(mDownmixHandle,
- EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
- &mDownmixConfig /*pCmdData*/,
- &replySize, &cmdStatus /*pReplyData*/);
- if (status != 0 || cmdStatus != 0) {
- ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
- status, cmdStatus);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
- return;
- }
-
- // Enable downmixer
- replySize = sizeof(int);
- status = (*mDownmixHandle)->command(mDownmixHandle,
- EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
- &replySize, &cmdStatus /*pReplyData*/);
- if (status != 0 || cmdStatus != 0) {
- ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
- status, cmdStatus);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
- return;
- }
-
- // Set downmix type
- // parameter size rounded for padding on 32bit boundary
- const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
- const int downmixParamSize =
- sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
- effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
- param->psize = sizeof(downmix_params_t);
- const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
- memcpy(param->data, &downmixParam, param->psize);
- const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
- param->vsize = sizeof(downmix_type_t);
- memcpy(param->data + psizePadded, &downmixType, param->vsize);
- replySize = sizeof(int);
- status = (*mDownmixHandle)->command(mDownmixHandle,
- EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
- param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
- free(param);
- if (status != 0 || cmdStatus != 0) {
- ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
- status, cmdStatus);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
- return;
- }
- ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
-}
-
-AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider()
-{
- ALOGV("~DownmixerBufferProvider (%p)", this);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
-}
-
-void AudioMixer::DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
- mDownmixConfig.inputCfg.buffer.frameCount = frames;
- mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
- mDownmixConfig.outputCfg.buffer.frameCount = frames;
- mDownmixConfig.outputCfg.buffer.raw = dst;
- // may be in-place if src == dst.
- status_t res = (*mDownmixHandle)->process(mDownmixHandle,
- &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
- ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
-}
-
-/* call once in a pthread_once handler. */
-/*static*/ status_t AudioMixer::DownmixerBufferProvider::init()
-{
- // find multichannel downmix effect if we have to play multichannel content
- uint32_t numEffects = 0;
- int ret = EffectQueryNumberEffects(&numEffects);
- if (ret != 0) {
- ALOGE("AudioMixer() error %d querying number of effects", ret);
- return NO_INIT;
- }
- ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
-
- for (uint32_t i = 0 ; i < numEffects ; i++) {
- if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
- ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
- if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
- ALOGI("found effect \"%s\" from %s",
- sDwnmFxDesc.name, sDwnmFxDesc.implementor);
- sIsMultichannelCapable = true;
- break;
- }
- }
- }
- ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
- return NO_INIT;
-}
-
-/*static*/ bool AudioMixer::DownmixerBufferProvider::sIsMultichannelCapable = false;
-/*static*/ effect_descriptor_t AudioMixer::DownmixerBufferProvider::sDwnmFxDesc;
-
-AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- size_t bufferFrameCount) :
- CopyBufferProvider(
- audio_bytes_per_sample(format)
- * audio_channel_count_from_out_mask(inputChannelMask),
- audio_bytes_per_sample(format)
- * audio_channel_count_from_out_mask(outputChannelMask),
- bufferFrameCount),
- mFormat(format),
- mSampleSize(audio_bytes_per_sample(format)),
- mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
- mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
-{
- ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
- this, format, inputChannelMask, outputChannelMask,
- mInputChannels, mOutputChannels);
-
- const audio_channel_representation_t inputRepresentation =
- audio_channel_mask_get_representation(inputChannelMask);
- const audio_channel_representation_t outputRepresentation =
- audio_channel_mask_get_representation(outputChannelMask);
- const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask);
- const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask);
-
- switch (inputRepresentation) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- switch (outputRepresentation) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry),
- outputBits, inputBits);
- return;
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- // TODO: output channel index mask not currently allowed
- // fall through
- default:
- break;
- }
- break;
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- switch (outputRepresentation) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry),
- outputBits, inputBits);
- return;
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- // TODO: output channel index mask not currently allowed
- // fall through
- default:
- break;
- }
- break;
- default:
- break;
- }
- LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x",
- inputChannelMask, outputChannelMask);
-}
-
-void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
- memcpy_by_index_array(dst, mOutputChannels,
- src, mInputChannels, mIdxAry, mSampleSize, frames);
-}
-
-AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
- audio_format_t inputFormat, audio_format_t outputFormat,
- size_t bufferFrameCount) :
- CopyBufferProvider(
- channels * audio_bytes_per_sample(inputFormat),
- channels * audio_bytes_per_sample(outputFormat),
- bufferFrameCount),
- mChannels(channels),
- mInputFormat(inputFormat),
- mOutputFormat(outputFormat)
-{
- ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
-}
-
-void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
- memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannels);
-}
-
// ----------------------------------------------------------------------------
// Ensure mConfiguredNames bitmask is initialized properly on all architectures.
@@ -442,6 +122,7 @@
t->resampler = NULL;
t->downmixerBufferProvider = NULL;
t->mReformatBufferProvider = NULL;
+ t->mTimestretchBufferProvider = NULL;
t++;
}
@@ -454,6 +135,7 @@
delete t->resampler;
delete t->downmixerBufferProvider;
delete t->mReformatBufferProvider;
+ delete t->mTimestretchBufferProvider;
t++;
}
delete [] mState.outputTemp;
@@ -532,6 +214,7 @@
t->mReformatBufferProvider = NULL;
t->downmixerBufferProvider = NULL;
t->mPostDownmixReformatBufferProvider = NULL;
+ t->mTimestretchBufferProvider = NULL;
t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
t->mFormat = format;
t->mMixerInFormat = selectMixerInFormat(format);
@@ -539,6 +222,7 @@
t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
+ t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// Check the downmixing (or upmixing) requirements.
status_t status = t->prepareForDownmix();
if (status != OK) {
@@ -633,7 +317,7 @@
// discard the previous downmixer if there was one
unprepareForDownmix();
- // Only remix (upmix or downmix) if the track and mixer/device channel masks
+ // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
// are not the same and not handled internally, as mono -> stereo currently is.
if (channelMask == mMixerChannelMask
|| (channelMask == AUDIO_CHANNEL_OUT_MONO
@@ -731,6 +415,10 @@
mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
bufferProvider = mPostDownmixReformatBufferProvider;
}
+ if (mTimestretchBufferProvider) {
+ mTimestretchBufferProvider->setBufferProvider(bufferProvider);
+ bufferProvider = mTimestretchBufferProvider;
+ }
}
void AudioMixer::deleteTrackName(int name)
@@ -751,7 +439,9 @@
mState.tracks[name].unprepareForDownmix();
// delete the reformatter
mState.tracks[name].unprepareForReformat();
-
+ // delete the timestretch provider
+ delete track.mTimestretchBufferProvider;
+ track.mTimestretchBufferProvider = NULL;
mTrackNames &= ~(1<<name);
}
@@ -973,6 +663,32 @@
}
}
break;
+ case TIMESTRETCH:
+ switch (param) {
+ case PLAYBACK_RATE: {
+ const AudioPlaybackRate *playbackRate =
+ reinterpret_cast<AudioPlaybackRate*>(value);
+ ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= playbackRate->mSpeed
+ && playbackRate->mSpeed <= AUDIO_TIMESTRETCH_SPEED_MAX,
+ "bad speed %f", playbackRate->mSpeed);
+ ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= playbackRate->mPitch
+ && playbackRate->mPitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
+ "bad pitch %f", playbackRate->mPitch);
+ //TODO: use function from AudioResamplerPublic.h to test validity.
+ if (track.setPlaybackRate(*playbackRate)) {
+ ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
+ "%f %f %d %d",
+ playbackRate->mSpeed,
+ playbackRate->mPitch,
+ playbackRate->mStretchMode,
+ playbackRate->mFallbackMode);
+ // invalidateState(1 << name);
+ }
+ } break;
+ default:
+ LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
+ }
+ break;
default:
LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
@@ -992,11 +708,10 @@
// FIXME this is flawed for dynamic sample rates, as we choose the resampler
// quality level based on the initial ratio, but that could change later.
// Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
- if (!((trackSampleRate == 44100 && devSampleRate == 48000) ||
- (trackSampleRate == 48000 && devSampleRate == 44100))) {
- quality = AudioResampler::DYN_LOW_QUALITY;
- } else {
+ if (isMusicRate(trackSampleRate)) {
quality = AudioResampler::DEFAULT_QUALITY;
+ } else {
+ quality = AudioResampler::DYN_LOW_QUALITY;
}
// TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
@@ -1018,6 +733,30 @@
return false;
}
+bool AudioMixer::track_t::setPlaybackRate(const AudioPlaybackRate &playbackRate)
+{
+ if ((mTimestretchBufferProvider == NULL &&
+ fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
+ fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) ||
+ isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
+ return false;
+ }
+ mPlaybackRate = playbackRate;
+ if (mTimestretchBufferProvider == NULL) {
+ // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
+ // but if none exists, it is the channel count (1 for mono).
+ const int timestretchChannelCount = downmixerBufferProvider != NULL
+ ? mMixerChannelCount : channelCount;
+ mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
+ mMixerInFormat, sampleRate, playbackRate);
+ reconfigureBufferProviders();
+ } else {
+ reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
+ ->setPlaybackRate(playbackRate);
+ }
+ return true;
+}
+
/* Checks to see if the volume ramp has completed and clears the increment
* variables appropriately.
*
@@ -1035,7 +774,8 @@
{
if (useFloat) {
for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
- if (mVolumeInc[i] != 0 && fabs(mVolume[i] - mPrevVolume[i]) <= fabs(mVolumeInc[i])) {
+ if ((mVolumeInc[i] > 0 && mPrevVolume[i] + mVolumeInc[i] >= mVolume[i]) ||
+ (mVolumeInc[i] < 0 && mPrevVolume[i] + mVolumeInc[i] <= mVolume[i])) {
volumeInc[i] = 0;
prevVolume[i] = volume[i] << 16;
mVolumeInc[i] = 0.;
@@ -1096,6 +836,8 @@
mState.tracks[name].downmixerBufferProvider->reset();
} else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) {
mState.tracks[name].mPostDownmixReformatBufferProvider->reset();
+ } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) {
+ mState.tracks[name].mTimestretchBufferProvider->reset();
}
mState.tracks[name].mInputBufferProvider = bufferProvider;
@@ -1178,7 +920,8 @@
} else {
if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
t.hook = getTrackHook(
- t.mMixerChannelCount == 2 // TODO: MONO_HACK.
+ (t.mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO // TODO: MONO_HACK
+ && t.channelMask == AUDIO_CHANNEL_OUT_MONO)
? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
t.mMixerChannelCount,
t.mMixerInFormat, t.mMixerFormat);
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 381036b..7165c6c 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -23,12 +23,14 @@
#include <hardware/audio_effect.h>
#include <media/AudioBufferProvider.h>
+#include <media/AudioResamplerPublic.h>
#include <media/nbaio/NBLog.h>
#include <system/audio.h>
#include <utils/Compat.h>
#include <utils/threads.h>
#include "AudioResampler.h"
+#include "BufferProviders.h"
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT
@@ -72,6 +74,7 @@
RESAMPLE = 0x3001,
RAMP_VOLUME = 0x3002, // ramp to new volume
VOLUME = 0x3003, // don't ramp
+ TIMESTRETCH = 0x3004,
// set Parameter names
// for target TRACK
@@ -99,6 +102,9 @@
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
AUXLEVEL = 0x4210,
+ // for target TIMESTRETCH
+ PLAYBACK_RATE = 0x4300, // Configure timestretch on this track name;
+ // parameter 'value' is a pointer to the new playback rate.
};
@@ -159,7 +165,6 @@
struct state_t;
struct track_t;
- class CopyBufferProvider;
typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
int32_t* aux);
@@ -214,6 +219,9 @@
/* Buffer providers are constructed to translate the track input data as needed.
*
+ * TODO: perhaps make a single PlaybackConverterProvider class to move
+ * all pre-mixer track buffer conversions outside the AudioMixer class.
+ *
* 1) mInputBufferProvider: The AudioTrack buffer provider.
* 2) mReformatBufferProvider: If not NULL, performs the audio reformat to
* match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer
@@ -223,13 +231,14 @@
* the number of channels required by the mixer sink.
* 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
* the downmixer requirements to the mixer engine input requirements.
+ * 5) mTimestretchBufferProvider: Adds timestretching for playback rate
*/
AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
- CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting.
- CopyBufferProvider* downmixerBufferProvider; // wrapper for channel conversion.
- CopyBufferProvider* mPostDownmixReformatBufferProvider;
+ PassthruBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting.
+ PassthruBufferProvider* downmixerBufferProvider; // wrapper for channel conversion.
+ PassthruBufferProvider* mPostDownmixReformatBufferProvider;
+ PassthruBufferProvider* mTimestretchBufferProvider;
- // 16-byte boundary
int32_t sessionId;
audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
@@ -251,6 +260,8 @@
audio_channel_mask_t mMixerChannelMask;
uint32_t mMixerChannelCount;
+ AudioPlaybackRate mPlaybackRate;
+
bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
bool doesResample() const { return resampler != NULL; }
@@ -263,6 +274,7 @@
void unprepareForDownmix();
status_t prepareForReformat();
void unprepareForReformat();
+ bool setPlaybackRate(const AudioPlaybackRate &playbackRate);
void reconfigureBufferProviders();
};
@@ -282,112 +294,6 @@
track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};
- // Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
- // and ReformatBufferProvider.
- // It handles a private buffer for use in converting format or channel masks from the
- // input data to a form acceptable by the mixer.
- // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
- // processing pipeline.
- class CopyBufferProvider : public AudioBufferProvider {
- public:
- // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
- // If bufferFrameCount is 0, no private buffer is created and in-place modification of
- // the upstream buffer provider's buffers is performed by copyFrames().
- CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
- size_t bufferFrameCount);
- virtual ~CopyBufferProvider();
-
- // Overrides AudioBufferProvider methods
- virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
- virtual void releaseBuffer(Buffer* buffer);
-
- // Other public methods
-
- // call this to release the buffer to the upstream provider.
- // treat it as an audio discontinuity for future samples.
- virtual void reset();
-
- // this function should be supplied by the derived class. It converts
- // #frames in the *src pointer to the *dst pointer. It is public because
- // some providers will allow this to work on arbitrary buffers outside
- // of the internal buffers.
- virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
-
- // set the upstream buffer provider. Consider calling "reset" before this function.
- void setBufferProvider(AudioBufferProvider *p) {
- mTrackBufferProvider = p;
- }
-
- protected:
- AudioBufferProvider* mTrackBufferProvider;
- const size_t mInputFrameSize;
- const size_t mOutputFrameSize;
- private:
- AudioBufferProvider::Buffer mBuffer;
- const size_t mLocalBufferFrameCount;
- void* mLocalBufferData;
- size_t mConsumed;
- };
-
- // DownmixerBufferProvider wraps a track AudioBufferProvider to provide
- // position dependent downmixing by an Audio Effect.
- class DownmixerBufferProvider : public CopyBufferProvider {
- public:
- DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
- virtual ~DownmixerBufferProvider();
- virtual void copyFrames(void *dst, const void *src, size_t frames);
- bool isValid() const { return mDownmixHandle != NULL; }
-
- static status_t init();
- static bool isMultichannelCapable() { return sIsMultichannelCapable; }
-
- protected:
- effect_handle_t mDownmixHandle;
- effect_config_t mDownmixConfig;
-
- // effect descriptor for the downmixer used by the mixer
- static effect_descriptor_t sDwnmFxDesc;
- // indicates whether a downmix effect has been found and is usable by this mixer
- static bool sIsMultichannelCapable;
- // FIXME: should we allow effects outside of the framework?
- // We need to here. A special ioId that must be <= -2 so it does not map to a session.
- static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
- };
-
- // RemixBufferProvider wraps a track AudioBufferProvider to perform an
- // upmix or downmix to the proper channel count and mask.
- class RemixBufferProvider : public CopyBufferProvider {
- public:
- RemixBufferProvider(audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- size_t bufferFrameCount);
- virtual void copyFrames(void *dst, const void *src, size_t frames);
-
- protected:
- const audio_format_t mFormat;
- const size_t mSampleSize;
- const size_t mInputChannels;
- const size_t mOutputChannels;
- int8_t mIdxAry[sizeof(uint32_t)*8]; // 32 bits => channel indices
- };
-
- // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data
- // to an acceptable mixer input format type.
- class ReformatBufferProvider : public CopyBufferProvider {
- public:
- ReformatBufferProvider(int32_t channels,
- audio_format_t inputFormat, audio_format_t outputFormat,
- size_t bufferFrameCount);
- virtual void copyFrames(void *dst, const void *src, size_t frames);
-
- protected:
- const int32_t mChannels;
- const audio_format_t mInputFormat;
- const audio_format_t mOutputFormat;
- };
-
// bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
uint32_t mTrackNames;
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 46e3d6c..e49b7b1 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -41,7 +41,7 @@
AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) :
AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
}
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
// number of bits used in interpolation multiply - 15 bits avoids overflow
@@ -51,9 +51,9 @@
static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
void init() {}
- void resampleMono16(int32_t* out, size_t outFrameCount,
+ size_t resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
- void resampleStereo16(int32_t* out, size_t outFrameCount,
+ size_t resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
@@ -329,7 +329,7 @@
// ----------------------------------------------------------------------------
-void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
// should never happen, but we overflow if it does
@@ -338,15 +338,16 @@
// select the appropriate resampler
switch (mChannelCount) {
case 1:
- resampleMono16(out, outFrameCount, provider);
- break;
+ return resampleMono16(out, outFrameCount, provider);
case 2:
- resampleStereo16(out, outFrameCount, provider);
- break;
+ return resampleStereo16(out, outFrameCount, provider);
+ default:
+ LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+ return 0;
}
}
-void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -442,9 +443,10 @@
// save state
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex / 2 /* channels for stereo */;
}
-void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -538,6 +540,7 @@
// save state
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex;
}
#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 863614a..a8e3e6f 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -67,12 +67,18 @@
// Resample int16_t samples from provider and accumulate into 'out'.
// A mono provider delivers a sequence of samples.
// A stereo provider delivers a sequence of interleaved pairs of samples.
- // Multi-channel providers are not supported.
+ //
// In either case, 'out' holds interleaved pairs of fixed-point Q4.27.
// That is, for a mono provider, there is an implicit up-channeling.
// Since this method accumulates, the caller is responsible for clearing 'out' initially.
- // FIXME assumes provider is always successful; it should return the actual frame count.
- virtual void resample(int32_t* out, size_t outFrameCount,
+ //
+ // For a float resampler, 'out' holds interleaved pairs of float samples.
+ //
+ // Multichannel interleaved frames for n > 2 is supported for quality DYN_LOW_QUALITY,
+ // DYN_MED_QUALITY, and DYN_HIGH_QUALITY.
+ //
+ // Returns the number of frames resampled into the out buffer.
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) = 0;
virtual void reset();
diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
index d3cbd1c..172c2a5 100644
--- a/services/audioflinger/AudioResamplerCubic.cpp
+++ b/services/audioflinger/AudioResamplerCubic.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AudioSRC"
+#define LOG_TAG "AudioResamplerCubic"
#include <stdint.h>
#include <string.h>
@@ -32,7 +32,7 @@
memset(&right, 0, sizeof(state));
}
-void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
// should never happen, but we overflow if it does
@@ -41,15 +41,16 @@
// select the appropriate resampler
switch (mChannelCount) {
case 1:
- resampleMono16(out, outFrameCount, provider);
- break;
+ return resampleMono16(out, outFrameCount, provider);
case 2:
- resampleStereo16(out, outFrameCount, provider);
- break;
+ return resampleStereo16(out, outFrameCount, provider);
+ default:
+ LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+ return 0;
}
}
-void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -67,7 +68,7 @@
mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer, mPTS);
if (mBuffer.raw == NULL) {
- return;
+ return 0;
}
// ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount);
}
@@ -115,9 +116,10 @@
// ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex / 2 /* channels for stereo */;
}
-void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -135,7 +137,7 @@
mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer, mPTS);
if (mBuffer.raw == NULL) {
- return;
+ return 0;
}
// ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount);
}
@@ -182,6 +184,7 @@
// ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex;
}
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h
index 1ddc5f9..4b45b0b 100644
--- a/services/audioflinger/AudioResamplerCubic.h
+++ b/services/audioflinger/AudioResamplerCubic.h
@@ -31,7 +31,7 @@
AudioResamplerCubic(int inChannelCount, int32_t sampleRate) :
AudioResampler(inChannelCount, sampleRate, MED_QUALITY) {
}
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
// number of bits used in interpolation multiply - 14 bits avoids overflow
@@ -43,9 +43,9 @@
int32_t a, b, c, y0, y1, y2, y3;
} state;
void init();
- void resampleMono16(int32_t* out, size_t outFrameCount,
+ size_t resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
- void resampleStereo16(int32_t* out, size_t outFrameCount,
+ size_t resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
static inline int32_t interp(state* p, int32_t x) {
return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1;
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index c21d4ca..6481b85 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -477,15 +477,15 @@
}
template<typename TC, typename TI, typename TO>
-void AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
- (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
+ return (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
}
template<typename TC, typename TI, typename TO>
template<int CHANNELS, bool LOCKED, int STRIDE>
-void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
+size_t AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
// TODO Mono -> Mono is not supported. OUTPUT_CHANNELS reflects minimum of stereo out.
@@ -610,6 +610,7 @@
ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer
mInBuffer.setImpulse(impulse);
mPhaseFraction = phaseFraction;
+ return outputIndex / OUTPUT_CHANNELS;
}
/* instantiate templates used by AudioResampler::create */
diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h
index 238b163..3b1c381 100644
--- a/services/audioflinger/AudioResamplerDyn.h
+++ b/services/audioflinger/AudioResamplerDyn.h
@@ -52,7 +52,7 @@
virtual void setVolume(float left, float right);
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
@@ -111,10 +111,10 @@
int inSampleRate, int outSampleRate, double tbwCheat);
template<int CHANNELS, bool LOCKED, int STRIDE>
- void resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
+ size_t resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
// define a pointer to member function type for resample
- typedef void (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
+ typedef size_t (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
size_t outFrameCount, AudioBufferProvider* provider);
// data - the contiguous storage and layout of these is important.
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index ba9a356..41730ee 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -256,7 +256,7 @@
mVolumeSIMD[1] = u4_28_from_float(clampFloatVol(right));
}
-void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
// FIXME store current state (up or down sample) and only load the coefs when the state
@@ -272,17 +272,18 @@
// select the appropriate resampler
switch (mChannelCount) {
case 1:
- resample<1>(out, outFrameCount, provider);
- break;
+ return resample<1>(out, outFrameCount, provider);
case 2:
- resample<2>(out, outFrameCount, provider);
- break;
+ return resample<2>(out, outFrameCount, provider);
+ default:
+ LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+ return 0;
}
}
template<int CHANNELS>
-void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
const Constants& c(*mConstants);
@@ -357,6 +358,7 @@
mImpulse = impulse;
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex / CHANNELS;
}
template<int CHANNELS>
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index 6d8e85d..0fbeac8 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -39,7 +39,7 @@
virtual ~AudioResamplerSinc();
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
void init();
@@ -47,7 +47,7 @@
virtual void setVolume(float left, float right);
template<int CHANNELS>
- void resample(int32_t* out, size_t outFrameCount,
+ size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
template<int CHANNELS>
diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp
new file mode 100644
index 0000000..8a580e8
--- /dev/null
+++ b/services/audioflinger/BufferProviders.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferProvider"
+//#define LOG_NDEBUG 0
+
+#include <audio_effects/effect_downmix.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
+#include <media/AudioResamplerPublic.h>
+#include <media/EffectsFactoryApi.h>
+
+#include <utils/Log.h>
+
+#include "Configuration.h"
+#include "BufferProviders.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+template <typename T>
+static inline T min(const T& a, const T& b)
+{
+ return a < b ? a : b;
+}
+
+CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
+ size_t outputFrameSize, size_t bufferFrameCount) :
+ mInputFrameSize(inputFrameSize),
+ mOutputFrameSize(outputFrameSize),
+ mLocalBufferFrameCount(bufferFrameCount),
+ mLocalBufferData(NULL),
+ mConsumed(0)
+{
+ ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
+ inputFrameSize, outputFrameSize, bufferFrameCount);
+ LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
+ "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
+ inputFrameSize, outputFrameSize);
+ if (mLocalBufferFrameCount) {
+ (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
+ }
+ mBuffer.frameCount = 0;
+}
+
+CopyBufferProvider::~CopyBufferProvider()
+{
+ ALOGV("~CopyBufferProvider(%p)", this);
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ free(mLocalBufferData);
+}
+
+status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+ int64_t pts)
+{
+ //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+ // this, pBuffer, pBuffer->frameCount, pts);
+ if (mLocalBufferFrameCount == 0) {
+ status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+ if (res == OK) {
+ copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
+ }
+ return res;
+ }
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = pBuffer->frameCount;
+ status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+ // At one time an upstream buffer provider had
+ // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
+ //
+ // By API spec, if res != OK, then mBuffer.frameCount == 0.
+ // but there may be improper implementations.
+ ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+ if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+ return res;
+ }
+ mConsumed = 0;
+ }
+ ALOG_ASSERT(mConsumed < mBuffer.frameCount);
+ size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
+ count = min(count, pBuffer->frameCount);
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = count;
+ copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
+ pBuffer->frameCount);
+ return OK;
+}
+
+void CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+ //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
+ // this, pBuffer, pBuffer->frameCount);
+ if (mLocalBufferFrameCount == 0) {
+ mTrackBufferProvider->releaseBuffer(pBuffer);
+ return;
+ }
+ // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+ mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
+ if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ ALOG_ASSERT(mBuffer.frameCount == 0);
+ }
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+}
+
+void CopyBufferProvider::reset()
+{
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ mConsumed = 0;
+}
+
+DownmixerBufferProvider::DownmixerBufferProvider(
+ audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
+ CopyBufferProvider(
+ audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
+ audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
+ bufferFrameCount) // set bufferFrameCount to 0 to do in-place
+{
+ ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
+ this, inputChannelMask, outputChannelMask, format,
+ sampleRate, sessionId);
+ if (!sIsMultichannelCapable
+ || EffectCreate(&sDwnmFxDesc.uuid,
+ sessionId,
+ SESSION_ID_INVALID_AND_IGNORED,
+ &mDownmixHandle) != 0) {
+ ALOGE("DownmixerBufferProvider() error creating downmixer effect");
+ mDownmixHandle = NULL;
+ return;
+ }
+ // channel input configuration will be overridden per-track
+ mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits
+ mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
+ mDownmixConfig.inputCfg.format = format;
+ mDownmixConfig.outputCfg.format = format;
+ mDownmixConfig.inputCfg.samplingRate = sampleRate;
+ mDownmixConfig.outputCfg.samplingRate = sampleRate;
+ mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+ // input and output buffer provider, and frame count will not be used as the downmix effect
+ // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
+ mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
+ EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
+ mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
+
+ int cmdStatus;
+ uint32_t replySize = sizeof(int);
+
+ // Configure downmixer
+ status_t status = (*mDownmixHandle)->command(mDownmixHandle,
+ EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
+ &mDownmixConfig /*pCmdData*/,
+ &replySize, &cmdStatus /*pReplyData*/);
+ if (status != 0 || cmdStatus != 0) {
+ ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
+ status, cmdStatus);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+ return;
+ }
+
+ // Enable downmixer
+ replySize = sizeof(int);
+ status = (*mDownmixHandle)->command(mDownmixHandle,
+ EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
+ &replySize, &cmdStatus /*pReplyData*/);
+ if (status != 0 || cmdStatus != 0) {
+ ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
+ status, cmdStatus);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+ return;
+ }
+
+ // Set downmix type
+ // parameter size rounded for padding on 32bit boundary
+ const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
+ const int downmixParamSize =
+ sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
+ effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
+ param->psize = sizeof(downmix_params_t);
+ const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
+ memcpy(param->data, &downmixParam, param->psize);
+ const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
+ param->vsize = sizeof(downmix_type_t);
+ memcpy(param->data + psizePadded, &downmixType, param->vsize);
+ replySize = sizeof(int);
+ status = (*mDownmixHandle)->command(mDownmixHandle,
+ EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
+ param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
+ free(param);
+ if (status != 0 || cmdStatus != 0) {
+ ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
+ status, cmdStatus);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+ return;
+ }
+ ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
+}
+
+DownmixerBufferProvider::~DownmixerBufferProvider()
+{
+ ALOGV("~DownmixerBufferProvider (%p)", this);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+}
+
+void DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+ mDownmixConfig.inputCfg.buffer.frameCount = frames;
+ mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
+ mDownmixConfig.outputCfg.buffer.frameCount = frames;
+ mDownmixConfig.outputCfg.buffer.raw = dst;
+ // may be in-place if src == dst.
+ status_t res = (*mDownmixHandle)->process(mDownmixHandle,
+ &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
+ ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
+}
+
+/* call once in a pthread_once handler. */
+/*static*/ status_t DownmixerBufferProvider::init()
+{
+ // find multichannel downmix effect if we have to play multichannel content
+ uint32_t numEffects = 0;
+ int ret = EffectQueryNumberEffects(&numEffects);
+ if (ret != 0) {
+ ALOGE("AudioMixer() error %d querying number of effects", ret);
+ return NO_INIT;
+ }
+ ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
+
+ for (uint32_t i = 0 ; i < numEffects ; i++) {
+ if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
+ ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
+ if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
+ ALOGI("found effect \"%s\" from %s",
+ sDwnmFxDesc.name, sDwnmFxDesc.implementor);
+ sIsMultichannelCapable = true;
+ break;
+ }
+ }
+ }
+ ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
+ return NO_INIT;
+}
+
+/*static*/ bool DownmixerBufferProvider::sIsMultichannelCapable = false;
+/*static*/ effect_descriptor_t DownmixerBufferProvider::sDwnmFxDesc;
+
+RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ size_t bufferFrameCount) :
+ CopyBufferProvider(
+ audio_bytes_per_sample(format)
+ * audio_channel_count_from_out_mask(inputChannelMask),
+ audio_bytes_per_sample(format)
+ * audio_channel_count_from_out_mask(outputChannelMask),
+ bufferFrameCount),
+ mFormat(format),
+ mSampleSize(audio_bytes_per_sample(format)),
+ mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
+ mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
+{
+ ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
+ this, format, inputChannelMask, outputChannelMask,
+ mInputChannels, mOutputChannels);
+ (void) memcpy_by_index_array_initialization_from_channel_mask(
+ mIdxAry, ARRAY_SIZE(mIdxAry), outputChannelMask, inputChannelMask);
+}
+
+void RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+ memcpy_by_index_array(dst, mOutputChannels,
+ src, mInputChannels, mIdxAry, mSampleSize, frames);
+}
+
+ReformatBufferProvider::ReformatBufferProvider(int32_t channelCount,
+ audio_format_t inputFormat, audio_format_t outputFormat,
+ size_t bufferFrameCount) :
+ CopyBufferProvider(
+ channelCount * audio_bytes_per_sample(inputFormat),
+ channelCount * audio_bytes_per_sample(outputFormat),
+ bufferFrameCount),
+ mChannelCount(channelCount),
+ mInputFormat(inputFormat),
+ mOutputFormat(outputFormat)
+{
+ ALOGV("ReformatBufferProvider(%p)(%u, %#x, %#x)",
+ this, channelCount, inputFormat, outputFormat);
+}
+
+void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+ memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
+}
+
+TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount,
+ audio_format_t format, uint32_t sampleRate, const AudioPlaybackRate &playbackRate) :
+ mChannelCount(channelCount),
+ mFormat(format),
+ mSampleRate(sampleRate),
+ mFrameSize(channelCount * audio_bytes_per_sample(format)),
+ mLocalBufferFrameCount(0),
+ mLocalBufferData(NULL),
+ mRemaining(0),
+ mSonicStream(sonicCreateStream(sampleRate, mChannelCount)),
+ mFallbackFailErrorShown(false)
+{
+ LOG_ALWAYS_FATAL_IF(mSonicStream == NULL,
+ "TimestretchBufferProvider can't allocate Sonic stream");
+
+ setPlaybackRate(playbackRate);
+ ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f %d %d)",
+ this, channelCount, format, sampleRate, playbackRate.mSpeed,
+ playbackRate.mPitch, playbackRate.mStretchMode, playbackRate.mFallbackMode);
+ mBuffer.frameCount = 0;
+}
+
+TimestretchBufferProvider::~TimestretchBufferProvider()
+{
+ ALOGV("~TimestretchBufferProvider(%p)", this);
+ sonicDestroyStream(mSonicStream);
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ free(mLocalBufferData);
+}
+
+status_t TimestretchBufferProvider::getNextBuffer(
+ AudioBufferProvider::Buffer *pBuffer, int64_t pts)
+{
+ ALOGV("TimestretchBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+ this, pBuffer, pBuffer->frameCount, pts);
+
+ // BYPASS
+ //return mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+
+ // check if previously processed data is sufficient.
+ if (pBuffer->frameCount <= mRemaining) {
+ ALOGV("previous sufficient");
+ pBuffer->raw = mLocalBufferData;
+ return OK;
+ }
+
+ // do we need to resize our buffer?
+ if (pBuffer->frameCount > mLocalBufferFrameCount) {
+ void *newmem;
+ if (posix_memalign(&newmem, 32, pBuffer->frameCount * mFrameSize) == OK) {
+ if (mRemaining != 0) {
+ memcpy(newmem, mLocalBufferData, mRemaining * mFrameSize);
+ }
+ free(mLocalBufferData);
+ mLocalBufferData = newmem;
+ mLocalBufferFrameCount = pBuffer->frameCount;
+ }
+ }
+
+ // need to fetch more data
+ const size_t outputDesired = pBuffer->frameCount - mRemaining;
+ mBuffer.frameCount = mPlaybackRate.mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
+ ? outputDesired : outputDesired * mPlaybackRate.mSpeed + 1;
+
+ status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+
+ ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+ if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+ ALOGD("buffer error");
+ if (mRemaining == 0) {
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+ return res;
+ } else { // return partial count
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = mRemaining;
+ return OK;
+ }
+ }
+
+ // time-stretch the data
+ size_t dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired);
+ size_t srcAvailable = mBuffer.frameCount;
+ processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable,
+ mBuffer.raw, &srcAvailable);
+
+ // release all data consumed
+ mBuffer.frameCount = srcAvailable;
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+
+ // update buffer vars with the actual data processed and return with buffer
+ mRemaining += dstAvailable;
+
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = mRemaining;
+
+ return OK;
+}
+
+void TimestretchBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+ ALOGV("TimestretchBufferProvider(%p)::releaseBuffer(%p (%zu))",
+ this, pBuffer, pBuffer->frameCount);
+
+ // BYPASS
+ //return mTrackBufferProvider->releaseBuffer(pBuffer);
+
+ // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+ if (pBuffer->frameCount < mRemaining) {
+ memcpy(mLocalBufferData,
+ (uint8_t*)mLocalBufferData + pBuffer->frameCount * mFrameSize,
+ (mRemaining - pBuffer->frameCount) * mFrameSize);
+ mRemaining -= pBuffer->frameCount;
+ } else if (pBuffer->frameCount == mRemaining) {
+ mRemaining = 0;
+ } else {
+ LOG_ALWAYS_FATAL("Releasing more frames(%zu) than available(%zu)",
+ pBuffer->frameCount, mRemaining);
+ }
+
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+}
+
+void TimestretchBufferProvider::reset()
+{
+ mRemaining = 0;
+}
+
+status_t TimestretchBufferProvider::setPlaybackRate(const AudioPlaybackRate &playbackRate)
+{
+ mPlaybackRate = playbackRate;
+ mFallbackFailErrorShown = false;
+ sonicSetSpeed(mSonicStream, mPlaybackRate.mSpeed);
+ //TODO: pitch is ignored for now
+ //TODO: optimize: if parameters are the same, don't do any extra computation.
+ return OK;
+}
+
+void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames,
+ const void *srcBuffer, size_t *srcFrames)
+{
+ ALOGV("processFrames(%zu %zu) remaining(%zu)", *dstFrames, *srcFrames, mRemaining);
+ // Note dstFrames is the required number of frames.
+
+ // Ensure consumption from src is as expected.
+ //TODO: add logic to track "very accurate" consumption related to speed, original sampling
+ //rate, actual frames processed.
+ const size_t targetSrc = *dstFrames * mPlaybackRate.mSpeed;
+ if (*srcFrames < targetSrc) { // limit dst frames to that possible
+ *dstFrames = *srcFrames / mPlaybackRate.mSpeed;
+ } else if (*srcFrames > targetSrc + 1) {
+ *srcFrames = targetSrc + 1;
+ }
+
+ if (mPlaybackRate.mSpeed< TIMESTRETCH_SONIC_SPEED_MIN ||
+ mPlaybackRate.mSpeed > TIMESTRETCH_SONIC_SPEED_MAX ) {
+ //fallback mode
+ if (*dstFrames > 0) {
+ switch(mPlaybackRate.mFallbackMode) {
+ case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
+ if (*dstFrames <= *srcFrames) {
+ size_t copySize = mFrameSize * *dstFrames;
+ memcpy(dstBuffer, srcBuffer, copySize);
+ } else {
+ // cyclically repeat the source.
+ for (size_t count = 0; count < *dstFrames; count += *srcFrames) {
+ size_t remaining = min(*srcFrames, *dstFrames - count);
+ memcpy((uint8_t*)dstBuffer + mFrameSize * count,
+ srcBuffer, mFrameSize * remaining);
+ }
+ }
+ break;
+ case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
+ case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
+ memset(dstBuffer,0, mFrameSize * *dstFrames);
+ break;
+ case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
+ default:
+ if(!mFallbackFailErrorShown) {
+ ALOGE("invalid parameters in TimestretchBufferProvider fallbackMode:%d",
+ mPlaybackRate.mFallbackMode);
+ mFallbackFailErrorShown = true;
+ }
+ break;
+ }
+ }
+ } else {
+ switch (mFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) {
+ ALOGE("sonicWriteFloatToStream cannot realloc");
+ *srcFrames = 0; // cannot consume all of srcBuffer
+ }
+ *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) {
+ ALOGE("sonicWriteShortToStream cannot realloc");
+ *srcFrames = 0; // cannot consume all of srcBuffer
+ }
+ *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames);
+ break;
+ default:
+ // could also be caught on construction
+ LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat);
+ }
+ }
+}
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/services/audioflinger/BufferProviders.h b/services/audioflinger/BufferProviders.h
new file mode 100644
index 0000000..4970b6c
--- /dev/null
+++ b/services/audioflinger/BufferProviders.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFER_PROVIDERS_H
+#define ANDROID_BUFFER_PROVIDERS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <hardware/audio_effect.h>
+#include <media/AudioBufferProvider.h>
+#include <system/audio.h>
+#include <sonic.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class PassthruBufferProvider : public AudioBufferProvider {
+public:
+ PassthruBufferProvider() : mTrackBufferProvider(NULL) { }
+
+ virtual ~PassthruBufferProvider() { }
+
+ // call this to release the buffer to the upstream provider.
+ // treat it as an audio discontinuity for future samples.
+ virtual void reset() { }
+
+ // set the upstream buffer provider. Consider calling "reset" before this function.
+ virtual void setBufferProvider(AudioBufferProvider *p) {
+ mTrackBufferProvider = p;
+ }
+
+protected:
+ AudioBufferProvider *mTrackBufferProvider;
+};
+
+// Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
+// and ReformatBufferProvider.
+// It handles a private buffer for use in converting format or channel masks from the
+// input data to a form acceptable by the mixer.
+// TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
+// processing pipeline.
+class CopyBufferProvider : public PassthruBufferProvider {
+public:
+ // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
+ // If bufferFrameCount is 0, no private buffer is created and in-place modification of
+ // the upstream buffer provider's buffers is performed by copyFrames().
+ CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
+ size_t bufferFrameCount);
+ virtual ~CopyBufferProvider();
+
+ // Overrides AudioBufferProvider methods
+ virtual status_t getNextBuffer(Buffer *buffer, int64_t pts);
+ virtual void releaseBuffer(Buffer *buffer);
+
+ // Overrides PassthruBufferProvider
+ virtual void reset();
+
+ // this function should be supplied by the derived class. It converts
+ // #frames in the *src pointer to the *dst pointer. It is public because
+ // some providers will allow this to work on arbitrary buffers outside
+ // of the internal buffers.
+ virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
+
+protected:
+ const size_t mInputFrameSize;
+ const size_t mOutputFrameSize;
+private:
+ AudioBufferProvider::Buffer mBuffer;
+ const size_t mLocalBufferFrameCount;
+ void *mLocalBufferData;
+ size_t mConsumed;
+};
+
+// DownmixerBufferProvider derives from CopyBufferProvider to provide
+// position dependent downmixing by an Audio Effect.
+class DownmixerBufferProvider : public CopyBufferProvider {
+public:
+ DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
+ virtual ~DownmixerBufferProvider();
+ //Overrides
+ virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+ bool isValid() const { return mDownmixHandle != NULL; }
+ static status_t init();
+ static bool isMultichannelCapable() { return sIsMultichannelCapable; }
+
+protected:
+ effect_handle_t mDownmixHandle;
+ effect_config_t mDownmixConfig;
+
+ // effect descriptor for the downmixer used by the mixer
+ static effect_descriptor_t sDwnmFxDesc;
+ // indicates whether a downmix effect has been found and is usable by this mixer
+ static bool sIsMultichannelCapable;
+ // FIXME: should we allow effects outside of the framework?
+ // We need to here. A special ioId that must be <= -2 so it does not map to a session.
+ static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
+};
+
+// RemixBufferProvider derives from CopyBufferProvider to perform an
+// upmix or downmix to the proper channel count and mask.
+class RemixBufferProvider : public CopyBufferProvider {
+public:
+ RemixBufferProvider(audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ size_t bufferFrameCount);
+ //Overrides
+ virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+protected:
+ const audio_format_t mFormat;
+ const size_t mSampleSize;
+ const size_t mInputChannels;
+ const size_t mOutputChannels;
+ int8_t mIdxAry[sizeof(uint32_t) * 8]; // 32 bits => channel indices
+};
+
+// ReformatBufferProvider derives from CopyBufferProvider to convert the input data
+// to an acceptable mixer input format type.
+class ReformatBufferProvider : public CopyBufferProvider {
+public:
+ ReformatBufferProvider(int32_t channelCount,
+ audio_format_t inputFormat, audio_format_t outputFormat,
+ size_t bufferFrameCount);
+ virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+protected:
+ const uint32_t mChannelCount;
+ const audio_format_t mInputFormat;
+ const audio_format_t mOutputFormat;
+};
+
+// TimestretchBufferProvider derives from PassthruBufferProvider for time stretching
+class TimestretchBufferProvider : public PassthruBufferProvider {
+public:
+ TimestretchBufferProvider(int32_t channelCount,
+ audio_format_t format, uint32_t sampleRate,
+ const AudioPlaybackRate &playbackRate);
+ virtual ~TimestretchBufferProvider();
+
+ // Overrides AudioBufferProvider methods
+ virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+ virtual void releaseBuffer(Buffer* buffer);
+
+ // Overrides PassthruBufferProvider
+ virtual void reset();
+
+ virtual status_t setPlaybackRate(const AudioPlaybackRate &playbackRate);
+
+ // processes frames
+ // dstBuffer is where to place the data
+ // dstFrames [in/out] is the desired frames (return with actual placed in buffer)
+ // srcBuffer is the source data
+ // srcFrames [in/out] is the available source frames (return with consumed)
+ virtual void processFrames(void *dstBuffer, size_t *dstFrames,
+ const void *srcBuffer, size_t *srcFrames);
+
+protected:
+ const uint32_t mChannelCount;
+ const audio_format_t mFormat;
+ const uint32_t mSampleRate; // const for now (TODO change this)
+ const size_t mFrameSize;
+ AudioPlaybackRate mPlaybackRate;
+
+private:
+ AudioBufferProvider::Buffer mBuffer; // for upstream request
+ size_t mLocalBufferFrameCount; // size of local buffer
+ void *mLocalBufferData; // internally allocated buffer for data returned
+ // to caller
+ size_t mRemaining; // remaining data in local buffer
+ sonicStream mSonicStream; // handle to sonic timestretch object
+ //FIXME: this dependency should be abstracted out
+ bool mFallbackFailErrorShown; // log fallback error only once
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
+
+#endif // ANDROID_BUFFER_PROVIDERS_H
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index 9e7e8a4..79ac12b 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -105,7 +105,7 @@
mFormat = mInputSource->format();
mSampleRate = Format_sampleRate(mFormat);
unsigned channelCount = Format_channelCount(mFormat);
- ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+ ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_8);
}
dumpState->mSampleRate = mSampleRate;
eitherChanged = true;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index efbdcff..9248bba 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -200,26 +200,17 @@
status = BAD_VALUE;
goto exit;
}
- // limit to connections between devices and input streams for HAL before 3.0
- if (patch->sinks[i].ext.mix.hw_module == srcModule &&
- (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
- (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
- ALOGW("createAudioPatch() invalid sink type %d for device source",
- patch->sinks[i].type);
- status = BAD_VALUE;
- goto exit;
- }
}
- if (patch->sinks[0].ext.device.hw_module != srcModule) {
- // limit to device to device connection if not on same hw module
- if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
- ALOGW("createAudioPatch() invalid sink type for cross hw module");
- status = INVALID_OPERATION;
- goto exit;
- }
- // special case num sources == 2 -=> reuse an exiting output mix to connect to the
- // sink
+ // manage patches requiring a software bridge
+ // - Device to device AND
+ // - source HW module != destination HW module OR
+ // - audio HAL version < 3.0
+ // - special patch request with 2 sources (reuse one existing output mix)
+ if ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) &&
+ ((patch->sinks[0].ext.device.hw_module != srcModule) ||
+ (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) ||
+ (patch->num_sources == 2))) {
if (patch->num_sources == 2) {
if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX ||
patch->sinks[0].ext.device.hw_module !=
@@ -283,52 +274,29 @@
goto exit;
}
} else {
- if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
- sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
- patch->sinks[0].ext.mix.handle);
- if (thread == 0) {
- ALOGW("createAudioPatch() bad capture I/O handle %d",
- patch->sinks[0].ext.mix.handle);
- status = BAD_VALUE;
- goto exit;
- }
- status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
- } else {
- audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
- status = hwDevice->create_audio_patch(hwDevice,
- patch->num_sources,
- patch->sources,
- patch->num_sinks,
- patch->sinks,
- &halHandle);
- }
- } else {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
if (thread == 0) {
ALOGW("createAudioPatch() bad capture I/O handle %d",
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
status = BAD_VALUE;
goto exit;
}
- char *address;
- if (strcmp(patch->sources[0].ext.device.address, "") != 0) {
- address = audio_device_address_to_parameter(
- patch->sources[0].ext.device.type,
- patch->sources[0].ext.device.address);
- } else {
- address = (char *)calloc(1, 1);
+ status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+ } else {
+ if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) {
+ status = INVALID_OPERATION;
+ goto exit;
}
- AudioParameter param = AudioParameter(String8(address));
- free(address);
- param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING),
- (int)patch->sources[0].ext.device.type);
- param.addInt(String8(AUDIO_PARAMETER_STREAM_INPUT_SOURCE),
- (int)patch->sinks[0].ext.mix.usecase.source);
- ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
- param.toString().string());
- status = thread->setParameters(param.toString());
+
+ audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+ status = hwDevice->create_audio_patch(hwDevice,
+ patch->num_sources,
+ patch->sources,
+ patch->num_sinks,
+ patch->sinks,
+ &halHandle);
}
}
} break;
@@ -341,6 +309,7 @@
goto exit;
}
// limit to connections between devices and output streams
+ audio_devices_t type = AUDIO_DEVICE_NONE;
for (unsigned int i = 0; i < patch->num_sinks; i++) {
if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
ALOGW("createAudioPatch() invalid sink type %d for mix source",
@@ -353,8 +322,8 @@
status = BAD_VALUE;
goto exit;
}
+ type |= patch->sinks[i].ext.device.type;
}
- AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
sp<ThreadBase> thread =
audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
if (thread == 0) {
@@ -363,28 +332,14 @@
status = BAD_VALUE;
goto exit;
}
- if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
- } else {
- audio_devices_t type = AUDIO_DEVICE_NONE;
- for (unsigned int i = 0; i < patch->num_sinks; i++) {
- type |= patch->sinks[i].ext.device.type;
- }
- char *address;
- if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {
- //FIXME: we only support address on first sink with HAL version < 3.0
- address = audio_device_address_to_parameter(
- patch->sinks[0].ext.device.type,
- patch->sinks[0].ext.device.address);
- } else {
- address = (char *)calloc(1, 1);
- }
- AudioParameter param = AudioParameter(String8(address));
- free(address);
+ if (thread == audioflinger->primaryPlaybackThread_l()) {
+ AudioParameter param = AudioParameter();
param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);
- status = thread->setParameters(param.toString());
+
+ audioflinger->broacastParametersToRecordThreads_l(param.toString());
}
+ status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
} break;
default:
status = BAD_VALUE;
@@ -472,6 +427,7 @@
// this track is given the same buffer as the PatchRecord buffer
patch->mPatchTrack = new PlaybackThread::PatchTrack(
patch->mPlaybackThread.get(),
+ audioPatch->sources[1].ext.mix.usecase.stream,
sampleRate,
outChannelMask,
format,
@@ -578,42 +534,30 @@
break;
}
- if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
- patch->sinks[0].ext.device.hw_module != srcModule) {
+ if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE ||
+ removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
clearPatchConnections(removedPatch);
break;
}
- AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
- if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
- sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
- patch->sinks[0].ext.mix.handle);
- if (thread == 0) {
- ALOGW("releaseAudioPatch() bad capture I/O handle %d",
- patch->sinks[0].ext.mix.handle);
- status = BAD_VALUE;
- break;
- }
- status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
- } else {
- audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
- status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle);
- }
- } else {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
if (thread == 0) {
ALOGW("releaseAudioPatch() bad capture I/O handle %d",
- patch->sinks[0].ext.mix.handle);
+ patch->sinks[0].ext.mix.handle);
status = BAD_VALUE;
break;
}
- AudioParameter param;
- param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
- ALOGV("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
- param.toString().string());
- status = thread->setParameters(param.toString());
+ status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
+ } else {
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+ if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) {
+ status = INVALID_OPERATION;
+ break;
+ }
+ audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+ status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle);
}
} break;
case AUDIO_PORT_TYPE_MIX: {
@@ -632,14 +576,7 @@
status = BAD_VALUE;
break;
}
- AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
- if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
- } else {
- AudioParameter param;
- param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
- status = thread->setParameters(param.toString());
- }
+ status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
} break;
default:
status = BAD_VALUE;
@@ -693,5 +630,4 @@
return NO_ERROR;
}
-
} // namespace android
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 45df6a9..7bc6f0c 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -156,11 +156,6 @@
bool mResumeToStopping; // track was paused in stopping state.
bool mFlushHwPending; // track requests for thread flush
- // for last call to getTimestamp
- bool mPreviousTimestampValid;
- // This is either the first timestamp or one that has passed
- // the check to prevent retrograde motion.
- AudioTimestamp mPreviousTimestamp;
}; // end of Track
class TimedTrack : public Track {
@@ -298,6 +293,7 @@
public:
PatchTrack(PlaybackThread *playbackThread,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 204a9d6..25d6d95 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -34,6 +34,7 @@
IAudioFlinger::track_flags_t flags,
track_type type);
virtual ~RecordTrack();
+ virtual status_t initCheck() const;
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
virtual void stop();
@@ -66,21 +67,6 @@
bool mOverflow; // overflow on most recent attempt to fill client buffer
- // updated by RecordThread::readInputParameters_l()
- AudioResampler *mResampler;
-
- // interleaved stereo pairs of fixed-point Q4.27
- int32_t *mRsmpOutBuffer;
- // current allocated frame count for the above, which may be larger than needed
- size_t mRsmpOutFrameCount;
-
- size_t mRsmpInUnrel; // unreleased frames remaining from
- // most recent getNextBuffer
- // for debug only
-
- // rolling counter that is never cleared
- int32_t mRsmpInFront; // next available frame
-
AudioBufferProvider::Buffer mSink; // references client's buffer sink in shared memory
// sync event triggering actual audio capture. Frames read before this event will
@@ -93,7 +79,10 @@
ssize_t mFramesToDrop;
// used by resampler to find source frames
- ResamplerBufferProvider *mResamplerBufferProvider;
+ ResamplerBufferProvider *mResamplerBufferProvider;
+
+ // used by the record thread to convert frames to proper destination format
+ RecordBufferConverter *mRecordBufferConverter;
};
// playback track, used by PatchPanel
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index fae19a1..33bd416 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -14,61 +14,114 @@
* limitations under the License.
*/
+#include <binder/AppOpsManager.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include "ServiceUtilities.h"
+/* When performing permission checks we do not use permission cache for
+ * runtime permissions (protection level dangerous) as they may change at
+ * runtime. All other permissions (protection level normal and dangerous)
+ * can be cached as they never change. Of course all permission checked
+ * here are platform defined.
+ */
+
namespace android {
// Not valid until initialized by AudioFlinger constructor. It would have to be
// re-initialized if the process containing AudioFlinger service forks (which it doesn't).
pid_t getpid_cached;
-bool recordingAllowed() {
+bool recordingAllowed(const String16& opPackageName) {
+ // Note: We are getting the UID from the calling IPC thread state because all
+ // clients that perform recording create AudioRecord in their own processes
+ // and the system does not create AudioRecord objects on behalf of apps. This
+ // differs from playback where in some situations the system recreates AudioTrack
+ // instances associated with a client's MediaPlayer on behalf of this client.
+ // In the latter case we have to store the client UID and pass in along for
+ // security checks.
+
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sRecordAudio("android.permission.RECORD_AUDIO");
- // don't use PermissionCache; this is not a system permission
- bool ok = checkCallingPermission(sRecordAudio);
- if (!ok) ALOGE("Request requires android.permission.RECORD_AUDIO");
- return ok;
+
+ // IMPORTANT: Don't use PermissionCache - a runtime permission and may change.
+ const bool ok = checkCallingPermission(sRecordAudio);
+ if (!ok) {
+ ALOGE("Request requires android.permission.RECORD_AUDIO");
+ return false;
+ }
+
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ String16 checkedOpPackageName = opPackageName;
+
+ // In some cases the calling code has no access to the package it runs under.
+ // For example, code using the wilhelm framework's OpenSL-ES APIs. In this
+ // case we will get the packages for the calling UID and pick the first one
+ // for attributing the app op. This will work correctly for runtime permissions
+ // as for legacy apps we will toggle the app op for all packages in the UID.
+ // The caveat is that the operation may be attributed to the wrong package and
+ // stats based on app ops may be slightly off.
+ if (checkedOpPackageName.size() <= 0) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("permission"));
+ if (binder == 0) {
+ ALOGE("Cannot get permission service");
+ return false;
+ }
+
+ sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
+ Vector<String16> packages;
+
+ permCtrl->getPackagesForUid(uid, packages);
+
+ if (packages.isEmpty()) {
+ ALOGE("No packages for calling UID");
+ return false;
+ }
+ checkedOpPackageName = packages[0];
+ }
+
+ AppOpsManager appOps;
+ if (appOps.noteOp(AppOpsManager::OP_RECORD_AUDIO, uid, checkedOpPackageName)
+ != AppOpsManager::MODE_ALLOWED) {
+ ALOGE("Request denied by app op OP_RECORD_AUDIO");
+ return false;
+ }
+
+ return true;
}
bool captureAudioOutputAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
- // don't use PermissionCache; this is not a system permission
- bool ok = checkCallingPermission(sCaptureAudioOutput);
+ // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+ bool ok = PermissionCache::checkCallingPermission(sCaptureAudioOutput);
if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
return ok;
}
bool captureHotwordAllowed() {
static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
- bool ok = checkCallingPermission(sCaptureHotwordAllowed);
+ // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+ bool ok = PermissionCache::checkCallingPermission(sCaptureHotwordAllowed);
if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD");
return ok;
}
-bool captureFmTunerAllowed() {
- static const String16 sCaptureFmTunerAllowed("android.permission.ACCESS_FM_RADIO");
- bool ok = checkCallingPermission(sCaptureFmTunerAllowed);
- if (!ok) ALOGE("android.permission.ACCESS_FM_RADIO");
- return ok;
-}
-
bool settingsAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
- // don't use PermissionCache; this is not a system permission
- bool ok = checkCallingPermission(sAudioSettings);
+ // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+ bool ok = PermissionCache::checkCallingPermission(sAudioSettings);
if (!ok) ALOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
return ok;
}
bool modifyAudioRoutingAllowed() {
static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING");
- bool ok = checkCallingPermission(sModifyAudioRoutingAllowed);
+ // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+ bool ok = PermissionCache::checkCallingPermission(sModifyAudioRoutingAllowed);
if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING");
return ok;
}
@@ -76,7 +129,7 @@
bool dumpAllowed() {
// don't optimize for same pid, since mediaserver never dumps itself
static const String16 sDump("android.permission.DUMP");
- // OK to use PermissionCache; this is a system permission
+ // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
bool ok = PermissionCache::checkCallingPermission(sDump);
// convention is for caller to dump an error message to fd instead of logging here
//if (!ok) ALOGE("Request requires android.permission.DUMP");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index ce18a90..fba6dce 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -20,12 +20,10 @@
extern pid_t getpid_cached;
-bool recordingAllowed();
+bool recordingAllowed(const String16& opPackageName);
bool captureAudioOutputAllowed();
bool captureHotwordAllowed();
-bool captureFmTunerAllowed();
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
bool dumpAllowed();
-
}
diff --git a/services/audioflinger/SpdifStreamOut.cpp b/services/audioflinger/SpdifStreamOut.cpp
index d23588e..45b541a 100644
--- a/services/audioflinger/SpdifStreamOut.cpp
+++ b/services/audioflinger/SpdifStreamOut.cpp
@@ -32,10 +32,12 @@
* If the AudioFlinger is processing encoded data and the HAL expects
* PCM then we need to wrap the data in an SPDIF wrapper.
*/
-SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags)
+SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev,
+ audio_output_flags_t flags,
+ audio_format_t format)
: AudioStreamOut(dev,flags)
, mRateMultiplier(1)
- , mSpdifEncoder(this)
+ , mSpdifEncoder(this, format)
, mRenderPositionHal(0)
, mPreviousHalPosition32(0)
{
@@ -49,15 +51,15 @@
{
struct audio_config customConfig = *config;
- customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-
// Some data bursts run at a higher sample rate.
+ // TODO Move this into the audio_utils as a static method.
switch(config->format) {
case AUDIO_FORMAT_E_AC3:
mRateMultiplier = 4;
break;
case AUDIO_FORMAT_AC3:
+ case AUDIO_FORMAT_DTS:
+ case AUDIO_FORMAT_DTS_HD:
mRateMultiplier = 1;
break;
default:
@@ -67,6 +69,9 @@
}
customConfig.sample_rate = config->sample_rate * mRateMultiplier;
+ customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+ customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
// Always print this because otherwise it could be very confusing if the
// HAL and AudioFlinger are using different formats.
// Print before open() because HAL may modify customConfig.
diff --git a/services/audioflinger/SpdifStreamOut.h b/services/audioflinger/SpdifStreamOut.h
index cb82ac7..d81c064 100644
--- a/services/audioflinger/SpdifStreamOut.h
+++ b/services/audioflinger/SpdifStreamOut.h
@@ -38,7 +38,8 @@
class SpdifStreamOut : public AudioStreamOut {
public:
- SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags);
+ SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags,
+ audio_format_t format);
virtual ~SpdifStreamOut() { }
@@ -77,8 +78,9 @@
class MySPDIFEncoder : public SPDIFEncoder
{
public:
- MySPDIFEncoder(SpdifStreamOut *spdifStreamOut)
- : mSpdifStreamOut(spdifStreamOut)
+ MySPDIFEncoder(SpdifStreamOut *spdifStreamOut, audio_format_t format)
+ : SPDIFEncoder(format)
+ , mSpdifStreamOut(spdifStreamOut)
{
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4efb3d7..594ed05 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -56,6 +56,7 @@
#include "AudioFlinger.h"
#include "AudioMixer.h"
+#include "BufferProviders.h"
#include "FastMixer.h"
#include "FastCapture.h"
#include "ServiceUtilities.h"
@@ -86,7 +87,17 @@
#define ALOGVV(a...) do { } while(0)
#endif
+// TODO: Move these macro/inlines to a header file.
#define max(a, b) ((a) > (b) ? (a) : (b))
+template <typename T>
+static inline T min(const T& a, const T& b)
+{
+ return a < b ? a : b;
+}
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
namespace android {
@@ -489,6 +500,7 @@
// mName will be set by concrete (non-virtual) subclass
mDeathRecipient(new PMDeathRecipient(this))
{
+ memset(&mPatch, 0, sizeof(struct audio_patch));
}
AudioFlinger::ThreadBase::~ThreadBase()
@@ -573,16 +585,16 @@
return status;
}
-void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param)
+void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event)
{
Mutex::Autolock _l(mLock);
- sendIoConfigEvent_l(event, param);
+ sendIoConfigEvent_l(event);
}
// sendIoConfigEvent_l() must be called with ThreadBase::mLock held
-void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param)
+void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event)
{
- sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event, param);
+ sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event);
sendConfigEvent_l(configEvent);
}
@@ -646,7 +658,7 @@
} break;
case CFG_EVENT_IO: {
IoConfigEventData *data = (IoConfigEventData *)event->mData.get();
- audioConfigChanged(data->mEvent, data->mParam);
+ ioConfigChanged(data->mEvent);
} break;
case CFG_EVENT_SET_PARAMETER: {
SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get();
@@ -1602,13 +1614,19 @@
// If you change this calculation, also review the start threshold which is related.
if (!(*flags & IAudioFlinger::TRACK_FAST)
&& audio_is_linear_pcm(format) && sharedBuffer == 0) {
+ // this must match AudioTrack.cpp calculateMinFrameCount().
+ // TODO: Move to a common library
uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
if (minBufCount < 2) {
minBufCount = 2;
}
+ // For normal mixing tracks, if speed is > 1.0f (normal), AudioTrack
+ // or the client should compute and pass in a larger buffer request.
size_t minFrameCount =
- minBufCount * sourceFramesNeeded(sampleRate, mNormalFrameCount, mSampleRate);
+ minBufCount * sourceFramesNeededWithTimestretch(
+ sampleRate, mNormalFrameCount,
+ mSampleRate, AUDIO_TIMESTRETCH_SPEED_NORMAL /*speed*/);
if (frameCount < minFrameCount) { // including frameCount == 0
frameCount = minFrameCount;
}
@@ -1904,32 +1922,29 @@
return out_s8;
}
-void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) {
- AudioSystem::OutputDescriptor desc;
- void *param2 = NULL;
+void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event) {
+ sp<AudioIoDescriptor> desc = new AudioIoDescriptor();
+ ALOGV("PlaybackThread::ioConfigChanged, thread %p, event %d", this, event);
- ALOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event,
- param);
+ desc->mIoHandle = mId;
switch (event) {
- case AudioSystem::OUTPUT_OPENED:
- case AudioSystem::OUTPUT_CONFIG_CHANGED:
- desc.channelMask = mChannelMask;
- desc.samplingRate = mSampleRate;
- desc.format = mFormat;
- desc.frameCount = mNormalFrameCount; // FIXME see
+ case AUDIO_OUTPUT_OPENED:
+ case AUDIO_OUTPUT_CONFIG_CHANGED:
+ desc->mPatch = mPatch;
+ desc->mChannelMask = mChannelMask;
+ desc->mSamplingRate = mSampleRate;
+ desc->mFormat = mFormat;
+ desc->mFrameCount = mNormalFrameCount; // FIXME see
// AudioFlinger::frameCount(audio_io_handle_t)
- desc.latency = latency_l();
- param2 = &desc;
+ desc->mLatency = latency_l();
break;
- case AudioSystem::STREAM_CONFIG_CHANGED:
- param2 = ¶m;
- case AudioSystem::OUTPUT_CLOSED:
+ case AUDIO_OUTPUT_CLOSED:
default:
break;
}
- mAudioFlinger->audioConfigChanged(event, mId, param2);
+ mAudioFlinger->ioConfigChanged(event, desc);
}
void AudioFlinger::PlaybackThread::writeCallback()
@@ -2038,6 +2053,9 @@
ALOGW("direct output implements resume but not pause");
}
}
+ if (!mHwSupportsPause && mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) {
+ LOG_ALWAYS_FATAL("HW_AV_SYNC requested but HAL does not implement pause and resume");
+ }
if (mType == DUPLICATING && mMixerBufferEnabled && mEffectBufferEnabled) {
// For best precision, we use float instead of the associated output
@@ -2661,13 +2679,23 @@
if (exitPending()) {
break;
}
- releaseWakeLock_l();
+ bool released = false;
+ // The following works around a bug in the offload driver. Ideally we would release
+ // the wake lock every time, but that causes the last offload buffer(s) to be
+ // dropped while the device is on battery, so we need to hold a wake lock during
+ // the drain phase.
+ if (mBytesRemaining && !(mDrainSequence & 1)) {
+ releaseWakeLock_l();
+ released = true;
+ }
mWakeLockUids.clear();
mActiveTracksGeneration++;
ALOGV("wait async completion");
mWaitWorkCV.wait(mLock);
ALOGV("async completion/wake");
- acquireWakeLock_l();
+ if (released) {
+ acquireWakeLock_l();
+ }
standbyTime = systemTime() + standbyDelay;
sleepTime = 0;
@@ -2916,21 +2944,79 @@
return INVALID_OPERATION;
}
+status_t AudioFlinger::MixerThread::createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ // if !&IDLE, holds the FastMixer state to restore after new parameters processed
+ FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
+ if (mFastMixer != 0) {
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ if (!(state->mCommand & FastMixerState::IDLE)) {
+ previousCommand = state->mCommand;
+ state->mCommand = FastMixerState::HOT_IDLE;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+ status_t status = PlaybackThread::createAudioPatch_l(patch, handle);
+
+ if (!(previousCommand & FastMixerState::IDLE)) {
+ ALOG_ASSERT(mFastMixer != 0);
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ ALOG_ASSERT(state->mCommand == FastMixerState::HOT_IDLE);
+ state->mCommand = previousCommand;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+ }
+
+ return status;
+}
+
status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_patch *patch,
audio_patch_handle_t *handle)
{
status_t status = NO_ERROR;
- if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- // store new device and send to effects
- audio_devices_t type = AUDIO_DEVICE_NONE;
- for (unsigned int i = 0; i < patch->num_sinks; i++) {
- type |= patch->sinks[i].ext.device.type;
- }
- mOutDevice = type;
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(mOutDevice);
+
+ // store new device and send to effects
+ audio_devices_t type = AUDIO_DEVICE_NONE;
+ for (unsigned int i = 0; i < patch->num_sinks; i++) {
+ type |= patch->sinks[i].ext.device.type;
+ }
+
+#ifdef ADD_BATTERY_DATA
+ // when changing the audio output device, call addBatteryData to notify
+ // the change
+ if (mOutDevice != type) {
+ uint32_t params = 0;
+ // check whether speaker is on
+ if (type & AUDIO_DEVICE_OUT_SPEAKER) {
+ params |= IMediaPlayerService::kBatteryDataSpeakerOn;
}
+ audio_devices_t deviceWithoutSpeaker
+ = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
+ // check if any other device (except speaker) is on
+ if (type & deviceWithoutSpeaker) {
+ params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
+ }
+
+ if (params != 0) {
+ addBatteryData(params);
+ }
+ }
+#endif
+
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(type);
+ }
+ mOutDevice = type;
+ mPatch = *patch;
+
+ if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
status = hwDevice->create_audio_patch(hwDevice,
patch->num_sources,
@@ -2939,19 +3025,72 @@
patch->sinks,
handle);
} else {
- ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+ char *address;
+ if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {
+ //FIXME: we only support address on first sink with HAL version < 3.0
+ address = audio_device_address_to_parameter(
+ patch->sinks[0].ext.device.type,
+ patch->sinks[0].ext.device.address);
+ } else {
+ address = (char *)calloc(1, 1);
+ }
+ AudioParameter param = AudioParameter(String8(address));
+ free(address);
+ param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ param.toString().string());
+ *handle = AUDIO_PATCH_HANDLE_NONE;
}
+ sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
+ return status;
+}
+
+status_t AudioFlinger::MixerThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
+{
+ // if !&IDLE, holds the FastMixer state to restore after new parameters processed
+ FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
+ if (mFastMixer != 0) {
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ if (!(state->mCommand & FastMixerState::IDLE)) {
+ previousCommand = state->mCommand;
+ state->mCommand = FastMixerState::HOT_IDLE;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+
+ status_t status = PlaybackThread::releaseAudioPatch_l(handle);
+
+ if (!(previousCommand & FastMixerState::IDLE)) {
+ ALOG_ASSERT(mFastMixer != 0);
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ ALOG_ASSERT(state->mCommand == FastMixerState::HOT_IDLE);
+ state->mCommand = previousCommand;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+ }
+
return status;
}
status_t AudioFlinger::PlaybackThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
{
status_t status = NO_ERROR;
+
+ mOutDevice = AUDIO_DEVICE_NONE;
+
if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
status = hwDevice->release_audio_patch(hwDevice, handle);
} else {
- ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+ AudioParameter param;
+ param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ param.toString().string());
}
return status;
}
@@ -3586,21 +3725,16 @@
// hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
// during last round
size_t desiredFrames;
- uint32_t sr = track->sampleRate();
- if (sr == mSampleRate) {
- desiredFrames = mNormalFrameCount;
- } else {
- desiredFrames = sourceFramesNeeded(sr, mNormalFrameCount, mSampleRate);
- // add frames already consumed but not yet released by the resampler
- // because mAudioTrackServerProxy->framesReady() will include these frames
- desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
-#if 0
- // the minimum track buffer size is normally twice the number of frames necessary
- // to fill one buffer and the resampler should not leave more than one buffer worth
- // of unreleased frames after each pass, but just in case...
- ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
-#endif
- }
+ const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();
+ AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
+
+ desiredFrames = sourceFramesNeededWithTimestretch(
+ sampleRate, mNormalFrameCount, mSampleRate, playbackRate.mSpeed);
+ // TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed.
+ // add frames already consumed but not yet released by the resampler
+ // because mAudioTrackServerProxy->framesReady() will include these frames
+ desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
@@ -3763,6 +3897,14 @@
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)reqSampleRate);
+
+ AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TIMESTRETCH,
+ AudioMixer::PLAYBACK_RATE,
+ &playbackRate);
+
/*
* Select the appropriate output buffer for the track.
*
@@ -4032,7 +4174,7 @@
audio_devices_t deviceWithoutSpeaker
= AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
// check if any other device (except speaker) is on
- if (value & deviceWithoutSpeaker ) {
+ if (value & deviceWithoutSpeaker) {
params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
}
@@ -4074,7 +4216,7 @@
}
mTracks[i]->mName = name;
}
- sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
}
@@ -4246,9 +4388,9 @@
sp<Track> l = mLatestActiveTrack.promote();
bool last = l.get() == track;
- if (mHwSupportsPause && track->isPausing()) {
+ if (track->isPausing()) {
track->setPaused();
- if (last && !mHwPaused) {
+ if (mHwSupportsPause && last && !mHwPaused) {
doHwPause = true;
mHwPaused = true;
}
@@ -4258,13 +4400,11 @@
if (last) {
flushPending = true;
}
- } else if (mHwSupportsPause && track->isResumePending()){
+ } else if (track->isResumePending()) {
track->resumeAck();
- if (last) {
- if (mHwPaused) {
- doHwResume = true;
- mHwPaused = false;
- }
+ if (last && mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
}
}
@@ -4526,7 +4666,7 @@
}
if (status == NO_ERROR && reconfig) {
readOutputParameters_l();
- sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
}
@@ -5085,10 +5225,13 @@
mOutputTracks[i]->destroy();
mOutputTracks.removeAt(i);
updateWaitTime_l();
+ if (thread->getOutput() == mOutput) {
+ mOutput = NULL;
+ }
return;
}
}
- ALOGV("removeOutputTrack(): unkonwn thread: %p", thread);
+ ALOGV("removeOutputTrack(): unknown thread: %p", thread);
}
// caller must hold mLock
@@ -5202,11 +5345,11 @@
}
initFastCapture =
// either capture sample rate is same as (a reasonable) primary output sample rate
- (((primaryOutputSampleRate == 44100 || primaryOutputSampleRate == 48000) &&
+ ((isMusicRate(primaryOutputSampleRate) &&
(mSampleRate == primaryOutputSampleRate)) ||
// or primary output sample rate is unknown, and capture sample rate is reasonable
((primaryOutputSampleRate == 0) &&
- ((mSampleRate == 44100 || mSampleRate == 48000)))) &&
+ isMusicRate(mSampleRate))) &&
// and the buffer size is < 12 ms
(mFrameCount * 1000) / mSampleRate < 12;
break;
@@ -5290,7 +5433,6 @@
// FIXME mNormalSource
}
-
AudioFlinger::RecordThread::~RecordThread()
{
if (mFastCapture != 0) {
@@ -5310,7 +5452,7 @@
}
mAudioFlinger->unregisterWriter(mFastCaptureNBLogWriter);
mAudioFlinger->unregisterWriter(mNBLogWriter);
- delete[] mRsmpInBuffer;
+ free(mRsmpInBuffer);
}
void AudioFlinger::RecordThread::onFirstRef()
@@ -5543,7 +5685,7 @@
// If an NBAIO source is present, use it to read the normal capture's data
if (mPipeSource != 0) {
size_t framesToRead = mBufferSize / mFrameSize;
- framesRead = mPipeSource->read(&mRsmpInBuffer[rear * mChannelCount],
+ framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
framesToRead, AudioBufferProvider::kInvalidPTS);
if (framesRead == 0) {
// since pipe is non-blocking, simulate blocking input
@@ -5552,7 +5694,7 @@
// otherwise use the HAL / AudioStreamIn directly
} else {
ssize_t bytesRead = mInput->stream->read(mInput->stream,
- &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+ (uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize);
if (bytesRead < 0) {
framesRead = bytesRead;
} else {
@@ -5572,13 +5714,13 @@
ALOG_ASSERT(framesRead > 0);
if (mTeeSink != 0) {
- (void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead);
+ (void) mTeeSink->write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
}
// If destination is non-contiguous, we now correct for reading past end of buffer.
{
size_t part1 = mRsmpInFramesP2 - rear;
if ((size_t) framesRead > part1) {
- memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
+ memcpy(mRsmpInBuffer, (uint8_t*)mRsmpInBuffer + mRsmpInFramesP2 * mFrameSize,
(framesRead - part1) * mFrameSize);
}
}
@@ -5594,6 +5736,9 @@
continue;
}
+ // TODO: This code probably should be moved to RecordTrack.
+ // TODO: Update the activeTrack buffer converter in case of reconfigure.
+
enum {
OVERRUN_UNKNOWN,
OVERRUN_TRUE,
@@ -5608,131 +5753,28 @@
size_t framesOut = activeTrack->mSink.frameCount;
LOG_ALWAYS_FATAL_IF((status == OK) != (framesOut > 0));
- int32_t front = activeTrack->mRsmpInFront;
- ssize_t filled = rear - front;
+ // check available frames and handle overrun conditions
+ // if the record track isn't draining fast enough.
+ bool hasOverrun;
size_t framesIn;
-
- if (filled < 0) {
- // should not happen, but treat like a massive overrun and re-sync
- framesIn = 0;
- activeTrack->mRsmpInFront = rear;
- overrun = OVERRUN_TRUE;
- } else if ((size_t) filled <= mRsmpInFrames) {
- framesIn = (size_t) filled;
- } else {
- // client is not keeping up with server, but give it latest data
- framesIn = mRsmpInFrames;
- activeTrack->mRsmpInFront = front = rear - framesIn;
+ activeTrack->mResamplerBufferProvider->sync(&framesIn, &hasOverrun);
+ if (hasOverrun) {
overrun = OVERRUN_TRUE;
}
-
if (framesOut == 0 || framesIn == 0) {
break;
}
- if (activeTrack->mResampler == NULL) {
- // no resampling
- if (framesIn > framesOut) {
- framesIn = framesOut;
- } else {
- framesOut = framesIn;
- }
- int8_t *dst = activeTrack->mSink.i8;
- while (framesIn > 0) {
- front &= mRsmpInFramesP2 - 1;
- size_t part1 = mRsmpInFramesP2 - front;
- if (part1 > framesIn) {
- part1 = framesIn;
- }
- int8_t *src = (int8_t *)mRsmpInBuffer + (front * mFrameSize);
- if (mChannelCount == activeTrack->mChannelCount) {
- memcpy(dst, src, part1 * mFrameSize);
- } else if (mChannelCount == 1) {
- upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (const int16_t *)src,
- part1);
- } else {
- downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
- (const int16_t *)src, part1);
- }
- dst += part1 * activeTrack->mFrameSize;
- front += part1;
- framesIn -= part1;
- }
- activeTrack->mRsmpInFront += framesOut;
-
- } else {
- // resampling
- // FIXME framesInNeeded should really be part of resampler API, and should
- // depend on the SRC ratio
- // to keep mRsmpInBuffer full so resampler always has sufficient input
- size_t framesInNeeded;
- // FIXME only re-calculate when it changes, and optimize for common ratios
- // Do not precompute in/out because floating point is not associative
- // e.g. a*b/c != a*(b/c).
- const double in(mSampleRate);
- const double out(activeTrack->mSampleRate);
- framesInNeeded = ceil(framesOut * in / out) + 1;
- ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g",
- framesInNeeded, framesOut, in / out);
- // Although we theoretically have framesIn in circular buffer, some of those are
- // unreleased frames, and thus must be discounted for purpose of budgeting.
- size_t unreleased = activeTrack->mRsmpInUnrel;
- framesIn = framesIn > unreleased ? framesIn - unreleased : 0;
- if (framesIn < framesInNeeded) {
- ALOGV("not enough to resample: have %u frames in but need %u in to "
- "produce %u out given in/out ratio of %.4g",
- framesIn, framesInNeeded, framesOut, in / out);
- size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * out / in) : 0;
- LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut);
- if (newFramesOut == 0) {
- break;
- }
- framesInNeeded = ceil(newFramesOut * in / out) + 1;
- ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g",
- framesInNeeded, newFramesOut, out / in);
- LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded);
- ALOGV("success 2: have %u frames in and need %u in to produce %u out "
- "given in/out ratio of %.4g",
- framesIn, framesInNeeded, newFramesOut, in / out);
- framesOut = newFramesOut;
- } else {
- ALOGV("success 1: have %u in and need %u in to produce %u out "
- "given in/out ratio of %.4g",
- framesIn, framesInNeeded, framesOut, in / out);
- }
-
- // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
- if (activeTrack->mRsmpOutFrameCount < framesOut) {
- // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
- delete[] activeTrack->mRsmpOutBuffer;
- // resampler always outputs stereo
- activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2];
- activeTrack->mRsmpOutFrameCount = framesOut;
- }
-
- // resampler accumulates, but we only have one source track
- memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
- activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut,
- // FIXME how about having activeTrack implement this interface itself?
- activeTrack->mResamplerBufferProvider
- /*this*/ /* AudioBufferProvider* */);
- // ditherAndClamp() works as long as all buffers returned by
- // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
- if (activeTrack->mChannelCount == 1) {
- // temporarily type pun mRsmpOutBuffer from Q4.27 to int16_t
- ditherAndClamp(activeTrack->mRsmpOutBuffer, activeTrack->mRsmpOutBuffer,
- framesOut);
- // the resampler always outputs stereo samples:
- // do post stereo to mono conversion
- downmix_to_mono_i16_from_stereo_i16(activeTrack->mSink.i16,
- (const int16_t *)activeTrack->mRsmpOutBuffer, framesOut);
- } else {
- ditherAndClamp((int32_t *)activeTrack->mSink.raw,
- activeTrack->mRsmpOutBuffer, framesOut);
- }
- // now done with mRsmpOutBuffer
-
- }
+ // Don't allow framesOut to be larger than what is possible with resampling
+ // from framesIn.
+ // This isn't strictly necessary but helps limit buffer resizing in
+ // RecordBufferConverter. TODO: remove when no longer needed.
+ framesOut = min(framesOut,
+ destinationFramesPossible(
+ framesIn, mSampleRate, activeTrack->mSampleRate));
+ // process frames from the RecordThread buffer provider to the RecordTrack buffer
+ framesOut = activeTrack->mRecordBufferConverter->convert(
+ activeTrack->mSink.raw, activeTrack->mResamplerBufferProvider, framesOut);
if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) {
overrun = OVERRUN_FALSE;
@@ -6041,12 +6083,9 @@
// was initialized to some value closer to the thread's mRsmpInFront, then the track could
// see previously buffered data before it called start(), but with greater risk of overrun.
- recordTrack->mRsmpInFront = mRsmpInRear;
- recordTrack->mRsmpInUnrel = 0;
- // FIXME why reset?
- if (recordTrack->mResampler != NULL) {
- recordTrack->mResampler->reset();
- }
+ recordTrack->mResamplerBufferProvider->reset();
+ // clear any converter state as new data will be discontinuous
+ recordTrack->mRecordBufferConverter->reset();
recordTrack->mState = TrackBase::STARTING_2;
// signal thread to start
mWaitWorkCV.broadcast();
@@ -6222,12 +6261,52 @@
write(fd, result.string(), result.size());
}
+
+void AudioFlinger::RecordThread::ResamplerBufferProvider::reset()
+{
+ sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
+ RecordThread *recordThread = (RecordThread *) threadBase.get();
+ mRsmpInFront = recordThread->mRsmpInRear;
+ mRsmpInUnrel = 0;
+}
+
+void AudioFlinger::RecordThread::ResamplerBufferProvider::sync(
+ size_t *framesAvailable, bool *hasOverrun)
+{
+ sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
+ RecordThread *recordThread = (RecordThread *) threadBase.get();
+ const int32_t rear = recordThread->mRsmpInRear;
+ const int32_t front = mRsmpInFront;
+ const ssize_t filled = rear - front;
+
+ size_t framesIn;
+ bool overrun = false;
+ if (filled < 0) {
+ // should not happen, but treat like a massive overrun and re-sync
+ framesIn = 0;
+ mRsmpInFront = rear;
+ overrun = true;
+ } else if ((size_t) filled <= recordThread->mRsmpInFrames) {
+ framesIn = (size_t) filled;
+ } else {
+ // client is not keeping up with server, but give it latest data
+ framesIn = recordThread->mRsmpInFrames;
+ mRsmpInFront = /* front = */ rear - framesIn;
+ overrun = true;
+ }
+ if (framesAvailable != NULL) {
+ *framesAvailable = framesIn;
+ }
+ if (hasOverrun != NULL) {
+ *hasOverrun = overrun;
+ }
+}
+
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
AudioBufferProvider::Buffer* buffer, int64_t pts __unused)
{
- RecordTrack *activeTrack = mRecordTrack;
- sp<ThreadBase> threadBase = activeTrack->mThread.promote();
+ sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
if (threadBase == 0) {
buffer->frameCount = 0;
buffer->raw = NULL;
@@ -6235,7 +6314,7 @@
}
RecordThread *recordThread = (RecordThread *) threadBase.get();
int32_t rear = recordThread->mRsmpInRear;
- int32_t front = activeTrack->mRsmpInFront;
+ int32_t front = mRsmpInFront;
ssize_t filled = rear - front;
// FIXME should not be P2 (don't want to increase latency)
// FIXME if client not keeping up, discard
@@ -6252,17 +6331,16 @@
part1 = ask;
}
if (part1 == 0) {
- // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
- LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved");
+ // out of data is fine since the resampler will return a short-count.
buffer->raw = NULL;
buffer->frameCount = 0;
- activeTrack->mRsmpInUnrel = 0;
+ mRsmpInUnrel = 0;
return NOT_ENOUGH_DATA;
}
- buffer->raw = recordThread->mRsmpInBuffer + front * recordThread->mChannelCount;
+ buffer->raw = (uint8_t*)recordThread->mRsmpInBuffer + front * recordThread->mFrameSize;
buffer->frameCount = part1;
- activeTrack->mRsmpInUnrel = part1;
+ mRsmpInUnrel = part1;
return NO_ERROR;
}
@@ -6270,18 +6348,264 @@
void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer(
AudioBufferProvider::Buffer* buffer)
{
- RecordTrack *activeTrack = mRecordTrack;
size_t stepCount = buffer->frameCount;
if (stepCount == 0) {
return;
}
- ALOG_ASSERT(stepCount <= activeTrack->mRsmpInUnrel);
- activeTrack->mRsmpInUnrel -= stepCount;
- activeTrack->mRsmpInFront += stepCount;
+ ALOG_ASSERT(stepCount <= mRsmpInUnrel);
+ mRsmpInUnrel -= stepCount;
+ mRsmpInFront += stepCount;
buffer->raw = NULL;
buffer->frameCount = 0;
}
+AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate) :
+ mSrcChannelMask(AUDIO_CHANNEL_INVALID), // updateParameters will set following vars
+ // mSrcFormat
+ // mSrcSampleRate
+ // mDstChannelMask
+ // mDstFormat
+ // mDstSampleRate
+ // mSrcChannelCount
+ // mDstChannelCount
+ // mDstFrameSize
+ mBuf(NULL), mBufFrames(0), mBufFrameSize(0),
+ mResampler(NULL),
+ mIsLegacyDownmix(false),
+ mIsLegacyUpmix(false),
+ mRequiresFloat(false),
+ mInputConverterProvider(NULL)
+{
+ (void)updateParameters(srcChannelMask, srcFormat, srcSampleRate,
+ dstChannelMask, dstFormat, dstSampleRate);
+}
+
+AudioFlinger::RecordThread::RecordBufferConverter::~RecordBufferConverter() {
+ free(mBuf);
+ delete mResampler;
+ delete mInputConverterProvider;
+}
+
+size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
+ AudioBufferProvider *provider, size_t frames)
+{
+ if (mInputConverterProvider != NULL) {
+ mInputConverterProvider->setBufferProvider(provider);
+ provider = mInputConverterProvider;
+ }
+
+ if (mResampler == NULL) {
+ ALOGVV("NO RESAMPLING sampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
+ mSrcSampleRate, mSrcFormat, mDstFormat);
+
+ AudioBufferProvider::Buffer buffer;
+ for (size_t i = frames; i > 0; ) {
+ buffer.frameCount = i;
+ status_t status = provider->getNextBuffer(&buffer, 0);
+ if (status != OK || buffer.frameCount == 0) {
+ frames -= i; // cannot fill request.
+ break;
+ }
+ // format convert to destination buffer
+ convertNoResampler(dst, buffer.raw, buffer.frameCount);
+
+ dst = (int8_t*)dst + buffer.frameCount * mDstFrameSize;
+ i -= buffer.frameCount;
+ provider->releaseBuffer(&buffer);
+ }
+ } else {
+ ALOGVV("RESAMPLING mSrcSampleRate:%u mDstSampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
+ mSrcSampleRate, mDstSampleRate, mSrcFormat, mDstFormat);
+
+ // reallocate buffer if needed
+ if (mBufFrameSize != 0 && mBufFrames < frames) {
+ free(mBuf);
+ mBufFrames = frames;
+ (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+ }
+ // resampler accumulates, but we only have one source track
+ memset(mBuf, 0, frames * mBufFrameSize);
+ frames = mResampler->resample((int32_t*)mBuf, frames, provider);
+ // format convert to destination buffer
+ convertResampler(dst, mBuf, frames);
+ }
+ return frames;
+}
+
+status_t AudioFlinger::RecordThread::RecordBufferConverter::updateParameters(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate)
+{
+ // quick evaluation if there is any change.
+ if (mSrcFormat == srcFormat
+ && mSrcChannelMask == srcChannelMask
+ && mSrcSampleRate == srcSampleRate
+ && mDstFormat == dstFormat
+ && mDstChannelMask == dstChannelMask
+ && mDstSampleRate == dstSampleRate) {
+ return NO_ERROR;
+ }
+
+ ALOGV("RecordBufferConverter updateParameters srcMask:%#x dstMask:%#x"
+ " srcFormat:%#x dstFormat:%#x srcRate:%u dstRate:%u",
+ srcChannelMask, dstChannelMask, srcFormat, dstFormat, srcSampleRate, dstSampleRate);
+ const bool valid =
+ audio_is_input_channel(srcChannelMask)
+ && audio_is_input_channel(dstChannelMask)
+ && audio_is_valid_format(srcFormat) && audio_is_linear_pcm(srcFormat)
+ && audio_is_valid_format(dstFormat) && audio_is_linear_pcm(dstFormat)
+ && (srcSampleRate <= dstSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX)
+ ; // no upsampling checks for now
+ if (!valid) {
+ return BAD_VALUE;
+ }
+
+ mSrcFormat = srcFormat;
+ mSrcChannelMask = srcChannelMask;
+ mSrcSampleRate = srcSampleRate;
+ mDstFormat = dstFormat;
+ mDstChannelMask = dstChannelMask;
+ mDstSampleRate = dstSampleRate;
+
+ // compute derived parameters
+ mSrcChannelCount = audio_channel_count_from_in_mask(srcChannelMask);
+ mDstChannelCount = audio_channel_count_from_in_mask(dstChannelMask);
+ mDstFrameSize = mDstChannelCount * audio_bytes_per_sample(mDstFormat);
+
+ // do we need to resample?
+ delete mResampler;
+ mResampler = NULL;
+ if (mSrcSampleRate != mDstSampleRate) {
+ mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_FLOAT,
+ mSrcChannelCount, mDstSampleRate);
+ mResampler->setSampleRate(mSrcSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
+ }
+
+ // are we running legacy channel conversion modes?
+ mIsLegacyDownmix = (mSrcChannelMask == AUDIO_CHANNEL_IN_STEREO
+ || mSrcChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK)
+ && mDstChannelMask == AUDIO_CHANNEL_IN_MONO;
+ mIsLegacyUpmix = mSrcChannelMask == AUDIO_CHANNEL_IN_MONO
+ && (mDstChannelMask == AUDIO_CHANNEL_IN_STEREO
+ || mDstChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK);
+
+ // do we need to process in float?
+ mRequiresFloat = mResampler != NULL || mIsLegacyDownmix || mIsLegacyUpmix;
+
+ // do we need a staging buffer to convert for destination (we can still optimize this)?
+ // we use mBufFrameSize > 0 to indicate both frame size as well as buffer necessity
+ if (mResampler != NULL) {
+ mBufFrameSize = max(mSrcChannelCount, FCC_2)
+ * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+ } else if ((mIsLegacyUpmix || mIsLegacyDownmix) && mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+ } else if (mSrcChannelMask != mDstChannelMask && mDstFormat != mSrcFormat) {
+ mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(mSrcFormat);
+ } else {
+ mBufFrameSize = 0;
+ }
+ mBufFrames = 0; // force the buffer to be resized.
+
+ // do we need an input converter buffer provider to give us float?
+ delete mInputConverterProvider;
+ mInputConverterProvider = NULL;
+ if (mRequiresFloat && mSrcFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ mInputConverterProvider = new ReformatBufferProvider(
+ audio_channel_count_from_in_mask(mSrcChannelMask),
+ mSrcFormat,
+ AUDIO_FORMAT_PCM_FLOAT,
+ 256 /* provider buffer frame count */);
+ }
+
+ // do we need a remixer to do channel mask conversion
+ if (!mIsLegacyDownmix && !mIsLegacyUpmix && mSrcChannelMask != mDstChannelMask) {
+ (void) memcpy_by_index_array_initialization_from_channel_mask(
+ mIdxAry, ARRAY_SIZE(mIdxAry), mDstChannelMask, mSrcChannelMask);
+ }
+ return NO_ERROR;
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convertNoResampler(
+ void *dst, const void *src, size_t frames)
+{
+ // src is native type unless there is legacy upmix or downmix, whereupon it is float.
+ if (mBufFrameSize != 0 && mBufFrames < frames) {
+ free(mBuf);
+ mBufFrames = frames;
+ (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+ }
+ // do we need to do legacy upmix and downmix?
+ if (mIsLegacyUpmix || mIsLegacyDownmix) {
+ void *dstBuf = mBuf != NULL ? mBuf : dst;
+ if (mIsLegacyUpmix) {
+ upmix_to_stereo_float_from_mono_float((float *)dstBuf,
+ (const float *)src, frames);
+ } else /*mIsLegacyDownmix */ {
+ downmix_to_mono_float_from_stereo_float((float *)dstBuf,
+ (const float *)src, frames);
+ }
+ if (mBuf != NULL) {
+ memcpy_by_audio_format(dst, mDstFormat, mBuf, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mDstChannelCount);
+ }
+ return;
+ }
+ // do we need to do channel mask conversion?
+ if (mSrcChannelMask != mDstChannelMask) {
+ void *dstBuf = mBuf != NULL ? mBuf : dst;
+ memcpy_by_index_array(dstBuf, mDstChannelCount,
+ src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mSrcFormat), frames);
+ if (dstBuf == dst) {
+ return; // format is the same
+ }
+ }
+ // convert to destination buffer
+ const void *convertBuf = mBuf != NULL ? mBuf : src;
+ memcpy_by_audio_format(dst, mDstFormat, convertBuf, mSrcFormat,
+ frames * mDstChannelCount);
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convertResampler(
+ void *dst, /*not-a-const*/ void *src, size_t frames)
+{
+ // src buffer format is ALWAYS float when entering this routine
+ if (mIsLegacyUpmix) {
+ ; // mono to stereo already handled by resampler
+ } else if (mIsLegacyDownmix
+ || (mSrcChannelMask == mDstChannelMask && mSrcChannelCount == 1)) {
+ // the resampler outputs stereo for mono input channel (a feature?)
+ // must convert to mono
+ downmix_to_mono_float_from_stereo_float((float *)src,
+ (const float *)src, frames);
+ } else if (mSrcChannelMask != mDstChannelMask) {
+ // convert to mono channel again for channel mask conversion (could be skipped
+ // with further optimization).
+ if (mSrcChannelCount == 1) {
+ downmix_to_mono_float_from_stereo_float((float *)src,
+ (const float *)src, frames);
+ }
+ // convert to destination format (in place, OK as float is larger than other types)
+ if (mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ memcpy_by_audio_format(src, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mSrcChannelCount);
+ }
+ // channel convert and save to dst
+ memcpy_by_index_array(dst, mDstChannelCount,
+ src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mDstFormat), frames);
+ return;
+ }
+ // convert to destination format and save to dst
+ memcpy_by_audio_format(dst, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mDstChannelCount);
+}
+
bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
status_t& status)
{
@@ -6292,6 +6616,10 @@
audio_format_t reqFormat = mFormat;
uint32_t samplingRate = mSampleRate;
audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount);
+ // possible that we are > 2 channels, use channel index mask
+ if (channelMask == AUDIO_CHANNEL_INVALID && mChannelCount <= FCC_8) {
+ audio_channel_mask_for_index_assignment_from_count(mChannelCount);
+ }
AudioParameter param = AudioParameter(keyValuePair);
int value;
@@ -6303,7 +6631,7 @@
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ if (!audio_is_linear_pcm((audio_format_t) value)) {
status = BAD_VALUE;
} else {
reqFormat = (audio_format_t) value;
@@ -6312,7 +6640,8 @@
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
audio_channel_mask_t mask = (audio_channel_mask_t) value;
- if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
+ if (!audio_is_input_channel(mask) ||
+ audio_channel_count_from_in_mask(mask) > FCC_8) {
status = BAD_VALUE;
} else {
channelMask = mask;
@@ -6377,19 +6706,17 @@
}
if (reconfig) {
if (status == BAD_VALUE &&
- reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
- reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
+ audio_is_linear_pcm(mInput->stream->common.get_format(&mInput->stream->common)) &&
+ audio_is_linear_pcm(reqFormat) &&
(mInput->stream->common.get_sample_rate(&mInput->stream->common)
- <= (2 * samplingRate)) &&
+ <= (AUDIO_RESAMPLER_DOWN_RATIO_MAX * samplingRate)) &&
audio_channel_count_from_in_mask(
- mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 &&
- (channelMask == AUDIO_CHANNEL_IN_MONO ||
- channelMask == AUDIO_CHANNEL_IN_STEREO)) {
+ mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_8) {
status = NO_ERROR;
}
if (status == NO_ERROR) {
readInputParameters_l();
- sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
+ sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
}
}
}
@@ -6410,26 +6737,27 @@
return out_s8;
}
-void AudioFlinger::RecordThread::audioConfigChanged(int event, int param __unused) {
- AudioSystem::OutputDescriptor desc;
- const void *param2 = NULL;
+void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event) {
+ sp<AudioIoDescriptor> desc = new AudioIoDescriptor();
+
+ desc->mIoHandle = mId;
switch (event) {
- case AudioSystem::INPUT_OPENED:
- case AudioSystem::INPUT_CONFIG_CHANGED:
- desc.channelMask = mChannelMask;
- desc.samplingRate = mSampleRate;
- desc.format = mFormat;
- desc.frameCount = mFrameCount;
- desc.latency = 0;
- param2 = &desc;
+ case AUDIO_INPUT_OPENED:
+ case AUDIO_INPUT_CONFIG_CHANGED:
+ desc->mPatch = mPatch;
+ desc->mChannelMask = mChannelMask;
+ desc->mSamplingRate = mSampleRate;
+ desc->mFormat = mFormat;
+ desc->mFrameCount = mFrameCount;
+ desc->mLatency = 0;
break;
- case AudioSystem::INPUT_CLOSED:
+ case AUDIO_INPUT_CLOSED:
default:
break;
}
- mAudioFlinger->audioConfigChanged(event, mId, param2);
+ mAudioFlinger->ioConfigChanged(event, desc);
}
void AudioFlinger::RecordThread::readInputParameters_l()
@@ -6437,10 +6765,13 @@
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
+ if (mChannelCount > FCC_8) {
+ ALOGE("HAL channel count %d > %d", mChannelCount, FCC_8);
+ }
mHALFormat = mInput->stream->common.get_format(&mInput->stream->common);
mFormat = mHALFormat;
- if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+ if (!audio_is_linear_pcm(mFormat)) {
+ ALOGE("HAL format %#x is not linear pcm", mFormat);
}
mFrameSize = audio_stream_in_frame_size(mInput->stream);
mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
@@ -6451,9 +6782,11 @@
// The value is somewhat arbitrary, and could probably be even larger.
// A larger value should allow more old data to be read after a track calls start(),
// without increasing latency.
+ //
+ // Note this is independent of the maximum downsampling ratio permitted for capture.
mRsmpInFrames = mFrameCount * 7;
mRsmpInFramesP2 = roundup(mRsmpInFrames);
- delete[] mRsmpInBuffer;
+ free(mRsmpInBuffer);
// TODO optimize audio capture buffer sizes ...
// Here we calculate the size of the sliding buffer used as a source
@@ -6463,7 +6796,7 @@
// The current value is higher than necessary. However it should not add to latency.
// Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
- mRsmpInBuffer = new int16_t[(mRsmpInFramesP2 + mFrameCount - 1) * mChannelCount];
+ (void)posix_memalign(&mRsmpInBuffer, 32, (mRsmpInFramesP2 + mFrameCount - 1) * mFrameSize);
// AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
// But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
@@ -6567,33 +6900,35 @@
audio_patch_handle_t *handle)
{
status_t status = NO_ERROR;
- if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- // store new device and send to effects
- mInDevice = patch->sources[0].ext.device.type;
+
+ // store new device and send to effects
+ mInDevice = patch->sources[0].ext.device.type;
+ mPatch = *patch;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mInDevice);
+ }
+
+ // disable AEC and NS if the device is a BT SCO headset supporting those
+ // pre processings
+ if (mTracks.size() > 0) {
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
+ setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
+ }
+ }
+
+ // store new source and send to effects
+ if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
+ mAudioSource = patch->sinks[0].ext.mix.usecase.source;
for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(mInDevice);
+ mEffectChains[i]->setAudioSource_l(mAudioSource);
}
+ }
- // disable AEC and NS if the device is a BT SCO headset supporting those
- // pre processings
- if (mTracks.size() > 0) {
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
- }
- }
-
- // store new source and send to effects
- if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
- mAudioSource = patch->sinks[0].ext.mix.usecase.source;
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setAudioSource_l(mAudioSource);
- }
- }
-
+ if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
status = hwDevice->create_audio_patch(hwDevice,
patch->num_sources,
@@ -6602,19 +6937,44 @@
patch->sinks,
handle);
} else {
- ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+ char *address;
+ if (strcmp(patch->sources[0].ext.device.address, "") != 0) {
+ address = audio_device_address_to_parameter(
+ patch->sources[0].ext.device.type,
+ patch->sources[0].ext.device.address);
+ } else {
+ address = (char *)calloc(1, 1);
+ }
+ AudioParameter param = AudioParameter(String8(address));
+ free(address);
+ param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING),
+ (int)patch->sources[0].ext.device.type);
+ param.addInt(String8(AUDIO_PARAMETER_STREAM_INPUT_SOURCE),
+ (int)patch->sinks[0].ext.mix.usecase.source);
+ status = mInput->stream->common.set_parameters(&mInput->stream->common,
+ param.toString().string());
+ *handle = AUDIO_PATCH_HANDLE_NONE;
}
+
+ sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
+
return status;
}
status_t AudioFlinger::RecordThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
{
status_t status = NO_ERROR;
+
+ mInDevice = AUDIO_DEVICE_NONE;
+
if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
status = hwDevice->release_audio_patch(hwDevice, handle);
} else {
- ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+ AudioParameter param;
+ param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
+ status = mInput->stream->common.set_parameters(&mInput->stream->common,
+ param.toString().string());
}
return status;
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index d600ea9..37bacae 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -100,22 +100,21 @@
class IoConfigEventData : public ConfigEventData {
public:
- IoConfigEventData(int event, int param) :
- mEvent(event), mParam(param) {}
+ IoConfigEventData(audio_io_config_event event) :
+ mEvent(event) {}
virtual void dump(char *buffer, size_t size) {
- snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam);
+ snprintf(buffer, size, "IO event: event %d\n", mEvent);
}
- const int mEvent;
- const int mParam;
+ const audio_io_config_event mEvent;
};
class IoConfigEvent : public ConfigEvent {
public:
- IoConfigEvent(int event, int param) :
+ IoConfigEvent(audio_io_config_event event) :
ConfigEvent(CFG_EVENT_IO) {
- mData = new IoConfigEventData(event, param);
+ mData = new IoConfigEventData(event);
}
virtual ~IoConfigEvent() {}
};
@@ -231,6 +230,8 @@
// static externally-visible
type_t type() const { return mType; }
+ bool isDuplicating() const { return (mType == DUPLICATING); }
+
audio_io_handle_t id() const { return mId;}
// dynamic externally-visible
@@ -250,13 +251,13 @@
status_t& status) = 0;
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys) = 0;
- virtual void audioConfigChanged(int event, int param = 0) = 0;
+ virtual void ioConfigChanged(audio_io_config_event event) = 0;
// sendConfigEvent_l() must be called with ThreadBase::mLock held
// Can temporarily release the lock if waiting for a reply from
// processConfigEvents_l().
status_t sendConfigEvent_l(sp<ConfigEvent>& event);
- void sendIoConfigEvent(int event, int param = 0);
- void sendIoConfigEvent_l(int event, int param = 0);
+ void sendIoConfigEvent(audio_io_config_event event);
+ void sendIoConfigEvent_l(audio_io_config_event event);
void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
status_t sendSetParameterConfigEvent_l(const String8& keyValuePair);
status_t sendCreateAudioPatchConfigEvent(const struct audio_patch *patch,
@@ -427,6 +428,7 @@
bool mStandby; // Whether thread is currently in standby.
audio_devices_t mOutDevice; // output device
audio_devices_t mInDevice; // input device
+ struct audio_patch mPatch;
audio_source_t mAudioSource;
const audio_io_handle_t mId;
@@ -560,7 +562,7 @@
{ return android_atomic_acquire_load(&mSuspended) > 0; }
virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged(int event, int param = 0);
+ virtual void ioConfigChanged(audio_io_config_event event);
status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
// FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency.
// Consider also removing and passing an explicit mMainBuffer initialization
@@ -713,8 +715,9 @@
audio_patch_handle_t *handle);
virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
- bool usesHwAvSync() const { return (mType == DIRECT) && (mOutput != NULL) &&
- (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
+ bool usesHwAvSync() const { return (mType == DIRECT) && (mOutput != NULL)
+ && mHwSupportsPause
+ && (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
private:
@@ -865,6 +868,10 @@
virtual void threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove);
virtual uint32_t correctLatency_l(uint32_t latency) const;
+ virtual status_t createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+ virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
+
AudioMixer* mAudioMixer; // normal mixer
private:
// one-time initialization, no locks required
@@ -1036,17 +1043,132 @@
public:
class RecordTrack;
+
+ /* The ResamplerBufferProvider is used to retrieve recorded input data from the
+ * RecordThread. It maintains local state on the relative position of the read
+ * position of the RecordTrack compared with the RecordThread.
+ */
class ResamplerBufferProvider : public AudioBufferProvider
- // derives from AudioBufferProvider interface for use by resampler
{
public:
- ResamplerBufferProvider(RecordTrack* recordTrack) : mRecordTrack(recordTrack) { }
+ ResamplerBufferProvider(RecordTrack* recordTrack) :
+ mRecordTrack(recordTrack),
+ mRsmpInUnrel(0), mRsmpInFront(0) { }
virtual ~ResamplerBufferProvider() { }
+
+ // called to set the ResamplerBufferProvider to head of the RecordThread data buffer,
+ // skipping any previous data read from the hal.
+ virtual void reset();
+
+ /* Synchronizes RecordTrack position with the RecordThread.
+ * Calculates available frames and handle overruns if the RecordThread
+ * has advanced faster than the ResamplerBufferProvider has retrieved data.
+ * TODO: why not do this for every getNextBuffer?
+ *
+ * Parameters
+ * framesAvailable: pointer to optional output size_t to store record track
+ * frames available.
+ * hasOverrun: pointer to optional boolean, returns true if track has overrun.
+ */
+
+ virtual void sync(size_t *framesAvailable = NULL, bool *hasOverrun = NULL);
+
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts);
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
private:
RecordTrack * const mRecordTrack;
+ size_t mRsmpInUnrel; // unreleased frames remaining from
+ // most recent getNextBuffer
+ // for debug only
+ int32_t mRsmpInFront; // next available frame
+ // rolling counter that is never cleared
+ };
+
+ /* The RecordBufferConverter is used for format, channel, and sample rate
+ * conversion for a RecordTrack.
+ *
+ * TODO: Self contained, so move to a separate file later.
+ *
+ * RecordBufferConverter uses the convert() method rather than exposing a
+ * buffer provider interface; this is to save a memory copy.
+ */
+ class RecordBufferConverter
+ {
+ public:
+ RecordBufferConverter(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate);
+
+ ~RecordBufferConverter();
+
+ /* Converts input data from an AudioBufferProvider by format, channelMask,
+ * and sampleRate to a destination buffer.
+ *
+ * Parameters
+ * dst: buffer to place the converted data.
+ * provider: buffer provider to obtain source data.
+ * frames: number of frames to convert
+ *
+ * Returns the number of frames converted.
+ */
+ size_t convert(void *dst, AudioBufferProvider *provider, size_t frames);
+
+ // returns NO_ERROR if constructor was successful
+ status_t initCheck() const {
+ // mSrcChannelMask set on successful updateParameters
+ return mSrcChannelMask != AUDIO_CHANNEL_INVALID ? NO_ERROR : NO_INIT;
+ }
+
+ // allows dynamic reconfigure of all parameters
+ status_t updateParameters(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate);
+
+ // called to reset resampler buffers on record track discontinuity
+ void reset() {
+ if (mResampler != NULL) {
+ mResampler->reset();
+ }
+ }
+
+ private:
+ // format conversion when not using resampler
+ void convertNoResampler(void *dst, const void *src, size_t frames);
+
+ // format conversion when using resampler; modifies src in-place
+ void convertResampler(void *dst, /*not-a-const*/ void *src, size_t frames);
+
+ // user provided information
+ audio_channel_mask_t mSrcChannelMask;
+ audio_format_t mSrcFormat;
+ uint32_t mSrcSampleRate;
+ audio_channel_mask_t mDstChannelMask;
+ audio_format_t mDstFormat;
+ uint32_t mDstSampleRate;
+
+ // derived information
+ uint32_t mSrcChannelCount;
+ uint32_t mDstChannelCount;
+ size_t mDstFrameSize;
+
+ // format conversion buffer
+ void *mBuf;
+ size_t mBufFrames;
+ size_t mBufFrameSize;
+
+ // resampler info
+ AudioResampler *mResampler;
+
+ bool mIsLegacyDownmix; // legacy stereo to mono conversion needed
+ bool mIsLegacyUpmix; // legacy mono to stereo conversion needed
+ bool mRequiresFloat; // data processing requires float (e.g. resampler)
+ PassthruBufferProvider *mInputConverterProvider; // converts input to float
+ int8_t mIdxAry[sizeof(uint32_t) * 8]; // used for channel mask conversion
};
#include "RecordTracks.h"
@@ -1111,7 +1233,7 @@
status_t& status);
virtual void cacheParameters_l() {}
virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged(int event, int param = 0);
+ virtual void ioConfigChanged(audio_io_config_event event);
virtual status_t createAudioPatch_l(const struct audio_patch *patch,
audio_patch_handle_t *handle);
virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
@@ -1157,7 +1279,7 @@
Condition mStartStopCond;
// resampler converts input at HAL Hz to output at AudioRecord client Hz
- int16_t *mRsmpInBuffer; // see new[] for details on the size
+ void *mRsmpInBuffer; //
size_t mRsmpInFrames; // size of resampler input in frames
size_t mRsmpInFramesP2;// size rounded up to a power-of-2
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index dc9f249..1b03060 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -404,8 +404,7 @@
mIsInvalid(false),
mAudioTrackServerProxy(NULL),
mResumeToStopping(false),
- mFlushHwPending(false),
- mPreviousTimestampValid(false)
+ mFlushHwPending(false)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -863,7 +862,6 @@
if (mState == FLUSHED) {
mState = IDLE;
}
- mPreviousTimestampValid = false;
}
}
@@ -885,12 +883,10 @@
{
// Client should implement this using SSQ; the unpresented frame count in latch is irrelevant
if (isFastTrack()) {
- // FIXME no lock held to set mPreviousTimestampValid = false
return INVALID_OPERATION;
}
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
- // FIXME no lock held to set mPreviousTimestampValid = false
return INVALID_OPERATION;
}
@@ -900,12 +896,14 @@
status_t result = INVALID_OPERATION;
if (!isOffloaded() && !isDirect()) {
if (!playbackThread->mLatchQValid) {
- mPreviousTimestampValid = false;
return INVALID_OPERATION;
}
- uint32_t unpresentedFrames =
- ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
- playbackThread->mSampleRate;
+ // FIXME Not accurate under dynamic changes of sample rate and speed.
+ // Do not use track's mSampleRate as it is not current for mixer tracks.
+ uint32_t sampleRate = mAudioTrackServerProxy->getSampleRate();
+ AudioPlaybackRate playbackRate = mAudioTrackServerProxy->getPlaybackRate();
+ uint32_t unpresentedFrames = ((double) playbackThread->mLatchQ.mUnpresentedFrames *
+ sampleRate * playbackRate.mSpeed)/ playbackThread->mSampleRate;
// FIXME Since we're using a raw pointer as the key, it is theoretically possible
// for a brand new track to share the same address as a recently destroyed
// track, and thus for us to get the frames released of the wrong track.
@@ -916,10 +914,7 @@
uint32_t framesWritten = i >= 0 ?
playbackThread->mLatchQ.mFramesReleased[i] :
mAudioTrackServerProxy->framesReleased();
- if (framesWritten < unpresentedFrames) {
- mPreviousTimestampValid = false;
- // return invalid result
- } else {
+ if (framesWritten >= unpresentedFrames) {
timestamp.mPosition = framesWritten - unpresentedFrames;
timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime;
result = NO_ERROR;
@@ -928,41 +923,6 @@
result = playbackThread->getTimestamp_l(timestamp);
}
- // Prevent retrograde motion in timestamp.
- if (result == NO_ERROR) {
- if (mPreviousTimestampValid) {
- if (timestamp.mTime.tv_sec < mPreviousTimestamp.mTime.tv_sec ||
- (timestamp.mTime.tv_sec == mPreviousTimestamp.mTime.tv_sec &&
- timestamp.mTime.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) {
- ALOGW("WARNING - retrograde timestamp time");
- // FIXME Consider blocking this from propagating upwards.
- }
-
- // Looking at signed delta will work even when the timestamps
- // are wrapping around.
- int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition
- - mPreviousTimestamp.mPosition);
- // position can bobble slightly as an artifact; this hides the bobble
- static const int32_t MINIMUM_POSITION_DELTA = 8;
- if (deltaPosition < 0) {
-#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec)
- ALOGW("WARNING - retrograde timestamp position corrected,"
- " %d = %u - %u, (at %llu, %llu nanos)",
- deltaPosition,
- timestamp.mPosition,
- mPreviousTimestamp.mPosition,
- TIME_TO_NANOS(timestamp.mTime),
- TIME_TO_NANOS(mPreviousTimestamp.mTime));
-#undef TIME_TO_NANOS
- }
- if (deltaPosition < MINIMUM_POSITION_DELTA) {
- // Current timestamp is bad. Use last valid timestamp.
- timestamp = mPreviousTimestamp;
- }
- }
- mPreviousTimestamp = timestamp;
- mPreviousTimestampValid = true;
- }
return result;
}
@@ -1861,13 +1821,14 @@
AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThread,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
size_t frameCount,
void *buffer,
IAudioFlinger::track_flags_t flags)
- : Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
+ : Track(playbackThread, NULL, streamType,
sampleRate, format, channelMask, frameCount,
buffer, 0, 0, getuid(), flags, TYPE_PATCH),
mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
@@ -1989,29 +1950,30 @@
((flags & IAudioFlinger::TRACK_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE),
type),
- mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
- // See real initialization of mRsmpInFront at RecordThread::start()
- mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
+ mOverflow(false),
+ mFramesToDrop(0)
{
if (mCblk == NULL) {
return;
}
+ mRecordBufferConverter = new RecordBufferConverter(
+ thread->mChannelMask, thread->mFormat, thread->mSampleRate,
+ channelMask, format, sampleRate);
+ // Check if the RecordBufferConverter construction was successful.
+ // If not, don't continue with construction.
+ //
+ // NOTE: It would be extremely rare that the record track cannot be created
+ // for the current device, but a pending or future device change would make
+ // the record track configuration valid.
+ if (mRecordBufferConverter->initCheck() != NO_ERROR) {
+ ALOGE("RecordTrack unable to create record buffer converter");
+ return;
+ }
+
mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack());
-
- uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
- // FIXME I don't understand either of the channel count checks
- if (thread->mSampleRate != sampleRate && thread->mChannelCount <= FCC_2 &&
- channelCount <= FCC_2) {
- // sink SR
- mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
- thread->mChannelCount, sampleRate);
- // source SR
- mResampler->setSampleRate(thread->mSampleRate);
- mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
- mResamplerBufferProvider = new ResamplerBufferProvider(this);
- }
+ mResamplerBufferProvider = new ResamplerBufferProvider(this);
if (flags & IAudioFlinger::TRACK_FAST) {
ALOG_ASSERT(thread->mFastTrackAvail);
@@ -2022,11 +1984,19 @@
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
{
ALOGV("%s", __func__);
- delete mResampler;
- delete[] mRsmpOutBuffer;
+ delete mRecordBufferConverter;
delete mResamplerBufferProvider;
}
+status_t AudioFlinger::RecordThread::RecordTrack::initCheck() const
+{
+ status_t status = TrackBase::initCheck();
+ if (status == NO_ERROR && mServerProxy == 0) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
int64_t pts __unused)
diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk
index 8604ef5..536eb93 100644
--- a/services/audioflinger/tests/Android.mk
+++ b/services/audioflinger/tests/Android.mk
@@ -39,11 +39,13 @@
LOCAL_SRC_FILES:= \
test-mixer.cpp \
../AudioMixer.cpp.arm \
+ ../BufferProviders.cpp
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils) \
- frameworks/av/services/audioflinger
+ frameworks/av/services/audioflinger \
+ external/sonic
LOCAL_STATIC_LIBRARIES := \
libsndfile
@@ -57,7 +59,8 @@
libdl \
libcutils \
libutils \
- liblog
+ liblog \
+ libsonic
LOCAL_MODULE:= test-mixer
diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp
index d6217ba..9e375db 100644
--- a/services/audioflinger/tests/resampler_tests.cpp
+++ b/services/audioflinger/tests/resampler_tests.cpp
@@ -48,7 +48,10 @@
if (thisFrames == 0 || thisFrames > outputFrames - i) {
thisFrames = outputFrames - i;
}
- resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
+ size_t framesResampled = resampler->resample(
+ (int32_t*) output + channels*i, thisFrames, provider);
+ // we should have enough buffer space, so there is no short count.
+ ASSERT_EQ(thisFrames, framesResampled);
i += thisFrames;
}
}
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 116d0d6..8523fc5 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -106,10 +106,12 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ int selectedDeviceId,
const audio_offload_info_t *offloadInfo) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding stream.
virtual status_t startOutput(audio_io_handle_t output,
@@ -128,10 +130,12 @@
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
input_type_t *inputType) = 0;
// indicates to the audio policy manager that the input starts being used.
virtual status_t startInput(audio_io_handle_t input,
@@ -207,7 +211,7 @@
struct audio_patch *patches,
unsigned int *generation) = 0;
virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
- virtual void clearAudioPatches(uid_t uid) = 0;
+ virtual void releaseResourcesForUid(uid_t uid) = 0;
virtual status_t acquireSoundTriggerSession(audio_session_t *session,
audio_io_handle_t *ioHandle,
@@ -217,6 +221,11 @@
virtual status_t registerPolicyMixes(Vector<AudioMix> mixes) = 0;
virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes) = 0;
+
+ virtual status_t startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle) = 0;
+ virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
};
@@ -319,6 +328,8 @@
virtual void onAudioPatchListUpdate() = 0;
virtual audio_unique_id_t newAudioUniqueId() = 0;
+
+ virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0;
};
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h
index a4cc759..4205589 100755
--- a/services/audiopolicy/common/include/Volume.h
+++ b/services/audiopolicy/common/include/Volume.h
@@ -18,6 +18,10 @@
#include <system/audio.h>
#include <utils/Log.h>
+#include <math.h>
+
+// Absolute min volume in dB (can be represented in single precision normal float value)
+#define VOLUME_MIN_DB (-758)
class VolumeCurvePoint
{
@@ -32,7 +36,7 @@
/**
* 4 points to define the volume attenuation curve, each characterized by the volume
* index (from 0 to 100) at which they apply, and the attenuation in dB at that index.
- * we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl()
+ * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb()
*
* @todo shall become configurable
*/
@@ -134,4 +138,20 @@
}
}
+ static inline float DbToAmpl(float decibels)
+ {
+ if (decibels <= VOLUME_MIN_DB) {
+ return 0.0f;
+ }
+ return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
+ }
+
+ static inline float AmplToDb(float amplification)
+ {
+ if (amplification == 0) {
+ return VOLUME_MIN_DB;
+ }
+ return 20 * log10(amplification);
+ }
+
};
diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h
index a2327ee..4eef02f2 100755
--- a/services/audiopolicy/common/include/policy.h
+++ b/services/audiopolicy/common/include/policy.h
@@ -20,7 +20,7 @@
// For mixed output and inputs, the policy will use max mixer sampling rates.
// Do not limit sampling rate otherwise
-#define MAX_MIXER_SAMPLING_RATE 48000
+#define MAX_MIXER_SAMPLING_RATE 192000
// For mixed output and inputs, the policy will use max mixer channel count.
// Do not limit channel count otherwise
@@ -60,7 +60,7 @@
*
* @return true if the device is a virtual one, false otherwise.
*/
-static bool is_virtual_input_device(audio_devices_t device)
+static inline bool is_virtual_input_device(audio_devices_t device)
{
if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
device &= ~AUDIO_DEVICE_BIT_IN;
@@ -78,7 +78,7 @@
*
* @return true if the device needs distinguish on address, false otherwise..
*/
-static bool device_distinguishes_on_address(audio_devices_t device)
+static inline bool device_distinguishes_on_address(audio_devices_t device)
{
return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0);
}
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index 71ba1cb..7c265aa 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -25,6 +25,7 @@
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/include \
$(TOPDIR)frameworks/av/services/audiopolicy/common/include \
+ $(TOPDIR)frameworks/av/services/audiopolicy
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH)/include
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index 7536a37..18bcfdb 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -34,12 +34,11 @@
public:
AudioInputDescriptor(const sp<IOProfile>& profile);
void setIoHandle(audio_io_handle_t ioHandle);
-
+ audio_port_handle_t getId() const;
audio_module_handle_t getModuleHandle() const;
status_t dump(int fd);
- audio_port_handle_t mId;
audio_io_handle_t mIoHandle; // input handle
audio_devices_t mDevice; // current device this input is routed to
AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
@@ -57,6 +56,9 @@
const struct audio_port_config *srcConfig = NULL) const;
virtual sp<AudioPort> getAudioPort() const { return mProfile; }
void toAudioPort(struct audio_port *port) const;
+
+private:
+ audio_port_handle_t mId;
};
class AudioInputCollection :
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 43ee691..50f622d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -27,24 +27,36 @@
class IOProfile;
class AudioMix;
+class AudioPolicyClientInterface;
// descriptor for audio outputs. Used to maintain current configuration of each opened audio output
// and keep track of the usage of this output by each audio stream type.
class AudioOutputDescriptor: public AudioPortConfig
{
public:
- AudioOutputDescriptor(const sp<IOProfile>& profile);
+ AudioOutputDescriptor(const sp<AudioPort>& port,
+ AudioPolicyClientInterface *clientInterface);
+ virtual ~AudioOutputDescriptor() {}
status_t dump(int fd);
+ void log(const char* indent);
- audio_devices_t device() const;
- void changeRefCount(audio_stream_type_t stream, int delta);
+ audio_port_handle_t getId() const;
+ virtual audio_devices_t device() const;
+ virtual bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
+ virtual audio_devices_t supportedDevices();
+ virtual bool isDuplicated() const { return false; }
+ virtual uint32_t latency() { return 0; }
+ virtual bool isFixedVolume(audio_devices_t device);
+ virtual sp<AudioOutputDescriptor> subOutput1() { return 0; }
+ virtual sp<AudioOutputDescriptor> subOutput2() { return 0; }
+ virtual bool setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t delayMs,
+ bool force);
+ virtual void changeRefCount(audio_stream_type_t stream, int delta);
- void setIoHandle(audio_io_handle_t ioHandle);
- bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
- audio_devices_t supportedDevices();
- uint32_t latency();
- bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
bool isActive(uint32_t inPastMs = 0) const;
bool isStreamActive(audio_stream_type_t stream,
uint32_t inPastMs = 0,
@@ -52,32 +64,70 @@
virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig = NULL) const;
- virtual sp<AudioPort> getAudioPort() const { return mProfile; }
- void toAudioPort(struct audio_port *port) const;
+ virtual sp<AudioPort> getAudioPort() const { return mPort; }
+ virtual void toAudioPort(struct audio_port *port) const;
audio_module_handle_t getModuleHandle() const;
- audio_port_handle_t mId;
- audio_io_handle_t mIoHandle; // output handle
- uint32_t mLatency; //
- audio_output_flags_t mFlags; //
+ sp<AudioPort> mPort;
audio_devices_t mDevice; // current device this output is routed to
- AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
audio_patch_handle_t mPatchHandle;
uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output
nsecs_t mStopTime[AUDIO_STREAM_CNT];
- sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output
- sp<AudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output
- float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume
+ float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume in dB
int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter
- const sp<IOProfile> mProfile; // I/O profile this output derives from
bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible
// device selection. See checkDeviceMuteStrategies()
- uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
+ AudioPolicyClientInterface *mClientInterface;
+
+protected:
+ audio_port_handle_t mId;
};
-class AudioOutputCollection :
- public DefaultKeyedVector< audio_io_handle_t, sp<AudioOutputDescriptor> >
+// Audio output driven by a software mixer in audio flinger.
+class SwAudioOutputDescriptor: public AudioOutputDescriptor
+{
+public:
+ SwAudioOutputDescriptor(const sp<IOProfile>& profile,
+ AudioPolicyClientInterface *clientInterface);
+ virtual ~SwAudioOutputDescriptor() {}
+
+ status_t dump(int fd);
+
+ void setIoHandle(audio_io_handle_t ioHandle);
+
+ virtual audio_devices_t device() const;
+ virtual bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
+ virtual audio_devices_t supportedDevices();
+ virtual uint32_t latency();
+ virtual bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
+ virtual bool isFixedVolume(audio_devices_t device);
+ virtual sp<AudioOutputDescriptor> subOutput1() { return mOutput1; }
+ virtual sp<AudioOutputDescriptor> subOutput2() { return mOutput2; }
+ virtual void changeRefCount(audio_stream_type_t stream, int delta);
+ virtual bool setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t delayMs,
+ bool force);
+
+ virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
+ virtual void toAudioPort(struct audio_port *port) const;
+
+ const sp<IOProfile> mProfile; // I/O profile this output derives from
+ audio_io_handle_t mIoHandle; // output handle
+ uint32_t mLatency; //
+ audio_output_flags_t mFlags; //
+ AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
+ sp<SwAudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output
+ sp<SwAudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output
+ uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
+ uint32_t mGlobalRefCount; // non-stream-specific ref count
+};
+
+class SwAudioOutputCollection :
+ public DefaultKeyedVector< audio_io_handle_t, sp<SwAudioOutputDescriptor> >
{
public:
bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
@@ -96,9 +146,9 @@
*/
audio_io_handle_t getA2dpOutput() const;
- sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
+ sp<SwAudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
- sp<AudioOutputDescriptor> getPrimaryOutput() const;
+ sp<SwAudioOutputDescriptor> getPrimaryOutput() const;
/**
* return true if any output is playing anything besides the stream to ignore
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 988aed6..d51f4e1 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -24,7 +24,7 @@
namespace android {
-class AudioOutputDescriptor;
+class SwAudioOutputDescriptor;
/**
* custom mix entry in mPolicyMixes
@@ -33,19 +33,19 @@
public:
AudioPolicyMix() {}
- const sp<AudioOutputDescriptor> &getOutput() const;
+ const sp<SwAudioOutputDescriptor> &getOutput() const;
- void setOutput(sp<AudioOutputDescriptor> &output);
+ void setOutput(sp<SwAudioOutputDescriptor> &output);
void clearOutput();
- android::AudioMix &getMix();
+ android::AudioMix *getMix();
void setMix(AudioMix &mix);
private:
AudioMix mMix; // Audio policy mix descriptor
- sp<AudioOutputDescriptor> mOutput; // Corresponding output stream
+ sp<SwAudioOutputDescriptor> mOutput; // Corresponding output stream
};
@@ -58,24 +58,24 @@
status_t unregisterMix(String8 address);
- void closeOutput(sp<AudioOutputDescriptor> &desc);
+ void closeOutput(sp<SwAudioOutputDescriptor> &desc);
/**
* Try to find an output descriptor for the given attributes.
*
- * @param[in] attributes to consider for the research of output descriptor.
+ * @param[in] attributes to consider fowr the research of output descriptor.
* @param[out] desc to return if an output could be found.
*
* @return NO_ERROR if an output was found for the given attribute (in this case, the
* descriptor output param is initialized), error code otherwise.
*/
- status_t getOutputForAttr(audio_attributes_t attributes, sp<AudioOutputDescriptor> &desc);
+ status_t getOutputForAttr(audio_attributes_t attributes, sp<SwAudioOutputDescriptor> &desc);
audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource,
audio_devices_t availableDeviceTypes,
AudioMix **policyMix);
- status_t getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix);
+ status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix);
};
}; // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index 4f7f2bc..82e2c43 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -32,13 +32,11 @@
{
public:
AudioPort(const String8& name, audio_port_type_t type,
- audio_port_role_t role, const sp<HwModule>& module);
+ audio_port_role_t role);
virtual ~AudioPort() {}
- audio_port_handle_t getHandle() { return mId; }
-
- void attach(const sp<HwModule>& module);
- bool isAttached() { return mId != 0; }
+ virtual void attach(const sp<HwModule>& module);
+ bool isAttached() { return mModule != 0; }
static audio_port_handle_t getNextUniqueId();
@@ -64,8 +62,12 @@
// searches for an exact match
status_t checkExactChannelMask(audio_channel_mask_t channelMask) const;
// searches for a compatible match, currently implemented for input channel masks only
- status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const;
- status_t checkFormat(audio_format_t format) const;
+ status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask) const;
+
+ status_t checkExactFormat(audio_format_t format) const;
+ // searches for a compatible match, currently implemented for input formats only
+ status_t checkCompatibleFormat(audio_format_t format, audio_format_t *updatedFormat) const;
status_t checkGain(const struct audio_gain_config *gainConfig, int index) const;
uint32_t pickSamplingRate() const;
@@ -73,11 +75,17 @@
audio_format_t pickFormat() const;
static const audio_format_t sPcmFormatCompareTable[];
+ static int compareFormats(const audio_format_t *format1, const audio_format_t *format2) {
+ return compareFormats(*format1, *format2);
+ }
static int compareFormats(audio_format_t format1, audio_format_t format2);
audio_module_handle_t getModuleHandle() const;
+ uint32_t getModuleVersion() const;
+ const char *getModuleName() const;
void dump(int fd, int spaces) const;
+ void log(const char* indent) const;
String8 mName;
audio_port_type_t mType;
@@ -94,13 +102,6 @@
uint32_t mFlags; // attribute flags (e.g primary output,
// direct output...).
-
-protected:
- //TODO - clarify the role of mId in this case, both an "attached" indicator
- // and a unique ID for identifying a port to the (upcoming) selection API,
- // and its relationship to the mId in AudioOutputDescriptor and AudioInputDescriptor.
- audio_port_handle_t mId;
-
private:
static volatile int32_t mNextUniqueId;
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
index 14a7d36..0b08430 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
@@ -38,12 +38,14 @@
uint32_t value;
};
-#define STRING_TO_ENUM(string) { #string, string }
+// TODO: move to a separate file. Should be in sync with audio.h.
+#define STRING_TO_ENUM(string) { #string, (uint32_t)string } // uint32_t cast removes warning
+#define NAME_TO_ENUM(name, value) { name, value }
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
-const StringToEnum sDeviceNameToEnumTable[] = {
+const StringToEnum sDeviceTypeToEnumTable[] = {
STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE),
@@ -94,6 +96,57 @@
STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK),
};
+const StringToEnum sDeviceNameToEnumTable[] = {
+ NAME_TO_ENUM("Earpiece", AUDIO_DEVICE_OUT_EARPIECE),
+ NAME_TO_ENUM("Speaker", AUDIO_DEVICE_OUT_SPEAKER),
+ NAME_TO_ENUM("Speaker Protected", AUDIO_DEVICE_OUT_SPEAKER_SAFE),
+ NAME_TO_ENUM("Wired Headset", AUDIO_DEVICE_OUT_WIRED_HEADSET),
+ NAME_TO_ENUM("Wired Headphones", AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
+ NAME_TO_ENUM("BT SCO", AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
+ NAME_TO_ENUM("BT SCO Headset", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET),
+ NAME_TO_ENUM("BT SCO Car Kit", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
+ NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_SCO),
+ NAME_TO_ENUM("BT A2DP Out", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP),
+ NAME_TO_ENUM("BT A2DP Headphones", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES),
+ NAME_TO_ENUM("BT A2DP Speaker", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
+ NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_A2DP),
+ NAME_TO_ENUM("HDMI Out", AUDIO_DEVICE_OUT_AUX_DIGITAL),
+ NAME_TO_ENUM("HDMI Out", AUDIO_DEVICE_OUT_HDMI),
+ NAME_TO_ENUM("Analog Dock Out", AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
+ NAME_TO_ENUM("Digital Dock Out", AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
+ NAME_TO_ENUM("USB Host Out", AUDIO_DEVICE_OUT_USB_ACCESSORY),
+ NAME_TO_ENUM("USB Device Out", AUDIO_DEVICE_OUT_USB_DEVICE),
+ NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_USB),
+ NAME_TO_ENUM("Reroute Submix Out", AUDIO_DEVICE_OUT_REMOTE_SUBMIX),
+ NAME_TO_ENUM("Telephony Tx", AUDIO_DEVICE_OUT_TELEPHONY_TX),
+ NAME_TO_ENUM("Line Out", AUDIO_DEVICE_OUT_LINE),
+ NAME_TO_ENUM("HDMI ARC Out", AUDIO_DEVICE_OUT_HDMI_ARC),
+ NAME_TO_ENUM("S/PDIF Out", AUDIO_DEVICE_OUT_SPDIF),
+ NAME_TO_ENUM("FM transceiver Out", AUDIO_DEVICE_OUT_FM),
+ NAME_TO_ENUM("Aux Line Out", AUDIO_DEVICE_OUT_AUX_LINE),
+ NAME_TO_ENUM("Ambient Mic", AUDIO_DEVICE_IN_AMBIENT),
+ NAME_TO_ENUM("Built-In Mic", AUDIO_DEVICE_IN_BUILTIN_MIC),
+ NAME_TO_ENUM("BT SCO Headset Mic", AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
+ NAME_TO_ENUM("", AUDIO_DEVICE_IN_ALL_SCO),
+ NAME_TO_ENUM("Wired Headset Mic", AUDIO_DEVICE_IN_WIRED_HEADSET),
+ NAME_TO_ENUM("HDMI In", AUDIO_DEVICE_IN_AUX_DIGITAL),
+ NAME_TO_ENUM("HDMI In", AUDIO_DEVICE_IN_HDMI),
+ NAME_TO_ENUM("Telephony Rx", AUDIO_DEVICE_IN_TELEPHONY_RX),
+ NAME_TO_ENUM("Telephony Rx", AUDIO_DEVICE_IN_VOICE_CALL),
+ NAME_TO_ENUM("Built-In Back Mic", AUDIO_DEVICE_IN_BACK_MIC),
+ NAME_TO_ENUM("Reroute Submix In", AUDIO_DEVICE_IN_REMOTE_SUBMIX),
+ NAME_TO_ENUM("Analog Dock In", AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
+ NAME_TO_ENUM("Digital Dock In", AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
+ NAME_TO_ENUM("USB Host In", AUDIO_DEVICE_IN_USB_ACCESSORY),
+ NAME_TO_ENUM("USB Device In", AUDIO_DEVICE_IN_USB_DEVICE),
+ NAME_TO_ENUM("FM Tuner In", AUDIO_DEVICE_IN_FM_TUNER),
+ NAME_TO_ENUM("TV Tuner In", AUDIO_DEVICE_IN_TV_TUNER),
+ NAME_TO_ENUM("Line In", AUDIO_DEVICE_IN_LINE),
+ NAME_TO_ENUM("S/PDIF In", AUDIO_DEVICE_IN_SPDIF),
+ NAME_TO_ENUM("BT A2DP In", AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
+ NAME_TO_ENUM("Loopback In", AUDIO_DEVICE_IN_LOOPBACK),
+};
+
const StringToEnum sOutputFlagNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT),
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY),
@@ -152,6 +205,17 @@
STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
};
+const StringToEnum sIndexChannelsNameToEnumTable[] = {
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_1),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_2),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_3),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_4),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_5),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_6),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_7),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_8),
+};
+
const StringToEnum sGainModeNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index d15f6b4..d1a2f4f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -29,7 +29,7 @@
class DeviceDescriptor : public AudioPort, public AudioPortConfig
{
public:
- DeviceDescriptor(const String8& name, audio_devices_t type);
+ DeviceDescriptor(audio_devices_t type);
virtual ~DeviceDescriptor() {}
@@ -41,19 +41,21 @@
const struct audio_port_config *srcConfig = NULL) const;
// AudioPort
+ virtual void attach(const sp<HwModule>& module);
virtual void loadGains(cnode *root);
virtual void toAudioPort(struct audio_port *port) const;
+ audio_port_handle_t getId() const;
audio_devices_t type() const { return mDeviceType; }
status_t dump(int fd, int spaces, int index) const;
+ void log() const;
+ String8 mTag;
String8 mAddress;
- audio_port_handle_t mId;
-
- static String8 emptyNameStr;
private:
- audio_devices_t mDeviceType;
+ audio_devices_t mDeviceType;
+ audio_port_handle_t mId;
friend class DeviceVector;
};
@@ -70,12 +72,12 @@
audio_devices_t types() const { return mDeviceTypes; }
void loadDevicesFromType(audio_devices_t types);
- void loadDevicesFromName(char *name, const DeviceVector& declaredDevices);
+ void loadDevicesFromTag(char *tag, const DeviceVector& declaredDevices);
sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const;
DeviceVector getDevicesFromType(audio_devices_t types) const;
sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const;
- sp<DeviceDescriptor> getDeviceFromName(const String8& name) const;
+ sp<DeviceDescriptor> getDeviceFromTag(const String8& tag) const;
DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) const;
audio_devices_t getDevicesFromHwModule(audio_module_handle_t moduleHandle) const;
diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
index 095e759..ab6fcc1 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
@@ -33,7 +33,7 @@
class IOProfile : public AudioPort
{
public:
- IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module);
+ IOProfile(const String8& name, audio_port_role_t role);
virtual ~IOProfile();
// This method is used for both output and input.
@@ -45,7 +45,9 @@
uint32_t samplingRate,
uint32_t *updatedSamplingRate,
audio_format_t format,
+ audio_format_t *updatedFormat,
audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask,
uint32_t flags) const;
void dump(int fd);
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index fa66728..937160b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -27,9 +27,9 @@
namespace android {
AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile)
- : mId(0), mIoHandle(0),
+ : mIoHandle(0),
mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0), mRefCount(0),
- mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false)
+ mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false), mId(0)
{
if (profile != NULL) {
mSamplingRate = profile->pickSamplingRate();
@@ -49,9 +49,17 @@
audio_module_handle_t AudioInputDescriptor::getModuleHandle() const
{
+ if (mProfile == 0) {
+ return 0;
+ }
return mProfile->getModuleHandle();
}
+audio_port_handle_t AudioInputDescriptor::getId() const
+{
+ return mId;
+}
+
void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig) const
{
@@ -68,7 +76,7 @@
dstConfig->id = mId;
dstConfig->role = AUDIO_PORT_ROLE_SINK;
dstConfig->type = AUDIO_PORT_TYPE_MIX;
- dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+ dstConfig->ext.mix.hw_module = getModuleHandle();
dstConfig->ext.mix.handle = mIoHandle;
dstConfig->ext.mix.usecase.source = mInputSource;
}
@@ -80,7 +88,7 @@
mProfile->toAudioPort(port);
port->id = mId;
toAudioPortConfig(&port->active_config);
- port->ext.mix.hw_module = mProfile->mModule->mHandle;
+ port->ext.mix.hw_module = getModuleHandle();
port->ext.mix.handle = mIoHandle;
port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL;
}
@@ -91,7 +99,7 @@
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, " ID: %d\n", mId);
+ snprintf(buffer, SIZE, " ID: %d\n", getId());
result.append(buffer);
snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
result.append(buffer);
@@ -130,7 +138,7 @@
sp<AudioInputDescriptor> inputDesc = NULL;
for (size_t i = 0; i < size(); i++) {
inputDesc = valueAt(i);
- if (inputDesc->mId == id) {
+ if (inputDesc->getId() == id) {
break;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index cdb5b51..a278375 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -17,9 +17,11 @@
#define LOG_TAG "APM::AudioOutputDescriptor"
//#define LOG_NDEBUG 0
+#include <AudioPolicyInterface.h>
#include "AudioOutputDescriptor.h"
#include "IOProfile.h"
#include "AudioGain.h"
+#include "Volume.h"
#include "HwModule.h"
#include <media/AudioPolicy.h>
@@ -29,11 +31,10 @@
namespace android {
-AudioOutputDescriptor::AudioOutputDescriptor(const sp<IOProfile>& profile)
- : mId(0), mIoHandle(0), mLatency(0),
- mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL),
- mPatchHandle(0),
- mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0)
+AudioOutputDescriptor::AudioOutputDescriptor(const sp<AudioPort>& port,
+ AudioPolicyClientInterface *clientInterface)
+ : mPort(port), mDevice(AUDIO_DEVICE_NONE),
+ mPatchHandle(0), mClientInterface(clientInterface), mId(0)
{
// clear usage count for all stream types
for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
@@ -45,66 +46,50 @@
for (int i = 0; i < NUM_STRATEGIES; i++) {
mStrategyMutedByDevice[i] = false;
}
- if (profile != NULL) {
- mFlags = (audio_output_flags_t)profile->mFlags;
- mSamplingRate = profile->pickSamplingRate();
- mFormat = profile->pickFormat();
- mChannelMask = profile->pickChannelMask();
- if (profile->mGains.size() > 0) {
- profile->mGains[0]->getDefaultConfig(&mGain);
+ if (port != NULL) {
+ mSamplingRate = port->pickSamplingRate();
+ mFormat = port->pickFormat();
+ mChannelMask = port->pickChannelMask();
+ if (port->mGains.size() > 0) {
+ port->mGains[0]->getDefaultConfig(&mGain);
}
}
}
audio_module_handle_t AudioOutputDescriptor::getModuleHandle() const
{
- return mProfile->getModuleHandle();
+ return mPort->getModuleHandle();
+}
+
+audio_port_handle_t AudioOutputDescriptor::getId() const
+{
+ return mId;
}
audio_devices_t AudioOutputDescriptor::device() const
{
- if (isDuplicated()) {
- return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice);
- } else {
- return mDevice;
- }
+ return mDevice;
}
-void AudioOutputDescriptor::setIoHandle(audio_io_handle_t ioHandle)
+audio_devices_t AudioOutputDescriptor::supportedDevices()
{
- mId = AudioPort::getNextUniqueId();
- mIoHandle = ioHandle;
-}
-
-uint32_t AudioOutputDescriptor::latency()
-{
- if (isDuplicated()) {
- return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency;
- } else {
- return mLatency;
- }
+ return mDevice;
}
bool AudioOutputDescriptor::sharesHwModuleWith(
const sp<AudioOutputDescriptor> outputDesc)
{
- if (isDuplicated()) {
- return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc);
- } else if (outputDesc->isDuplicated()){
- return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2);
+ if (outputDesc->isDuplicated()) {
+ return sharesHwModuleWith(outputDesc->subOutput1()) ||
+ sharesHwModuleWith(outputDesc->subOutput2());
} else {
- return (mProfile->mModule == outputDesc->mProfile->mModule);
+ return (getModuleHandle() == outputDesc->getModuleHandle());
}
}
void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
int delta)
{
- // forward usage count change to attached outputs
- if (isDuplicated()) {
- mOutput1->changeRefCount(stream, delta);
- mOutput2->changeRefCount(stream, delta);
- }
if ((delta + (int)mRefCount[stream]) < 0) {
ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d",
delta, stream, mRefCount[stream]);
@@ -115,15 +100,6 @@
ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
}
-audio_devices_t AudioOutputDescriptor::supportedDevices()
-{
- if (isDuplicated()) {
- return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices());
- } else {
- return mProfile->mSupportedDevices.types() ;
- }
-}
-
bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const
{
nsecs_t sysTime = 0;
@@ -160,12 +136,33 @@
return false;
}
+
+bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused)
+{
+ return false;
+}
+
+bool AudioOutputDescriptor::setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device __unused,
+ uint32_t delayMs,
+ bool force)
+{
+ // We actually change the volume if:
+ // - the float value returned by computeVolume() changed
+ // - the force flag is set
+ if (volume != mCurVolume[stream] || force) {
+ ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs);
+ mCurVolume[stream] = volume;
+ return true;
+ }
+ return false;
+}
+
void AudioOutputDescriptor::toAudioPortConfig(
struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig) const
{
- ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle);
-
dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN;
if (srcConfig != NULL) {
@@ -176,22 +173,16 @@
dstConfig->id = mId;
dstConfig->role = AUDIO_PORT_ROLE_SOURCE;
dstConfig->type = AUDIO_PORT_TYPE_MIX;
- dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
- dstConfig->ext.mix.handle = mIoHandle;
+ dstConfig->ext.mix.hw_module = getModuleHandle();
dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
}
void AudioOutputDescriptor::toAudioPort(
struct audio_port *port) const
{
- ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
- mProfile->toAudioPort(port);
+ mPort->toAudioPort(port);
port->id = mId;
- toAudioPortConfig(&port->active_config);
- port->ext.mix.hw_module = mProfile->mModule->mHandle;
- port->ext.mix.handle = mIoHandle;
- port->ext.mix.latency_class =
- mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
+ port->ext.mix.hw_module = getModuleHandle();
}
status_t AudioOutputDescriptor::dump(int fd)
@@ -208,10 +199,6 @@
result.append(buffer);
snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask);
result.append(buffer);
- snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
- result.append(buffer);
- snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
- result.append(buffer);
snprintf(buffer, SIZE, " Devices %08x\n", device());
result.append(buffer);
snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
@@ -226,11 +213,188 @@
return NO_ERROR;
}
-bool AudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+void AudioOutputDescriptor::log(const char* indent)
+{
+ ALOGI("%sID: %d,0x%X, [rt:%d fmt:0x%X ch:0x%X]",
+ indent, mId, mId, mSamplingRate, mFormat, mChannelMask);
+}
+
+// SwAudioOutputDescriptor implementation
+SwAudioOutputDescriptor::SwAudioOutputDescriptor(
+ const sp<IOProfile>& profile, AudioPolicyClientInterface *clientInterface)
+ : AudioOutputDescriptor(profile, clientInterface),
+ mProfile(profile), mIoHandle(0), mLatency(0),
+ mFlags((audio_output_flags_t)0), mPolicyMix(NULL),
+ mOutput1(0), mOutput2(0), mDirectOpenCount(0), mGlobalRefCount(0)
+{
+ if (profile != NULL) {
+ mFlags = (audio_output_flags_t)profile->mFlags;
+ }
+}
+
+void SwAudioOutputDescriptor::setIoHandle(audio_io_handle_t ioHandle)
+{
+ mId = AudioPort::getNextUniqueId();
+ mIoHandle = ioHandle;
+}
+
+
+status_t SwAudioOutputDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ AudioOutputDescriptor::dump(fd);
+
+ return NO_ERROR;
+}
+
+audio_devices_t SwAudioOutputDescriptor::device() const
+{
+ if (isDuplicated()) {
+ return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice);
+ } else {
+ return mDevice;
+ }
+}
+
+bool SwAudioOutputDescriptor::sharesHwModuleWith(
+ const sp<AudioOutputDescriptor> outputDesc)
+{
+ if (isDuplicated()) {
+ return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc);
+ } else if (outputDesc->isDuplicated()){
+ return sharesHwModuleWith(outputDesc->subOutput1()) ||
+ sharesHwModuleWith(outputDesc->subOutput2());
+ } else {
+ return AudioOutputDescriptor::sharesHwModuleWith(outputDesc);
+ }
+}
+
+audio_devices_t SwAudioOutputDescriptor::supportedDevices()
+{
+ if (isDuplicated()) {
+ return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices());
+ } else {
+ return mProfile->mSupportedDevices.types() ;
+ }
+}
+
+uint32_t SwAudioOutputDescriptor::latency()
+{
+ if (isDuplicated()) {
+ return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency;
+ } else {
+ return mLatency;
+ }
+}
+
+void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
+ int delta)
+{
+ // forward usage count change to attached outputs
+ if (isDuplicated()) {
+ mOutput1->changeRefCount(stream, delta);
+ mOutput2->changeRefCount(stream, delta);
+ }
+ AudioOutputDescriptor::changeRefCount(stream, delta);
+
+ // handle stream-independent ref count
+ uint32_t oldGlobalRefCount = mGlobalRefCount;
+ if ((delta + (int)mGlobalRefCount) < 0) {
+ ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount);
+ mGlobalRefCount = 0;
+ } else {
+ mGlobalRefCount += delta;
+ }
+ if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) {
+ if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+ {
+ mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId,
+ MIX_STATE_MIXING);
+ }
+
+ } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) {
+ if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+ {
+ mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId,
+ MIX_STATE_IDLE);
+ }
+ }
+}
+
+
+bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device)
+{
+ // unit gain if rerouting to external policy
+ if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) {
+ if (mPolicyMix != NULL) {
+ ALOGV("max gain when rerouting for output=%d", mIoHandle);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwAudioOutputDescriptor::toAudioPortConfig(
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+
+ ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle);
+ AudioOutputDescriptor::toAudioPortConfig(dstConfig, srcConfig);
+
+ dstConfig->ext.mix.handle = mIoHandle;
+}
+
+void SwAudioOutputDescriptor::toAudioPort(
+ struct audio_port *port) const
+{
+ ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
+
+ AudioOutputDescriptor::toAudioPort(port);
+
+ toAudioPortConfig(&port->active_config);
+ port->ext.mix.handle = mIoHandle;
+ port->ext.mix.latency_class =
+ mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
+}
+
+bool SwAudioOutputDescriptor::setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t delayMs,
+ bool force)
+{
+ bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);
+
+ if (changed) {
+ // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
+ // enabled
+ float volume = Volume::DbToAmpl(mCurVolume[stream]);
+ if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
+ mClientInterface->setStreamVolume(
+ AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs);
+ }
+ mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
+ }
+ return changed;
+}
+
+// SwAudioOutputCollection implementation
+
+bool SwAudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < this->size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = this->valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
return true;
}
@@ -238,12 +402,12 @@
return false;
}
-bool AudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
+bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
uint32_t inPastMs) const
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) &&
outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
// do not consider re routing (when the output is going to a dynamic policy)
@@ -256,10 +420,10 @@
return false;
}
-audio_io_handle_t AudioOutputCollection::getA2dpOutput() const
+audio_io_handle_t SwAudioOutputCollection::getA2dpOutput() const
{
for (size_t i = 0; i < size(); i++) {
- sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) {
return this->keyAt(i);
}
@@ -267,10 +431,10 @@
return 0;
}
-sp<AudioOutputDescriptor> AudioOutputCollection::getPrimaryOutput() const
+sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getPrimaryOutput() const
{
for (size_t i = 0; i < size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
return outputDesc;
}
@@ -278,26 +442,26 @@
return NULL;
}
-sp<AudioOutputDescriptor> AudioOutputCollection::getOutputFromId(audio_port_handle_t id) const
+sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getOutputFromId(audio_port_handle_t id) const
{
- sp<AudioOutputDescriptor> outputDesc = NULL;
+ sp<SwAudioOutputDescriptor> outputDesc = NULL;
for (size_t i = 0; i < size(); i++) {
outputDesc = valueAt(i);
- if (outputDesc->mId == id) {
+ if (outputDesc->getId() == id) {
break;
}
}
return outputDesc;
}
-bool AudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
+bool SwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
{
for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) {
if (s == (size_t) streamToIgnore) {
continue;
}
for (size_t i = 0; i < size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (outputDesc->mRefCount[s] != 0) {
return true;
}
@@ -306,15 +470,15 @@
return false;
}
-audio_devices_t AudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const
+audio_devices_t SwAudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const
{
- sp<AudioOutputDescriptor> outputDesc = valueFor(handle);
+ sp<SwAudioOutputDescriptor> outputDesc = valueFor(handle);
audio_devices_t devices = outputDesc->mProfile->mSupportedDevices.types();
return devices;
}
-status_t AudioOutputCollection::dump(int fd) const
+status_t SwAudioOutputCollection::dump(int fd) const
{
const size_t SIZE = 256;
char buffer[SIZE];
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
index 3a317fa..a06d867 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
@@ -54,8 +54,8 @@
for (size_t i = 0; i < mPatch.num_sources; i++) {
if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) {
snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
- mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
mPatch.sources[i].ext.device.type));
} else {
snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
@@ -68,8 +68,8 @@
for (size_t i = 0; i < mPatch.num_sinks; i++) {
if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) {
snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
- mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
mPatch.sinks[i].ext.device.type));
} else {
snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 84a53ebd..6f1998c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -26,12 +26,12 @@
namespace android {
-void AudioPolicyMix::setOutput(sp<AudioOutputDescriptor> &output)
+void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
{
mOutput = output;
}
-const sp<AudioOutputDescriptor> &AudioPolicyMix::getOutput() const
+const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
{
return mOutput;
}
@@ -46,9 +46,9 @@
mMix = mix;
}
-android::AudioMix &AudioPolicyMix::getMix()
+android::AudioMix *AudioPolicyMix::getMix()
{
- return mMix;
+ return &mMix;
}
status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix)
@@ -88,7 +88,7 @@
return NO_ERROR;
}
-void AudioPolicyMixCollection::closeOutput(sp<AudioOutputDescriptor> &desc)
+void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
{
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
@@ -99,40 +99,40 @@
}
status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes,
- sp<AudioOutputDescriptor> &desc)
+ sp<SwAudioOutputDescriptor> &desc)
{
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
- AudioMix mix = policyMix->getMix();
+ AudioMix *mix = policyMix->getMix();
- if (mix.mMixType == MIX_TYPE_PLAYERS) {
- for (size_t j = 0; j < mix.mCriteria.size(); j++) {
- if ((RULE_MATCH_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mUsage == attributes.usage) ||
- (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mUsage != attributes.usage)) {
+ if (mix->mMixType == MIX_TYPE_PLAYERS) {
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ if ((RULE_MATCH_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mUsage == attributes.usage) ||
+ (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mUsage != attributes.usage)) {
desc = policyMix->getOutput();
break;
}
if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
strncmp(attributes.tags + strlen("addr="),
- mix.mRegistrationId.string(),
+ mix->mRegistrationId.string(),
AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
desc = policyMix->getOutput();
break;
}
}
- } else if (mix.mMixType == MIX_TYPE_RECORDERS) {
+ } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
strncmp(attributes.tags + strlen("addr="),
- mix.mRegistrationId.string(),
+ mix->mRegistrationId.string(),
AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
desc = policyMix->getOutput();
}
}
if (desc != 0) {
- desc->mPolicyMix = &mix;
+ desc->mPolicyMix = mix;
return NO_ERROR;
}
}
@@ -144,19 +144,19 @@
AudioMix **policyMix)
{
for (size_t i = 0; i < size(); i++) {
- AudioMix mix = valueAt(i)->getMix();
+ AudioMix *mix = valueAt(i)->getMix();
- if (mix.mMixType != MIX_TYPE_RECORDERS) {
+ if (mix->mMixType != MIX_TYPE_RECORDERS) {
continue;
}
- for (size_t j = 0; j < mix.mCriteria.size(); j++) {
- if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mSource == inputSource) ||
- (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mSource != inputSource)) {
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mSource == inputSource) ||
+ (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mSource != inputSource)) {
if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
if (policyMix != NULL) {
- *policyMix = &mix;
+ *policyMix = mix;
}
return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
}
@@ -167,7 +167,7 @@
return AUDIO_DEVICE_NONE;
}
-status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix)
+status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix)
{
if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
return BAD_VALUE;
@@ -176,17 +176,17 @@
ssize_t index = indexOfKey(address);
if (index < 0) {
- ALOGW("getInputForAttr() no policy for address %s", address.string());
+ ALOGW("getInputMixForAttr() no policy for address %s", address.string());
return BAD_VALUE;
}
sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
- AudioMix mix = audioPolicyMix->getMix();
+ AudioMix *mix = audioPolicyMix->getMix();
- if (mix.mMixType != MIX_TYPE_PLAYERS) {
- ALOGW("getInputForAttr() bad policy mix type for address %s", address.string());
+ if (mix->mMixType != MIX_TYPE_PLAYERS) {
+ ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
return BAD_VALUE;
}
- policyMix = &mix;
+ *policyMix = mix;
return NO_ERROR;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index 46a119e..afcd073 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "APM::AudioPort"
//#define LOG_NDEBUG 0
-
+#include <media/AudioResamplerPublic.h>
#include "AudioPort.h"
#include "HwModule.h"
#include "AudioGain.h"
@@ -31,8 +31,8 @@
// --- AudioPort class implementation
AudioPort::AudioPort(const String8& name, audio_port_type_t type,
- audio_port_role_t role, const sp<HwModule>& module) :
- mName(name), mType(type), mRole(role), mModule(module), mFlags(0), mId(0)
+ audio_port_role_t role) :
+ mName(name), mType(type), mRole(role), mFlags(0)
{
mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
@@ -40,7 +40,6 @@
void AudioPort::attach(const sp<HwModule>& module)
{
- mId = getNextUniqueId();
mModule = module;
}
@@ -51,9 +50,28 @@
audio_module_handle_t AudioPort::getModuleHandle() const
{
+ if (mModule == 0) {
+ return 0;
+ }
return mModule->mHandle;
}
+uint32_t AudioPort::getModuleVersion() const
+{
+ if (mModule == 0) {
+ return 0;
+ }
+ return mModule->mHalVersion;
+}
+
+const char *AudioPort::getModuleName() const
+{
+ if (mModule == 0) {
+ return "";
+ }
+ return mModule->mName;
+}
+
void AudioPort::toAudioPort(struct audio_port *port) const
{
port->role = mRole;
@@ -128,7 +146,7 @@
break;
}
}
- if (!hasFormat) { // never import a channel mask twice
+ if (!hasFormat) { // never import a format twice
mFormats.add(format);
}
}
@@ -198,6 +216,12 @@
}
str = strtok(NULL, "|");
}
+ // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry.
+ // TODO: compareFormats could be a lambda to convert between pointer-to-format to format:
+ // [](const audio_format_t *format1, const audio_format_t *format2) {
+ // return compareFormats(*format1, *format2);
+ // }
+ mFormats.sort(compareFormats);
}
void AudioPort::loadInChannels(char *name)
@@ -216,8 +240,14 @@
(audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable,
ARRAY_SIZE(sInChannelsNameToEnumTable),
str);
+ if (channelMask == 0) { // if not found, check the channel index table
+ channelMask = (audio_channel_mask_t)
+ ConfigParsingUtils::stringToEnum(sIndexChannelsNameToEnumTable,
+ ARRAY_SIZE(sIndexChannelsNameToEnumTable),
+ str);
+ }
if (channelMask != 0) {
- ALOGV("loadInChannels() adding channelMask %04x", channelMask);
+ ALOGV("loadInChannels() adding channelMask %#x", channelMask);
mChannelMasks.add(channelMask);
}
str = strtok(NULL, "|");
@@ -242,6 +272,12 @@
(audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable,
ARRAY_SIZE(sOutChannelsNameToEnumTable),
str);
+ if (channelMask == 0) { // if not found, check the channel index table
+ channelMask = (audio_channel_mask_t)
+ ConfigParsingUtils::stringToEnum(sIndexChannelsNameToEnumTable,
+ ARRAY_SIZE(sIndexChannelsNameToEnumTable),
+ str);
+ }
if (channelMask != 0) {
mChannelMasks.add(channelMask);
}
@@ -340,6 +376,9 @@
uint32_t *updatedSamplingRate) const
{
if (mSamplingRates.isEmpty()) {
+ if (updatedSamplingRate != NULL) {
+ *updatedSamplingRate = samplingRate;
+ }
return NO_ERROR;
}
@@ -369,16 +408,11 @@
}
}
}
- // This uses hard-coded knowledge about AudioFlinger resampling ratios.
- // TODO Move these assumptions out.
- static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs
- static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur
- // due to approximation by an int32_t of the
- // phase increments
+
// Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum.
if (minAbove >= 0) {
candidate = mSamplingRates[minAbove];
- if (candidate / kMaxDownSampleRatio <= samplingRate) {
+ if (candidate / AUDIO_RESAMPLER_DOWN_RATIO_MAX <= samplingRate) {
if (updatedSamplingRate != NULL) {
*updatedSamplingRate = candidate;
}
@@ -388,7 +422,7 @@
// But if we have to up-sample from a lower sampling rate, that's OK.
if (maxBelow >= 0) {
candidate = mSamplingRates[maxBelow];
- if (candidate * kMaxUpSampleRatio >= samplingRate) {
+ if (candidate * AUDIO_RESAMPLER_UP_RATIO_MAX >= samplingRate) {
if (updatedSamplingRate != NULL) {
*updatedSamplingRate = candidate;
}
@@ -413,35 +447,101 @@
return BAD_VALUE;
}
-status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask)
- const
+status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask) const
{
if (mChannelMasks.isEmpty()) {
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = channelMask;
+ }
return NO_ERROR;
}
const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK;
+ const bool isIndex = audio_channel_mask_get_representation(channelMask)
+ == AUDIO_CHANNEL_REPRESENTATION_INDEX;
+ int bestMatch = 0;
for (size_t i = 0; i < mChannelMasks.size(); i ++) {
- // FIXME Does not handle multi-channel automatic conversions yet
audio_channel_mask_t supported = mChannelMasks[i];
if (supported == channelMask) {
+ // Exact matches always taken.
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = channelMask;
+ }
return NO_ERROR;
}
- if (isRecordThread) {
- // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix.
- // FIXME Abstract this out to a table.
- if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO)
- && channelMask == AUDIO_CHANNEL_IN_MONO) ||
- (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
- || channelMask == AUDIO_CHANNEL_IN_STEREO))) {
- return NO_ERROR;
+
+ // AUDIO_CHANNEL_NONE (value: 0) is used for dynamic channel support
+ if (isRecordThread && supported != AUDIO_CHANNEL_NONE) {
+ // Approximate (best) match:
+ // The match score measures how well the supported channel mask matches the
+ // desired mask, where increasing-is-better.
+ //
+ // TODO: Some tweaks may be needed.
+ // Should be a static function of the data processing library.
+ //
+ // In priority:
+ // match score = 1000 if legacy channel conversion equivalent (always prefer this)
+ // OR
+ // match score += 100 if the channel mask representations match
+ // match score += number of channels matched.
+ //
+ // If there are no matched channels, the mask may still be accepted
+ // but the playback or record will be silent.
+ const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
+ == AUDIO_CHANNEL_REPRESENTATION_INDEX);
+ int match;
+ if (isIndex && isSupportedIndex) {
+ // index equivalence
+ match = 100 + __builtin_popcount(
+ audio_channel_mask_get_bits(channelMask)
+ & audio_channel_mask_get_bits(supported));
+ } else if (isIndex && !isSupportedIndex) {
+ const uint32_t equivalentBits =
+ (1 << audio_channel_count_from_in_mask(supported)) - 1 ;
+ match = __builtin_popcount(
+ audio_channel_mask_get_bits(channelMask) & equivalentBits);
+ } else if (!isIndex && isSupportedIndex) {
+ const uint32_t equivalentBits =
+ (1 << audio_channel_count_from_in_mask(channelMask)) - 1;
+ match = __builtin_popcount(
+ equivalentBits & audio_channel_mask_get_bits(supported));
+ } else {
+ // positional equivalence
+ match = 100 + __builtin_popcount(
+ audio_channel_mask_get_bits(channelMask)
+ & audio_channel_mask_get_bits(supported));
+ switch (supported) {
+ case AUDIO_CHANNEL_IN_FRONT_BACK:
+ case AUDIO_CHANNEL_IN_STEREO:
+ if (channelMask == AUDIO_CHANNEL_IN_MONO) {
+ match = 1000;
+ }
+ break;
+ case AUDIO_CHANNEL_IN_MONO:
+ if (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
+ || channelMask == AUDIO_CHANNEL_IN_STEREO) {
+ match = 1000;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (match > bestMatch) {
+ bestMatch = match;
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = supported;
+ } else {
+ return NO_ERROR; // any match will do in this case.
+ }
}
}
}
- return BAD_VALUE;
+ return bestMatch > 0 ? NO_ERROR : BAD_VALUE;
}
-status_t AudioPort::checkFormat(audio_format_t format) const
+status_t AudioPort::checkExactFormat(audio_format_t format) const
{
if (mFormats.isEmpty()) {
return NO_ERROR;
@@ -455,6 +555,35 @@
return BAD_VALUE;
}
+status_t AudioPort::checkCompatibleFormat(audio_format_t format, audio_format_t *updatedFormat)
+ const
+{
+ if (mFormats.isEmpty()) {
+ if (updatedFormat != NULL) {
+ *updatedFormat = format;
+ }
+ return NO_ERROR;
+ }
+
+ const bool checkInexact = // when port is input and format is linear pcm
+ mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK
+ && audio_is_linear_pcm(format);
+
+ // iterate from best format to worst format (reverse order)
+ for (ssize_t i = mFormats.size() - 1; i >= 0 ; --i) {
+ if (mFormats[i] == format ||
+ (checkInexact
+ && mFormats[i] != AUDIO_FORMAT_DEFAULT
+ && audio_is_linear_pcm(mFormats[i]))) {
+ // for inexact checks we take the first linear pcm format due to sorting.
+ if (updatedFormat != NULL) {
+ *updatedFormat = mFormats[i];
+ }
+ return NO_ERROR;
+ }
+ }
+ return BAD_VALUE;
+}
uint32_t AudioPort::pickSamplingRate() const
{
@@ -482,9 +611,13 @@
// For mixed output and inputs, use max mixer sampling rates. Do not
// limit sampling rate otherwise
+ // For inputs, also see checkCompatibleSamplingRate().
if (mType != AUDIO_PORT_TYPE_MIX) {
maxRate = UINT_MAX;
}
+ // TODO: should mSamplingRates[] be ordered in terms of our preference
+ // and we return the first (and hence most preferred) match? This is of concern if
+ // we want to choose 96kHz over 192kHz for USB driver stability or resource constraints.
for (size_t i = 0; i < mSamplingRates.size(); i ++) {
if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) {
samplingRate = mSamplingRates[i];
@@ -629,7 +762,7 @@
char buffer[SIZE];
String8 result;
- if (mName.size() != 0) {
+ if (mName.length() != 0) {
snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string());
result.append(buffer);
}
@@ -673,10 +806,15 @@
const char *formatStr = ConfigParsingUtils::enumToString(sFormatNameToEnumTable,
ARRAY_SIZE(sFormatNameToEnumTable),
mFormats[i]);
- if (i == 0 && strcmp(formatStr, "") == 0) {
+ const bool isEmptyStr = formatStr[0] == 0;
+ if (i == 0 && isEmptyStr) {
snprintf(buffer, SIZE, "Dynamic");
} else {
- snprintf(buffer, SIZE, "%s", formatStr);
+ if (isEmptyStr) {
+ snprintf(buffer, SIZE, "%#x", mFormats[i]);
+ } else {
+ snprintf(buffer, SIZE, "%s", formatStr);
+ }
}
result.append(buffer);
result.append(i == (mFormats.size() - 1) ? "" : ", ");
@@ -687,13 +825,16 @@
if (mGains.size() != 0) {
snprintf(buffer, SIZE, "%*s- gains:\n", spaces, "");
write(fd, buffer, strlen(buffer) + 1);
- result.append(buffer);
for (size_t i = 0; i < mGains.size(); i++) {
mGains[i]->dump(fd, spaces + 2, i);
}
}
}
+void AudioPort::log(const char* indent) const
+{
+ ALOGI("%s Port[nm:%s, type:%d, role:%d]", indent, mName.string(), mType, mRole);
+}
// --- AudioPortConfig class implementation
@@ -735,7 +876,7 @@
mChannelMask = config->channel_mask;
}
if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
- status = audioport->checkFormat(config->format);
+ status = audioport->checkExactFormat(config->format);
if (status != NO_ERROR) {
goto exit;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
index fe5bc5f..89ef045 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
@@ -113,8 +113,8 @@
char *devName = strtok(name, "|");
while (devName != NULL) {
if (strlen(devName) != 0) {
- device |= stringToEnum(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ device |= stringToEnum(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
devName);
}
devName = strtok(NULL, "|");
@@ -218,23 +218,23 @@
node = node->first_child;
while (node) {
if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
- availableOutputDevices.loadDevicesFromName((char *)node->value,
+ availableOutputDevices.loadDevicesFromTag((char *)node->value,
declaredDevices);
ALOGV("loadGlobalConfig() Attached Output Devices %08x",
availableOutputDevices.types());
} else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
audio_devices_t device = (audio_devices_t)stringToEnum(
- sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
(char *)node->value);
if (device != AUDIO_DEVICE_NONE) {
- defaultOutputDevice = new DeviceDescriptor(String8("default-output"), device);
+ defaultOutputDevice = new DeviceDescriptor(device);
} else {
ALOGW("loadGlobalConfig() default device not specified");
}
ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type());
} else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
- availableInputDevices.loadDevicesFromName((char *)node->value,
+ availableInputDevices.loadDevicesFromTag((char *)node->value,
declaredDevices);
ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types());
} else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 7df7d75..797077a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -24,24 +24,35 @@
namespace android {
-String8 DeviceDescriptor::emptyNameStr = String8("");
-
-DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) :
- AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
+DeviceDescriptor::DeviceDescriptor(audio_devices_t type) :
+ AudioPort(String8(""), AUDIO_PORT_TYPE_DEVICE,
audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
- AUDIO_PORT_ROLE_SOURCE,
- NULL),
- mAddress(""), mDeviceType(type)
+ AUDIO_PORT_ROLE_SOURCE),
+ mTag(""), mAddress(""), mDeviceType(type), mId(0)
{
}
+audio_port_handle_t DeviceDescriptor::getId() const
+{
+ return mId;
+}
+
+void DeviceDescriptor::attach(const sp<HwModule>& module)
+{
+ AudioPort::attach(module);
+ mId = getNextUniqueId();
+}
+
bool DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const
{
// Devices are considered equal if they:
// - are of the same type (a device type cannot be AUDIO_DEVICE_NONE)
// - have the same address or one device does not specify the address
// - have the same channel mask or one device does not specify the channel mask
+ if (other == 0) {
+ return false;
+ }
return (mDeviceType == other->mDeviceType) &&
(mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) &&
(mChannelMask == 0 || other->mChannelMask == 0 ||
@@ -129,21 +140,21 @@
uint32_t i = 31 - __builtin_clz(types);
uint32_t type = 1 << i;
types &= ~type;
- add(new DeviceDescriptor(String8("device_type"), type | role_bit));
+ add(new DeviceDescriptor(type | role_bit));
}
}
-void DeviceVector::loadDevicesFromName(char *name,
+void DeviceVector::loadDevicesFromTag(char *tag,
const DeviceVector& declaredDevices)
{
- char *devName = strtok(name, "|");
- while (devName != NULL) {
- if (strlen(devName) != 0) {
- audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
- devName);
+ char *devTag = strtok(tag, "|");
+ while (devTag != NULL) {
+ if (strlen(devTag) != 0) {
+ audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
+ devTag);
if (type != AUDIO_DEVICE_NONE) {
- sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(name), type);
+ sp<DeviceDescriptor> dev = new DeviceDescriptor(type);
if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX ||
type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) {
dev->mAddress = String8("0");
@@ -151,13 +162,13 @@
add(dev);
} else {
sp<DeviceDescriptor> deviceDesc =
- declaredDevices.getDeviceFromName(String8(devName));
+ declaredDevices.getDeviceFromTag(String8(devTag));
if (deviceDesc != 0) {
add(deviceDesc);
}
}
}
- devName = strtok(NULL, "|");
+ devTag = strtok(NULL, "|");
}
}
@@ -183,7 +194,7 @@
{
sp<DeviceDescriptor> device;
for (size_t i = 0; i < size(); i++) {
- if (itemAt(i)->getHandle() == id) {
+ if (itemAt(i)->getId() == id) {
device = itemAt(i);
break;
}
@@ -223,11 +234,11 @@
return devices;
}
-sp<DeviceDescriptor> DeviceVector::getDeviceFromName(const String8& name) const
+sp<DeviceDescriptor> DeviceVector::getDeviceFromTag(const String8& tag) const
{
sp<DeviceDescriptor> device;
for (size_t i = 0; i < size(); i++) {
- if (itemAt(i)->mName == name) {
+ if (itemAt(i)->mTag == tag) {
device = itemAt(i);
break;
}
@@ -303,8 +314,8 @@
result.append(buffer);
}
snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "",
- ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
mDeviceType));
result.append(buffer);
if (mAddress.size() != 0) {
@@ -317,4 +328,16 @@
return NO_ERROR;
}
+void DeviceDescriptor::log() const
+{
+ ALOGI("Device id:%d type:0x%X:%s, addr:%s",
+ mId,
+ mDeviceType,
+ ConfigParsingUtils::enumToString(
+ sDeviceNameToEnumTable, ARRAY_SIZE(sDeviceNameToEnumTable), mDeviceType),
+ mAddress.string());
+
+ AudioPort::log(" ");
+}
+
}; // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index 0097d69..7e2050b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -48,7 +48,7 @@
{
cnode *node = root->first_child;
- sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this);
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK);
while (node) {
if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
@@ -58,7 +58,7 @@
} else if (strcmp(node->name, CHANNELS_TAG) == 0) {
profile->loadInChannels((char *)node->value);
} else if (strcmp(node->name, DEVICES_TAG) == 0) {
- profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+ profile->mSupportedDevices.loadDevicesFromTag((char *)node->value,
mDeclaredDevices);
} else if (strcmp(node->name, FLAGS_TAG) == 0) {
profile->mFlags = ConfigParsingUtils::parseInputFlagNames((char *)node->value);
@@ -83,6 +83,7 @@
ALOGV("loadInput() adding input Supported Devices %04x",
profile->mSupportedDevices.types());
+ profile->attach(this);
mInputProfiles.add(profile);
return NO_ERROR;
} else {
@@ -94,7 +95,7 @@
{
cnode *node = root->first_child;
- sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this);
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE);
while (node) {
if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
@@ -104,7 +105,7 @@
} else if (strcmp(node->name, CHANNELS_TAG) == 0) {
profile->loadOutChannels((char *)node->value);
} else if (strcmp(node->name, DEVICES_TAG) == 0) {
- profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+ profile->mSupportedDevices.loadDevicesFromTag((char *)node->value,
mDeclaredDevices);
} else if (strcmp(node->name, FLAGS_TAG) == 0) {
profile->mFlags = ConfigParsingUtils::parseOutputFlagNames((char *)node->value);
@@ -128,7 +129,7 @@
ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
profile->mSupportedDevices.types(), profile->mFlags);
-
+ profile->attach(this);
mOutputProfiles.add(profile);
return NO_ERROR;
} else {
@@ -153,8 +154,8 @@
ALOGW("loadDevice() bad type %08x", type);
return BAD_VALUE;
}
- sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
- deviceDesc->mModule = this;
+ sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(type);
+ deviceDesc->mTag = String8(root->name);
node = root->first_child;
while (node) {
@@ -172,8 +173,8 @@
node = node->next;
}
- ALOGV("loadDevice() adding device name %s type %08x address %s",
- deviceDesc->mName.string(), type, deviceDesc->mAddress.string());
+ ALOGV("loadDevice() adding device tag %s type %08x address %s",
+ deviceDesc->mTag.string(), type, deviceDesc->mAddress.string());
mDeclaredDevices.add(deviceDesc);
@@ -183,16 +184,17 @@
status_t HwModule::addOutputProfile(String8 name, const audio_config_t *config,
audio_devices_t device, String8 address)
{
- sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE, this);
+ sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE);
profile->mSamplingRates.add(config->sample_rate);
profile->mChannelMasks.add(config->channel_mask);
profile->mFormats.add(config->format);
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device);
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device);
devDesc->mAddress = address;
profile->mSupportedDevices.add(devDesc);
+ profile->attach(this);
mOutputProfiles.add(profile);
return NO_ERROR;
@@ -213,18 +215,19 @@
status_t HwModule::addInputProfile(String8 name, const audio_config_t *config,
audio_devices_t device, String8 address)
{
- sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK, this);
+ sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK);
profile->mSamplingRates.add(config->sample_rate);
profile->mChannelMasks.add(config->channel_mask);
profile->mFormats.add(config->format);
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device);
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device);
devDesc->mAddress = address;
profile->mSupportedDevices.add(devDesc);
ALOGV("addInputProfile() name %s rate %d mask 0x08", name.string(), config->sample_rate, config->channel_mask);
+ profile->attach(this);
mInputProfiles.add(profile);
return NO_ERROR;
@@ -348,7 +351,8 @@
}
sp<DeviceDescriptor> devDesc =
- new DeviceDescriptor(String8(device_name != NULL ? device_name : ""), device);
+ new DeviceDescriptor(device);
+ devDesc->mName = device_name;
devDesc->mAddress = address;
return devDesc;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
index 376dd22..7b6d51d 100644
--- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
@@ -23,9 +23,8 @@
namespace android {
-IOProfile::IOProfile(const String8& name, audio_port_role_t role,
- const sp<HwModule>& module)
- : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module)
+IOProfile::IOProfile(const String8& name, audio_port_role_t role)
+ : AudioPort(name, AUDIO_PORT_TYPE_MIX, role)
{
}
@@ -41,7 +40,9 @@
uint32_t samplingRate,
uint32_t *updatedSamplingRate,
audio_format_t format,
+ audio_format_t *updatedFormat,
audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask,
uint32_t flags) const
{
const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE;
@@ -72,7 +73,14 @@
return false;
}
- if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) {
+ if (!audio_is_valid_format(format)) {
+ return false;
+ }
+ if (isPlaybackThread && checkExactFormat(format) != NO_ERROR) {
+ return false;
+ }
+ audio_format_t myUpdatedFormat = format;
+ if (isRecordThread && checkCompatibleFormat(format, &myUpdatedFormat) != NO_ERROR) {
return false;
}
@@ -80,8 +88,9 @@
checkExactChannelMask(channelMask) != NO_ERROR)) {
return false;
}
+ audio_channel_mask_t myUpdatedChannelMask = channelMask;
if (isRecordThread && (!audio_is_input_channel(channelMask) ||
- checkCompatibleChannelMask(channelMask) != NO_ERROR)) {
+ checkCompatibleChannelMask(channelMask, &myUpdatedChannelMask) != NO_ERROR)) {
return false;
}
@@ -100,6 +109,12 @@
if (updatedSamplingRate != NULL) {
*updatedSamplingRate = myUpdatedSamplingRate;
}
+ if (updatedFormat != NULL) {
+ *updatedFormat = myUpdatedFormat;
+ }
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = myUpdatedChannelMask;
+ }
return true;
}
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
index eadaa77..db0573f 100755
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
@@ -134,16 +134,16 @@
audio_policy_dev_state_t state) = 0;
/**
- * Translate a volume index given by the UI to an amplification value for a stream type
+ * Translate a volume index given by the UI to an amplification value in dB for a stream type
* and a device category.
*
* @param[in] deviceCategory for which the conversion is requested.
* @param[in] stream type for which the conversion is requested.
* @param[in] indexInUi index received from the UI to be translated.
*
- * @return amplification value matching the UI index for this given device and stream.
+ * @return amplification value in dB matching the UI index for this given device and stream.
*/
- virtual float volIndexToAmpl(Volume::device_category deviceCategory, audio_stream_type_t stream,
+ virtual float volIndexToDb(Volume::device_category deviceCategory, audio_stream_type_t stream,
int indexInUi) = 0;
/**
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
index 4f5427e..6d43df2 100755
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
@@ -43,7 +43,7 @@
virtual const AudioPolicyMixCollection &getAudioPolicyMixCollection() const = 0;
- virtual const AudioOutputCollection &getOutputs() const = 0;
+ virtual const SwAudioOutputCollection &getOutputs() const = 0;
virtual const AudioInputCollection &getInputs() const = 0;
diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk
index b0ae835..8d43b89 100755
--- a/services/audiopolicy/enginedefault/Android.mk
+++ b/services/audiopolicy/enginedefault/Android.mk
@@ -43,6 +43,4 @@
libutils \
libaudioutils \
-include external/stlport/libstlport.mk
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 1fd3341..50f1609 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -63,13 +63,14 @@
return (mApmObserver != NULL) ? NO_ERROR : NO_INIT;
}
-float Engine::volIndexToAmpl(Volume::device_category category, audio_stream_type_t streamType,
+float Engine::volIndexToDb(Volume::device_category category, audio_stream_type_t streamType,
int indexInUi)
{
const StreamDescriptor &streamDesc = mApmObserver->getStreamDescriptors().valueAt(streamType);
- return Gains::volIndexToAmpl(category, streamDesc, indexInUi);
+ return Gains::volIndexToDb(category, streamDesc, indexInUi);
}
+
status_t Engine::initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax)
{
ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
@@ -243,7 +244,7 @@
routing_strategy Engine::getStrategyForUsage(audio_usage_t usage)
{
- const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+ const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
// usage to strategy mapping
switch (usage) {
@@ -291,7 +292,7 @@
const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
- const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+ const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
uint32_t device = AUDIO_DEVICE_NONE;
uint32_t availableOutputDevicesType = availableOutputDevices.types();
@@ -358,7 +359,7 @@
if (((availableInputDevices.types() &
AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) ||
(((txDevice & availPrimaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
- (primaryOutput->getAudioPort()->mModule->mHalVersion <
+ (primaryOutput->getAudioPort()->getModuleVersion() <
AUDIO_DEVICE_API_VERSION_3_0))) {
availableOutputDevicesType = availPrimaryOutputDevices;
}
@@ -582,7 +583,7 @@
{
const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
- const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+ const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
uint32_t device = AUDIO_DEVICE_NONE;
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index f44556c..56a4748 100755
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -101,10 +101,10 @@
{
return mPolicyEngine->initializeVolumeCurves(isSpeakerDrcEnabled);
}
- virtual float volIndexToAmpl(Volume::device_category deviceCategory,
+ virtual float volIndexToDb(Volume::device_category deviceCategory,
audio_stream_type_t stream,int indexInUi)
{
- return mPolicyEngine->volIndexToAmpl(deviceCategory, stream, indexInUi);
+ return mPolicyEngine->volIndexToDb(deviceCategory, stream, indexInUi);
}
private:
Engine *mPolicyEngine;
@@ -141,7 +141,7 @@
audio_devices_t getDeviceForStrategy(routing_strategy strategy) const;
audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const;
- float volIndexToAmpl(Volume::device_category category,
+ float volIndexToDb(Volume::device_category category,
audio_stream_type_t stream, int indexInUi);
status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax);
void initializeVolumeCurves(bool isSpeakerDrcEnabled);
diff --git a/services/audiopolicy/enginedefault/src/Gains.cpp b/services/audiopolicy/enginedefault/src/Gains.cpp
index a684fdd..78f2909 100644
--- a/services/audiopolicy/enginedefault/src/Gains.cpp
+++ b/services/audiopolicy/enginedefault/src/Gains.cpp
@@ -197,10 +197,10 @@
};
//static
-float Gains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc,
- int indexInUi)
+float Gains::volIndexToDb(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi)
{
- Volume::device_category deviceCategory = Volume::getDeviceCategory(device);
const VolumeCurvePoint *curve = streamDesc.getVolumeCurvePoint(deviceCategory);
// the volume index in the UI is relative to the min and max volume indices for this stream type
@@ -212,7 +212,7 @@
// find what part of the curve this index volume belongs to, or if it's out of bounds
int segment = 0;
if (volIdx < curve[Volume::VOLMIN].mIndex) { // out of bounds
- return 0.0f;
+ return VOLUME_MIN_DB;
} else if (volIdx < curve[Volume::VOLKNEE1].mIndex) {
segment = 0;
} else if (volIdx < curve[Volume::VOLKNEE2].mIndex) {
@@ -220,7 +220,7 @@
} else if (volIdx <= curve[Volume::VOLMAX].mIndex) {
segment = 2;
} else { // out of bounds
- return 1.0f;
+ return 0.0f;
}
// linear interpolation in the attenuation table in dB
@@ -231,17 +231,25 @@
((float)(curve[segment+1].mIndex -
curve[segment].mIndex)) );
- float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
-
- ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f",
+ ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
curve[segment].mIndex, volIdx,
curve[segment+1].mIndex,
curve[segment].mDBAttenuation,
decibels,
- curve[segment+1].mDBAttenuation,
- amplification);
+ curve[segment+1].mDBAttenuation);
- return amplification;
+ return decibels;
}
+
+//static
+float Gains::volIndexToAmpl(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi)
+{
+ return Volume::DbToAmpl(volIndexToDb(deviceCategory, streamDesc, indexInUi));
+}
+
+
+
}; // namespace android
diff --git a/services/audiopolicy/enginedefault/src/Gains.h b/services/audiopolicy/enginedefault/src/Gains.h
index b5601ca..7620b7d 100644
--- a/services/audiopolicy/enginedefault/src/Gains.h
+++ b/services/audiopolicy/enginedefault/src/Gains.h
@@ -29,8 +29,13 @@
class Gains
{
public :
- static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc,
- int indexInUi);
+ static float volIndexToAmpl(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi);
+
+ static float volIndexToDb(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi);
// default volume curve
static const VolumeCurvePoint sDefaultVolumeCurve[Volume::VOLCNT];
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 797a2b4..d1ee400 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -157,7 +157,7 @@
// outputs must be closed after checkOutputForAllStrategies() is executed
if (!outputs.isEmpty()) {
for (size_t i = 0; i < outputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
// close unused outputs after device disconnection or direct outputs that have been
// opened by checkOutputsForDevice() to query dynamic parameters
if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
@@ -176,18 +176,17 @@
updateCallRouting(newDevice);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_io_handle_t output = mOutputs.keyAt(i);
- if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
- audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i),
- true /*fromCache*/);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
+ audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
// do not force device change on duplicated output because if device is 0, it will
// also force a device 0 for the two outputs it is duplicated to which may override
// a valid device selection on those outputs.
- bool force = !mOutputs.valueAt(i)->isDuplicated()
+ bool force = !desc->isDuplicated()
&& (!device_distinguishes_on_address(device)
// always force when disconnecting (a non-duplicated device)
|| (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
- setOutputDevice(output, newDevice, force, 0);
+ setOutputDevice(desc, newDevice, force, 0);
}
}
@@ -349,10 +348,11 @@
AUDIO_OUTPUT_FLAG_NONE,
AUDIO_FORMAT_INVALID);
if (output != AUDIO_IO_HANDLE_NONE) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
ALOG_ASSERT(!outputDesc->isDuplicated(),
"updateCallRouting() RX device output is duplicated");
outputDesc->toAudioPortConfig(&patch.sources[1]);
+ patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
patch.num_sources = 2;
}
@@ -395,6 +395,7 @@
ALOG_ASSERT(!outputDesc->isDuplicated(),
"updateCallRouting() RX device output is duplicated");
outputDesc->toAudioPortConfig(&patch.sources[1]);
+ patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
patch.num_sources = 2;
}
@@ -448,13 +449,13 @@
checkOutputForAllStrategies();
updateDevicesAndOutputs();
- sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput);
+ sp<SwAudioOutputDescriptor> hwOutputDesc = mPrimaryOutput;
int delayMs = 0;
if (isStateInCall(state)) {
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
// mute media and sonification strategies and delay device switch by the largest
// latency of any output where either strategy is active.
// This avoid sending the ring tone or music tail into the earpiece or headset.
@@ -464,14 +465,14 @@
isStrategyActive(desc, STRATEGY_SONIFICATION,
SONIFICATION_HEADSET_MUSIC_DELAY,
sysTime)) &&
- (delayMs < (int)desc->mLatency*2)) {
- delayMs = desc->mLatency*2;
+ (delayMs < (int)desc->latency()*2)) {
+ delayMs = desc->latency()*2;
}
- setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i));
- setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+ setStrategyMute(STRATEGY_MEDIA, true, desc);
+ setStrategyMute(STRATEGY_MEDIA, false, desc, MUTE_TIME_MS,
getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/));
- setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i));
- setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+ setStrategyMute(STRATEGY_SONIFICATION, true, desc);
+ setStrategyMute(STRATEGY_SONIFICATION, false, desc, MUTE_TIME_MS,
getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/));
}
}
@@ -547,13 +548,13 @@
updateCallRouting(newDevice);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_io_handle_t output = mOutputs.keyAt(i);
- audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/);
- if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
- setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ audio_devices_t newDevice = getNewOutputDevice(outputDesc, true /*fromCache*/);
+ if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {
+ setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE));
}
if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
- applyStreamVolumes(output, newDevice, 0, true);
+ applyStreamVolumes(outputDesc, newDevice, 0, true);
}
}
@@ -584,8 +585,10 @@
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
- bool found = profile->isCompatibleProfile(device, String8(""), samplingRate,
- NULL /*updatedSamplingRate*/, format, channelMask,
+ bool found = profile->isCompatibleProfile(device, String8(""),
+ samplingRate, NULL /*updatedSamplingRate*/,
+ format, NULL /*updatedFormat*/,
+ channelMask, NULL /*updatedChannelMask*/,
flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ?
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_DIRECT);
if (found && (mAvailableOutputDevices.types() & profile->mSupportedDevices.types())) {
@@ -617,10 +620,12 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
audio_attributes_t attributes;
@@ -639,7 +644,7 @@
}
stream_type_to_audio_attributes(*stream, &attributes);
}
- sp<AudioOutputDescriptor> desc;
+ sp<SwAudioOutputDescriptor> desc;
if (mPolicyMixes.getOutputForAttr(attributes, desc) == NO_ERROR) {
ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
if (!audio_is_linear_pcm(format)) {
@@ -655,8 +660,22 @@
return BAD_VALUE;
}
- ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x",
- attributes.usage, attributes.content_type, attributes.tags, attributes.flags);
+ ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
+ " session %d selectedDeviceId %d",
+ attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
+ session, selectedDeviceId);
+
+ *stream = streamTypefromAttributesInt(&attributes);
+
+ // Explicit routing?
+ sp<DeviceDescriptor> deviceDesc;
+ for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
+ if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) {
+ deviceDesc = mAvailableOutputDevices[i];
+ break;
+ }
+ }
+ mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
@@ -668,13 +687,14 @@
ALOGV("getOutputForAttr() device 0x%x, samplingRate %d, format %x, channelMask %x, flags %x",
device, samplingRate, format, channelMask, flags);
- *stream = streamTypefromAttributesInt(&attributes);
*output = getOutputForDevice(device, session, *stream,
samplingRate, format, channelMask,
flags, offloadInfo);
if (*output == AUDIO_IO_HANDLE_NONE) {
+ mOutputRoutes.removeRoute(session);
return INVALID_OPERATION;
}
+
return NO_ERROR;
}
@@ -699,7 +719,8 @@
if (mTestOutputs[mCurOutput] == 0) {
ALOGV("getOutput() opening test output");
- sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+ sp<AudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(NULL,
+ mpClientInterface);
outputDesc->mDevice = mTestDevice;
outputDesc->mLatency = mTestLatencyMs;
outputDesc->mFlags =
@@ -747,6 +768,9 @@
if (stream != AUDIO_STREAM_MUSIC) {
flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
}
+ if (stream == AUDIO_STREAM_TTS) {
+ flags = AUDIO_OUTPUT_FLAG_TTS;
+ }
sp<IOProfile> profile;
@@ -775,10 +799,10 @@
}
if (profile != 0) {
- sp<AudioOutputDescriptor> outputDesc = NULL;
+ sp<SwAudioOutputDescriptor> outputDesc = NULL;
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (!desc->isDuplicated() && (profile == desc->mProfile)) {
outputDesc = desc;
// reuse direct output if currently open and configured with same parameters
@@ -795,7 +819,7 @@
if (outputDesc != NULL) {
closeOutput(outputDesc->mIoHandle);
}
- outputDesc = new AudioOutputDescriptor(profile);
+ outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface);
outputDesc->mDevice = device;
outputDesc->mLatency = 0;
outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags);
@@ -806,7 +830,7 @@
if (offloadInfo != NULL) {
config.offload_info = *offloadInfo;
}
- status = mpClientInterface->openOutput(profile->mModule->mHandle,
+ status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&outputDesc->mDevice,
@@ -856,7 +880,6 @@
}
non_direct_output:
-
// ignoring channel mask due to downmix capability in mixer
// open a non direct output
@@ -874,7 +897,7 @@
ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d,"
"format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags);
- ALOGV("getOutput() returns output %d", output);
+ ALOGV(" getOutputForDevice() returns output %d", output);
return output;
}
@@ -902,7 +925,7 @@
audio_io_handle_t outputPrimary = 0;
for (size_t i = 0; i < outputs.size(); i++) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
if (!outputDesc->isDuplicated()) {
// if a valid format is specified, skip output if not compatible
if (format != AUDIO_FORMAT_INVALID) {
@@ -941,15 +964,63 @@
audio_stream_type_t stream,
audio_session_t session)
{
- ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
+ ALOGV("startOutput() output %d, stream %d, session %d",
+ output, stream, session);
ssize_t index = mOutputs.indexOfKey(output);
if (index < 0) {
ALOGW("startOutput() unknown output %d", output);
return BAD_VALUE;
}
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+
+ // Routing?
+ mOutputRoutes.incRouteActivity(session);
+
+ audio_devices_t newDevice;
+ if (outputDesc->mPolicyMix != NULL) {
+ newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ } else if (mOutputRoutes.hasRouteChanged(session)) {
+ newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
+ checkStrategyRoute(getStrategy(stream), output);
+ } else {
+ newDevice = AUDIO_DEVICE_NONE;
+ }
+
+ uint32_t delayMs = 0;
+
+ status_t status = startSource(outputDesc, stream, newDevice, &delayMs);
+
+ if (status != NO_ERROR) {
+ mOutputRoutes.decRouteActivity(session);
+ return status;
+ }
+ // Automatically enable the remote submix input when output is started on a re routing mix
+ // of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
+ outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ outputDesc->mPolicyMix->mRegistrationId,
+ "remote-submix");
+ }
+
+ if (delayMs != 0) {
+ usleep(delayMs * 1000);
+ }
+
+ return status;
+}
+
+status_t AudioPolicyManager::startSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t *delayMs)
+{
// cannot start playback of STREAM_TTS if any other output is being used
uint32_t beaconMuteLatency = 0;
+
+ *delayMs = 0;
if (stream == AUDIO_STREAM_TTS) {
ALOGV("\t found BEACON stream");
if (mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
@@ -962,20 +1033,15 @@
beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT);
}
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
-
// increment usage count for this stream on the requested output:
// NOTE that the usage count is the same for duplicated output and hardware output which is
// necessary for a correct control of hardware output routing by startOutput() and stopOutput()
outputDesc->changeRefCount(stream, 1);
- if (outputDesc->mRefCount[stream] == 1) {
+ if (outputDesc->mRefCount[stream] == 1 || device != AUDIO_DEVICE_NONE) {
// starting an output being rerouted?
- audio_devices_t newDevice;
- if (outputDesc->mPolicyMix != NULL) {
- newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
- } else {
- newDevice = getNewOutputDevice(output, false /*fromCache*/);
+ if (device == AUDIO_DEVICE_NONE) {
+ device = getNewOutputDevice(outputDesc, false /*fromCache*/);
}
routing_strategy strategy = getStrategy(stream);
bool shouldWait = (strategy == STRATEGY_SONIFICATION) ||
@@ -991,7 +1057,7 @@
// In this case, the audio HAL must receive the new device selection so that it can
// change the device currently selected by the other active output.
if (outputDesc->sharesHwModuleWith(desc) &&
- desc->device() != newDevice) {
+ desc->device() != device) {
force = true;
}
// wait for audio on other active outputs to be presented when starting
@@ -1003,7 +1069,7 @@
}
}
}
- uint32_t muteWaitMs = setOutputDevice(output, newDevice, force);
+ uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force);
// handle special case for sonification while in call
if (isInCall()) {
@@ -1012,32 +1078,18 @@
// apply volume rules for current stream and device if necessary
checkAndSetVolume(stream,
- mStreams[stream].getVolumeIndex(newDevice),
- output,
- newDevice);
+ mStreams.valueFor(stream).getVolumeIndex(device),
+ outputDesc,
+ device);
// update the outputs if starting an output with a stream that can affect notification
// routing
handleNotificationRoutingForStream(stream);
- // Automatically enable the remote submix input when output is started on a re routing mix
- // of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
- outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
- outputDesc->mPolicyMix->mRegistrationId,
- "remote-submix");
- }
-
// force reevaluating accessibility routing when ringtone or alarm starts
if (strategy == STRATEGY_SONIFICATION) {
mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY);
}
-
- if (waitMs > muteWaitMs) {
- usleep((waitMs - muteWaitMs) * 2 * 1000);
- }
}
return NO_ERROR;
}
@@ -1054,8 +1106,39 @@
return BAD_VALUE;
}
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+ if (outputDesc->mRefCount[stream] == 1) {
+ // Automatically disable the remote submix input when output is stopped on a
+ // re routing mix of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(outputDesc->mDevice) &&
+ outputDesc->mPolicyMix != NULL &&
+ outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ outputDesc->mPolicyMix->mRegistrationId,
+ "remote-submix");
+ }
+ }
+
+ // Routing?
+ bool forceDeviceUpdate = false;
+ if (outputDesc->mRefCount[stream] > 0) {
+ int activityCount = mOutputRoutes.decRouteActivity(session);
+ forceDeviceUpdate = (mOutputRoutes.hasRoute(session) && (activityCount == 0));
+
+ if (forceDeviceUpdate) {
+ checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
+ }
+ }
+
+ return stopSource(outputDesc, stream, forceDeviceUpdate);
+}
+
+status_t AudioPolicyManager::stopSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream,
+ bool forceDeviceUpdate)
+{
// always handle stream stop, check which stream type is stopping
handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
@@ -1067,41 +1150,31 @@
if (outputDesc->mRefCount[stream] > 0) {
// decrement usage count of this stream on the output
outputDesc->changeRefCount(stream, -1);
- // store time at which the stream was stopped - see isStreamActive()
- if (outputDesc->mRefCount[stream] == 0) {
- // Automatically disable the remote submix input when output is stopped on a
- // re routing mix of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(outputDesc->mDevice) &&
- outputDesc->mPolicyMix != NULL &&
- outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
- outputDesc->mPolicyMix->mRegistrationId,
- "remote-submix");
- }
+ // store time at which the stream was stopped - see isStreamActive()
+ if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) {
outputDesc->mStopTime[stream] = systemTime();
- audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
+ audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
// delay the device switch by twice the latency because stopOutput() is executed when
// the track stop() command is received and at that time the audio track buffer can
// still contain data that needs to be drained. The latency only covers the audio HAL
// and kernel buffers. Also the latency does not always include additional delay in the
// audio path (audio DSP, CODEC ...)
- setOutputDevice(output, newDevice, false, outputDesc->mLatency*2);
+ setOutputDevice(outputDesc, newDevice, false, outputDesc->latency()*2);
// force restoring the device selection on other active outputs if it differs from the
// one being selected for this output
for (size_t i = 0; i < mOutputs.size(); i++) {
audio_io_handle_t curOutput = mOutputs.keyAt(i);
sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
- if (curOutput != output &&
+ if (desc != outputDesc &&
desc->isActive() &&
outputDesc->sharesHwModuleWith(desc) &&
(newDevice != desc->device())) {
- setOutputDevice(curOutput,
- getNewOutputDevice(curOutput, false /*fromCache*/),
+ setOutputDevice(desc,
+ getNewOutputDevice(desc, false /*fromCache*/),
true,
- outputDesc->mLatency*2);
+ outputDesc->latency()*2);
}
}
// update the outputs if stopping one with a stream that can affect notification routing
@@ -1109,7 +1182,7 @@
}
return NO_ERROR;
} else {
- ALOGW("stopOutput() refcount is already 0 for output %d", output);
+ ALOGW("stopOutput() refcount is already 0");
return INVALID_OPERATION;
}
}
@@ -1138,7 +1211,10 @@
}
#endif //AUDIO_POLICY_TEST
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(index);
+ // Routing
+ mOutputRoutes.removeRoute(session);
+
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(index);
if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (desc->mDirectOpenCount <= 0) {
ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -1150,8 +1226,9 @@
// If effects where present on the output, audioflinger moved them to the primary
// output by default: move them back to the appropriate output.
audio_io_handle_t dstOutput = getOutputForEffect();
- if (dstOutput != mPrimaryOutput) {
- mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
+ if (dstOutput != mPrimaryOutput->mIoHandle) {
+ mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX,
+ mPrimaryOutput->mIoHandle, dstOutput);
}
mpClientInterface->onAudioPortListUpdate();
}
@@ -1162,10 +1239,12 @@
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
input_type_t *inputType)
{
ALOGV("getInputForAttr() source %d, samplingRate %d, format %d, channelMask %x,"
@@ -1187,9 +1266,19 @@
}
halInputSource = inputSource;
+ // Explicit routing?
+ sp<DeviceDescriptor> deviceDesc;
+ for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
+ if (mAvailableInputDevices[i]->getId() == selectedDeviceId) {
+ deviceDesc = mAvailableInputDevices[i];
+ break;
+ }
+ }
+ mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
+
if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
- status_t ret = mPolicyMixes.getInputMixForAttr(*attr, policyMix);
+ status_t ret = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
if (ret != NO_ERROR) {
return ret;
}
@@ -1247,48 +1336,54 @@
}
}
- sp<IOProfile> profile = getInputProfile(device, address,
- samplingRate, format, channelMask,
- flags);
- if (profile == 0) {
- //retry without flags
- audio_input_flags_t log_flags = flags;
- flags = AUDIO_INPUT_FLAG_NONE;
+ // find a compatible input profile (not necessarily identical in parameters)
+ sp<IOProfile> profile;
+ // samplingRate and flags may be updated by getInputProfile
+ uint32_t profileSamplingRate = samplingRate;
+ audio_format_t profileFormat = format;
+ audio_channel_mask_t profileChannelMask = channelMask;
+ audio_input_flags_t profileFlags = flags;
+ for (;;) {
profile = getInputProfile(device, address,
- samplingRate, format, channelMask,
- flags);
- if (profile == 0) {
+ profileSamplingRate, profileFormat, profileChannelMask,
+ profileFlags);
+ if (profile != 0) {
+ break; // success
+ } else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
+ profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
+ } else { // fail
ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u,"
"format %#x, channelMask 0x%X, flags %#x",
- device, samplingRate, format, channelMask, log_flags);
+ device, samplingRate, format, channelMask, flags);
return BAD_VALUE;
}
}
- if (profile->mModule->mHandle == 0) {
- ALOGE("getInputForAttr(): HW module %s not opened", profile->mModule->mName);
+ if (profile->getModuleHandle() == 0) {
+ ALOGE("getInputForAttr(): HW module %s not opened", profile->getModuleName());
return NO_INIT;
}
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- config.sample_rate = samplingRate;
- config.channel_mask = channelMask;
- config.format = format;
+ config.sample_rate = profileSamplingRate;
+ config.channel_mask = profileChannelMask;
+ config.format = profileFormat;
- status_t status = mpClientInterface->openInput(profile->mModule->mHandle,
+ status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
input,
&config,
&device,
address,
halInputSource,
- flags);
+ profileFlags);
// only accept input with the exact requested set of parameters
if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE ||
- (samplingRate != config.sample_rate) ||
- (format != config.format) ||
- (channelMask != config.channel_mask)) {
- ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d, channelMask %x",
+ (profileSamplingRate != config.sample_rate) ||
+ (profileFormat != config.format) ||
+ (profileChannelMask != config.channel_mask)) {
+ ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d,"
+ " channelMask %x",
samplingRate, format, channelMask);
if (*input != AUDIO_IO_HANDLE_NONE) {
mpClientInterface->closeInput(*input);
@@ -1300,18 +1395,19 @@
inputDesc->mInputSource = inputSource;
inputDesc->mRefCount = 0;
inputDesc->mOpenRefCount = 1;
- inputDesc->mSamplingRate = samplingRate;
- inputDesc->mFormat = format;
- inputDesc->mChannelMask = channelMask;
+ inputDesc->mSamplingRate = profileSamplingRate;
+ inputDesc->mFormat = profileFormat;
+ inputDesc->mChannelMask = profileChannelMask;
inputDesc->mDevice = device;
inputDesc->mSessions.add(session);
inputDesc->mIsSoundTrigger = isSoundTrigger;
inputDesc->mPolicyMix = policyMix;
- ALOGV("getInputForAttr() returns input type = %d", inputType);
+ ALOGV("getInputForAttr() returns input type = %d", *inputType);
addInput(*input, inputDesc);
mpClientInterface->onAudioPortListUpdate();
+
return NO_ERROR;
}
@@ -1353,7 +1449,17 @@
}
}
- if (inputDesc->mRefCount == 0) {
+ // Routing?
+ mInputRoutes.incRouteActivity(session);
+
+ if (inputDesc->mRefCount == 0 || mInputRoutes.hasRouteChanged(session)) {
+ // if input maps to a dynamic policy with an activity listener, notify of state change
+ if ((inputDesc->mPolicyMix != NULL)
+ && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+ mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId,
+ MIX_STATE_MIXING);
+ }
+
if (mInputs.activeInputsCount() == 0) {
SoundTrigger::setCaptureState(true);
}
@@ -1406,7 +1512,17 @@
}
inputDesc->mRefCount--;
+
+ // Routing?
+ mInputRoutes.decRouteActivity(session);
+
if (inputDesc->mRefCount == 0) {
+ // if input maps to a dynamic policy with an activity listener, notify of state change
+ if ((inputDesc->mPolicyMix != NULL)
+ && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+ mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId,
+ MIX_STATE_IDLE);
+ }
// automatically disable the remote submix output when input is stopped if not
// used by a policy mix of type MIX_TYPE_RECORDERS
@@ -1442,6 +1558,10 @@
ALOGW("releaseInput() releasing unknown input %d", input);
return;
}
+
+ // Routing
+ mInputRoutes.removeRoute(session);
+
sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
ALOG_ASSERT(inputDesc != 0);
@@ -1505,8 +1625,8 @@
audio_devices_t device)
{
- if ((index < mStreams[stream].getVolumeIndexMin()) ||
- (index > mStreams[stream].getVolumeIndexMax())) {
+ if ((index < mStreams.valueFor(stream).getVolumeIndexMin()) ||
+ (index > mStreams.valueFor(stream).getVolumeIndexMax())) {
return BAD_VALUE;
}
if (!audio_is_output_device(device)) {
@@ -1514,7 +1634,7 @@
}
// Force max volume if stream cannot be muted
- if (!mStreams.canBeMuted(stream)) index = mStreams[stream].getVolumeIndexMax();
+ if (!mStreams.canBeMuted(stream)) index = mStreams.valueFor(stream).getVolumeIndexMax();
ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d",
stream, device, index);
@@ -1543,16 +1663,17 @@
}
status_t status = NO_ERROR;
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_devices_t curDevice = Volume::getDeviceForVolume(mOutputs.valueAt(i)->device());
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & strategyDevice) != 0)) {
- status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);
+ status_t volStatus = checkAndSetVolume(stream, index, desc, curDevice);
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & accessibilityDevice) != 0)) {
status_t volStatus = checkAndSetVolume(AUDIO_STREAM_ACCESSIBILITY,
- index, mOutputs.keyAt(i), curDevice);
+ index, desc, curDevice);
}
}
return status;
@@ -1575,7 +1696,7 @@
}
device = Volume::getDeviceForVolume(device);
- *index = mStreams[stream].getVolumeIndex(device);
+ *index = mStreams.valueFor(stream).getVolumeIndex(device);
ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index);
return NO_ERROR;
}
@@ -1599,7 +1720,7 @@
audio_io_handle_t outputDeepBuffer = 0;
for (size_t i = 0; i < outputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags);
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
outputOffloaded = outputs[i];
@@ -1653,6 +1774,16 @@
return mEffects.registerEffect(desc, io, strategy, session, id);
}
+bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+ return mOutputs.isStreamActive(stream, inPastMs);
+}
+
+bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+ return mOutputs.isStreamActiveRemotely(stream, inPastMs);
+}
+
bool AudioPolicyManager::isSourceActive(audio_source_t source) const
{
for (size_t i = 0; i < mInputs.size(); i++) {
@@ -1803,7 +1934,7 @@
snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
result.append(buffer);
- snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput);
+ snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput->mIoHandle);
result.append(buffer);
snprintf(buffer, SIZE, " Phone state: %d\n", mEngine->getPhoneState());
result.append(buffer);
@@ -2021,7 +2152,7 @@
}
if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
if (outputDesc == NULL) {
ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
return BAD_VALUE;
@@ -2055,9 +2186,12 @@
patch->sources[0].sample_rate,
NULL, // updatedSamplingRate
patch->sources[0].format,
+ NULL, // updatedFormat
patch->sources[0].channel_mask,
+ NULL, // updatedChannelMask
AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
- ALOGV("createAudioPatch() profile not supported for device %08x", devDesc->type());
+ ALOGV("createAudioPatch() profile not supported for device %08x",
+ devDesc->type());
return INVALID_OPERATION;
}
devices.add(devDesc);
@@ -2069,7 +2203,7 @@
// TODO: reconfigure output format and channels here
ALOGV("createAudioPatch() setting device %08x on output %d",
devices.types(), outputDesc->mIoHandle);
- setOutputDevice(outputDesc->mIoHandle, devices.types(), true, 0, handle);
+ setOutputDevice(outputDesc, devices.types(), true, 0, handle);
index = mAudioPatches.indexOfKey(*handle);
if (index >= 0) {
if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2109,7 +2243,9 @@
patch->sinks[0].sample_rate,
NULL, /*updatedSampleRate*/
patch->sinks[0].format,
+ NULL, /*updatedFormat*/
patch->sinks[0].channel_mask,
+ NULL, /*updatedChannelMask*/
// FIXME for the parameter type,
// and the NONE
(audio_output_flags_t)
@@ -2163,8 +2299,12 @@
}
sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
- if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
- // only one sink supported when connected devices across HW modules
+ // create a software bridge in PatchPanel if:
+ // - source and sink devices are on differnt HW modules OR
+ // - audio HAL version is < 3.0
+ if ((srcDeviceDesc->getModuleHandle() != sinkDeviceDesc->getModuleHandle()) ||
+ (srcDeviceDesc->mModule->mHalVersion < AUDIO_DEVICE_API_VERSION_3_0)) {
+ // support only one sink device for now to simplify output selection logic
if (patch->num_sinks > 1) {
return INVALID_OPERATION;
}
@@ -2181,6 +2321,7 @@
return INVALID_OPERATION;
}
outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
+ newPatch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
newPatch.num_sources = 2;
}
}
@@ -2242,14 +2383,14 @@
struct audio_patch *patch = &patchDesc->mPatch;
patchDesc->mUid = mUidCached;
if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
if (outputDesc == NULL) {
ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
return BAD_VALUE;
}
- setOutputDevice(outputDesc->mIoHandle,
- getNewOutputDevice(outputDesc->mIoHandle, true /*fromCache*/),
+ setOutputDevice(outputDesc,
+ getNewOutputDevice(outputDesc, true /*fromCache*/),
true,
0,
NULL);
@@ -2308,7 +2449,7 @@
sp<AudioPortConfig> audioPortConfig;
if (config->type == AUDIO_PORT_TYPE_MIX) {
if (config->role == AUDIO_PORT_ROLE_SOURCE) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
if (outputDesc == NULL) {
return BAD_VALUE;
}
@@ -2356,6 +2497,12 @@
return status;
}
+void AudioPolicyManager::releaseResourcesForUid(uid_t uid)
+{
+ clearAudioPatches(uid);
+ clearSessionRoutes(uid);
+}
+
void AudioPolicyManager::clearAudioPatches(uid_t uid)
{
for (ssize_t i = (ssize_t)mAudioPatches.size() - 1; i >= 0; i--) {
@@ -2366,6 +2513,82 @@
}
}
+
+void AudioPolicyManager::checkStrategyRoute(routing_strategy strategy,
+ audio_io_handle_t ouptutToSkip)
+{
+ audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
+ SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);
+ for (size_t j = 0; j < mOutputs.size(); j++) {
+ if (mOutputs.keyAt(j) == ouptutToSkip) {
+ continue;
+ }
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(j);
+ if (!isStrategyActive(outputDesc, (routing_strategy)strategy)) {
+ continue;
+ }
+ // If the default device for this strategy is on another output mix,
+ // invalidate all tracks in this strategy to force re connection.
+ // Otherwise select new device on the output mix.
+ if (outputs.indexOf(mOutputs.keyAt(j)) < 0) {
+ for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
+ if (stream == AUDIO_STREAM_PATCH) {
+ continue;
+ }
+ if (getStrategy((audio_stream_type_t)stream) == strategy) {
+ mpClientInterface->invalidateStream((audio_stream_type_t)stream);
+ }
+ }
+ } else {
+ audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
+ setOutputDevice(outputDesc, newDevice, false);
+ }
+ }
+}
+
+void AudioPolicyManager::clearSessionRoutes(uid_t uid)
+{
+ // remove output routes associated with this uid
+ SortedVector<routing_strategy> affectedStrategies;
+ for (ssize_t i = (ssize_t)mOutputRoutes.size() - 1; i >= 0; i--) {
+ sp<SessionRoute> route = mOutputRoutes.valueAt(i);
+ if (route->mUid == uid) {
+ mOutputRoutes.removeItemsAt(i);
+ if (route->mDeviceDescriptor != 0) {
+ affectedStrategies.add(getStrategy(route->mStreamType));
+ }
+ }
+ }
+ // reroute outputs if necessary
+ for (size_t i = 0; i < affectedStrategies.size(); i++) {
+ checkStrategyRoute(affectedStrategies[i], AUDIO_IO_HANDLE_NONE);
+ }
+
+ // remove input routes associated with this uid
+ SortedVector<audio_source_t> affectedSources;
+ for (ssize_t i = (ssize_t)mInputRoutes.size() - 1; i >= 0; i--) {
+ sp<SessionRoute> route = mInputRoutes.valueAt(i);
+ if (route->mUid == uid) {
+ mInputRoutes.removeItemsAt(i);
+ if (route->mDeviceDescriptor != 0) {
+ affectedSources.add(route->mSource);
+ }
+ }
+ }
+ // reroute inputs if necessary
+ SortedVector<audio_io_handle_t> inputsToClose;
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i);
+ if (affectedSources.indexOf(inputDesc->mInputSource) >= 0) {
+ inputsToClose.add(inputDesc->mIoHandle);
+ }
+ }
+ for (size_t i = 0; i < inputsToClose.size(); i++) {
+ closeInput(inputsToClose[i]);
+ }
+}
+
+
status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session,
audio_io_handle_t *ioHandle,
audio_devices_t *device)
@@ -2377,6 +2600,18 @@
return mSoundTriggerSessions.acquireSession(*session, *ioHandle);
}
+status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *source __unused,
+ const audio_attributes_t *attributes __unused,
+ audio_io_handle_t *handle __unused)
+{
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyManager::stopAudioSource(audio_io_handle_t handle __unused)
+{
+ return INVALID_OPERATION;
+}
+
// ----------------------------------------------------------------------------
// AudioPolicyManager
// ----------------------------------------------------------------------------
@@ -2390,7 +2625,6 @@
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
- mPrimaryOutput((audio_io_handle_t)0),
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mA2dpSuspended(false),
mSpeakerDrcEnabled(false),
@@ -2417,7 +2651,7 @@
mUidCached = getuid();
mpClientInterface = clientInterface;
- mDefaultOutputDevice = new DeviceDescriptor(String8("Speaker"), AUDIO_DEVICE_OUT_SPEAKER);
+ mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
if (ConfigParsingUtils::loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE,
mHwModules, mAvailableInputDevices, mAvailableOutputDevices,
mDefaultOutputDevice, mSpeakerDrcEnabled) != NO_ERROR) {
@@ -2474,7 +2708,8 @@
if ((profileType & outputDeviceTypes) == 0) {
continue;
}
- sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile);
+ sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
+ mpClientInterface);
outputDesc->mDevice = profileType;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -2482,7 +2717,7 @@
config.channel_mask = outputDesc->mChannelMask;
config.format = outputDesc->mFormat;
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle,
+ status_t status = mpClientInterface->openOutput(outProfile->getModuleHandle(),
&output,
&config,
&outputDesc->mDevice,
@@ -2510,10 +2745,10 @@
}
if (mPrimaryOutput == 0 &&
outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
- mPrimaryOutput = output;
+ mPrimaryOutput = outputDesc;
}
addOutput(output, outputDesc);
- setOutputDevice(output,
+ setOutputDevice(outputDesc,
outputDesc->mDevice,
true);
}
@@ -2558,7 +2793,7 @@
config.channel_mask = inputDesc->mChannelMask;
config.format = inputDesc->mFormat;
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openInput(inProfile->mModule->mHandle,
+ status_t status = mpClientInterface->openInput(inProfile->getModuleHandle(),
&input,
&config,
&inputDesc->mDevice,
@@ -2620,7 +2855,7 @@
if (mPrimaryOutput != 0) {
AudioParameter outputCmd = AudioParameter();
outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
+ mpClientInterface->setParameters(mPrimaryOutput->mIoHandle, outputCmd.toString());
mTestDevice = AUDIO_DEVICE_OUT_SPEAKER;
mTestSamplingRate = 44100;
@@ -2760,20 +2995,21 @@
if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
param.remove(String8("test_cmd_policy_reopen"));
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
- mpClientInterface->closeOutput(mPrimaryOutput);
+ mpClientInterface->closeOutput(mpClientInterface->closeOutput(mPrimaryOutput););
- audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle;
+ audio_module_handle_t moduleHandle = mPrimaryOutput->getModuleHandle();
- removeOutput(mPrimaryOutput);
- sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+ removeOutput(mPrimaryOutput->mIoHandle);
+ sp<SwAudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL,
+ mpClientInterface);
outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = outputDesc->mSamplingRate;
config.channel_mask = outputDesc->mChannelMask;
config.format = outputDesc->mFormat;
+ audio_io_handle_t handle;
status_t status = mpClientInterface->openOutput(moduleHandle,
- &mPrimaryOutput,
+ &handle,
&config,
&outputDesc->mDevice,
String8(""),
@@ -2787,10 +3023,11 @@
outputDesc->mSamplingRate = config.sample_rate;
outputDesc->mChannelMask = config.channel_mask;
outputDesc->mFormat = config.format;
+ mPrimaryOutput = outputDesc;
AudioParameter outputCmd = AudioParameter();
outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
- addOutput(mPrimaryOutput, outputDesc);
+ mpClientInterface->setParameters(handle, outputCmd.toString());
+ addOutput(handle, outputDesc);
}
}
@@ -2822,7 +3059,7 @@
// ---
-void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc)
{
outputDesc->setIoHandle(output);
mOutputs.add(output, outputDesc);
@@ -2841,7 +3078,7 @@
nextAudioPortGeneration();
}
-void AudioPolicyManager::findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/,
+void AudioPolicyManager::findIoHandlesByAddress(sp<SwAudioOutputDescriptor> desc /*in*/,
const audio_devices_t device /*in*/,
const String8 address /*in*/,
SortedVector<audio_io_handle_t>& outputs /*out*/) {
@@ -2860,7 +3097,7 @@
const String8 address)
{
audio_devices_t device = devDesc->type();
- sp<AudioOutputDescriptor> desc;
+ sp<SwAudioOutputDescriptor> desc;
// erase all current sample rates, formats and channel masks
devDesc->clearCapabilities();
@@ -2868,7 +3105,7 @@
// first list already open outputs that can be routed to this device
for (size_t i = 0; i < mOutputs.size(); i++) {
desc = mOutputs.valueAt(i);
- if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices.types() & device)) {
+ if (!desc->isDuplicated() && (desc->supportedDevices() & device)) {
if (!device_distinguishes_on_address(device)) {
ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
@@ -2927,7 +3164,7 @@
ALOGV("opening output for device %08x with params %s profile %p",
device, address.string(), profile.get());
- desc = new AudioOutputDescriptor(profile);
+ desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
desc->mDevice = device;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = desc->mSamplingRate;
@@ -2937,7 +3174,7 @@
config.offload_info.channel_mask = desc->mChannelMask;
config.offload_info.format = desc->mFormat;
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openOutput(profile->mModule->mHandle,
+ status_t status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&desc->mDevice,
@@ -3007,7 +3244,7 @@
config.offload_info.sample_rate = config.sample_rate;
config.offload_info.channel_mask = config.channel_mask;
config.offload_info.format = config.format;
- status = mpClientInterface->openOutput(profile->mModule->mHandle,
+ status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&desc->mDevice,
@@ -3032,7 +3269,7 @@
address.string());
}
policyMix->setOutput(desc);
- desc->mPolicyMix = &(policyMix->getMix());
+ desc->mPolicyMix = policyMix->getMix();
} else if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) {
// no duplicated output for direct outputs and
@@ -3040,28 +3277,29 @@
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
// set initial stream volume for device
- applyStreamVolumes(output, device, 0, true);
+ applyStreamVolumes(desc, device, 0, true);
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
- duplicatedOutput = mpClientInterface->openDuplicateOutput(output,
- mPrimaryOutput);
+ duplicatedOutput =
+ mpClientInterface->openDuplicateOutput(output,
+ mPrimaryOutput->mIoHandle);
if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) {
// add duplicated output descriptor
- sp<AudioOutputDescriptor> dupOutputDesc =
- new AudioOutputDescriptor(NULL);
- dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput);
- dupOutputDesc->mOutput2 = mOutputs.valueFor(output);
+ sp<SwAudioOutputDescriptor> dupOutputDesc =
+ new SwAudioOutputDescriptor(NULL, mpClientInterface);
+ dupOutputDesc->mOutput1 = mPrimaryOutput;
+ dupOutputDesc->mOutput2 = desc;
dupOutputDesc->mSamplingRate = desc->mSamplingRate;
dupOutputDesc->mFormat = desc->mFormat;
dupOutputDesc->mChannelMask = desc->mChannelMask;
dupOutputDesc->mLatency = desc->mLatency;
addOutput(duplicatedOutput, dupOutputDesc);
- applyStreamVolumes(duplicatedOutput, device, 0, true);
+ applyStreamVolumes(dupOutputDesc, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
- mPrimaryOutput, output);
+ mPrimaryOutput->mIoHandle, output);
mpClientInterface->closeOutput(output);
removeOutput(output);
nextAudioPortGeneration();
@@ -3083,7 +3321,7 @@
if (device_distinguishes_on_address(device)) {
ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)",
device, address.string());
- setOutputDevice(output, device, true/*force*/, 0/*delay*/,
+ setOutputDevice(desc, device, true/*force*/, 0/*delay*/,
NULL/*patch handle*/, address.string());
}
ALOGV("checkOutputsForDevice(): adding output %d", output);
@@ -3101,10 +3339,9 @@
if (!desc->isDuplicated()) {
// exact match on device
if (device_distinguishes_on_address(device) &&
- (desc->mProfile->mSupportedDevices.types() == device)) {
+ (desc->supportedDevices() == device)) {
findIoHandlesByAddress(desc, device, address, outputs);
- } else if (!(desc->mProfile->mSupportedDevices.types()
- & mAvailableOutputDevices.types())) {
+ } else if (!(desc->supportedDevices() & mAvailableOutputDevices.types())) {
ALOGV("checkOutputsForDevice(): disconnecting adding output %d",
mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
@@ -3212,7 +3449,7 @@
config.channel_mask = desc->mChannelMask;
config.format = desc->mFormat;
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openInput(profile->mModule->mHandle,
+ status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
&input,
&config,
&desc->mDevice,
@@ -3339,7 +3576,7 @@
{
ALOGV("closeOutput(%d)", output);
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
if (outputDesc == NULL) {
ALOGW("closeOutput() unknown output %d", output);
return;
@@ -3348,7 +3585,7 @@
// look for duplicated outputs connected to the output being removed.
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
if (dupOutputDesc->isDuplicated() &&
(dupOutputDesc->mOutput1 == outputDesc ||
dupOutputDesc->mOutput2 == outputDesc)) {
@@ -3417,15 +3654,17 @@
mInputs.removeItem(input);
}
-SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
- AudioOutputCollection openOutputs)
+SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(
+ audio_devices_t device,
+ SwAudioOutputCollection openOutputs)
{
SortedVector<audio_io_handle_t> outputs;
ALOGVV("getOutputsForDevice() device %04x", device);
for (size_t i = 0; i < openOutputs.size(); i++) {
ALOGVV("output %d isDuplicated=%d device=%04x",
- i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices());
+ i, openOutputs.valueAt(i)->isDuplicated(),
+ openOutputs.valueAt(i)->supportedDevices());
if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) {
ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i));
outputs.add(openOutputs.keyAt(i));
@@ -3459,14 +3698,14 @@
// associated with policies in the "before" and "after" output vectors
ALOGVV("checkOutputForStrategy(): policy related outputs");
for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {
- const sp<AudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
+ const sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
if (desc != 0 && desc->mPolicyMix != NULL) {
srcOutputs.add(desc->mIoHandle);
ALOGVV(" previous outputs: adding %d", desc->mIoHandle);
}
}
for (size_t i = 0 ; i < mOutputs.size() ; i++) {
- const sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ const sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != 0 && desc->mPolicyMix != NULL) {
dstOutputs.add(desc->mIoHandle);
ALOGVV(" new outputs: adding %d", desc->mIoHandle);
@@ -3478,10 +3717,10 @@
strategy, srcOutputs[0], dstOutputs[0]);
// mute strategy while moving tracks from one output to another
for (size_t i = 0; i < srcOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
if (isStrategyActive(desc, strategy)) {
- setStrategyMute(strategy, true, srcOutputs[i]);
- setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);
+ setStrategyMute(strategy, true, desc);
+ setStrategyMute(strategy, false, desc, MUTE_TIME_MS, newDevice);
}
}
@@ -3578,12 +3817,11 @@
}
}
-audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, bool fromCache)
+audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
+ bool fromCache)
{
audio_devices_t device = AUDIO_DEVICE_NONE;
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-
ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
@@ -3657,7 +3895,6 @@
audio_devices_t device = getDeviceAndMixForInputSource(inputDesc->mInputSource);
- ALOGV("getNewInputDevice() selected device %x", device);
return device;
}
@@ -3761,9 +3998,9 @@
ALOGV("\t muting %d", mute);
uint32_t maxLatency = 0;
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
setStreamMute(AUDIO_STREAM_TTS, mute/*on*/,
- desc->mIoHandle,
+ desc,
0 /*delay*/, AUDIO_DEVICE_NONE);
const uint32_t latency = desc->latency() * 2;
if (latency > maxLatency) {
@@ -3779,6 +4016,21 @@
audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
bool fromCache)
{
+ // Routing
+ // see if we have an explicit route
+ // scan the whole RouteMap, for each entry, convert the stream type to a strategy
+ // (getStrategy(stream)).
+ // if the strategy from the stream type in the RouteMap is the same as the argument above,
+ // and activity count is non-zero
+ // the device = the device from the descriptor in the RouteMap, and exit.
+ for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
+ sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
+ routing_strategy strat = getStrategy(route->mStreamType);
+ if (strat == strategy && route->isActive()) {
+ return route->mDeviceDescriptor->type();
+ }
+ }
+
if (fromCache) {
ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
strategy, mDeviceForStrategy[strategy]);
@@ -3812,7 +4064,7 @@
for (size_t i = 0; i < NUM_STRATEGIES; i++) {
audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/);
- curDevice = curDevice & outputDesc->mProfile->mSupportedDevices.types();
+ curDevice = curDevice & outputDesc->supportedDevices();
bool mute = shouldMute && (curDevice & device) && (curDevice != device);
bool doMute = false;
@@ -3831,10 +4083,9 @@
== AUDIO_DEVICE_NONE) {
continue;
}
- audio_io_handle_t curOutput = mOutputs.keyAt(j);
- ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d",
- mute ? "muting" : "unmuting", i, curDevice, curOutput);
- setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs);
+ ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x)",
+ mute ? "muting" : "unmuting", i, curDevice);
+ setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs);
if (isStrategyActive(desc, (routing_strategy)i)) {
if (mute) {
// FIXME: should not need to double latency if volume could be applied
@@ -3859,9 +4110,9 @@
}
for (size_t i = 0; i < NUM_STRATEGIES; i++) {
if (isStrategyActive(outputDesc, (routing_strategy)i)) {
- setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle);
+ setStrategyMute((routing_strategy)i, true, outputDesc);
// do tempMute unmute after twice the mute wait time
- setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle,
+ setStrategyMute((routing_strategy)i, false, outputDesc,
muteWaitMs *2, device);
}
}
@@ -3876,36 +4127,35 @@
return 0;
}
-uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
+uint32_t AudioPolicyManager::setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
bool force,
int delayMs,
audio_patch_handle_t *patchHandle,
const char* address)
{
- ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ ALOGV("setOutputDevice() device %04x delayMs %d", device, delayMs);
AudioParameter param;
uint32_t muteWaitMs;
if (outputDesc->isDuplicated()) {
- muteWaitMs = setOutputDevice(outputDesc->mOutput1->mIoHandle, device, force, delayMs);
- muteWaitMs += setOutputDevice(outputDesc->mOutput2->mIoHandle, device, force, delayMs);
+ muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);
+ muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);
return muteWaitMs;
}
// no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
// output profile
if ((device != AUDIO_DEVICE_NONE) &&
- ((device & outputDesc->mProfile->mSupportedDevices.types()) == 0)) {
+ ((device & outputDesc->supportedDevices()) == 0)) {
return 0;
}
// filter devices according to output selected
- device = (audio_devices_t)(device & outputDesc->mProfile->mSupportedDevices.types());
+ device = (audio_devices_t)(device & outputDesc->supportedDevices());
audio_devices_t prevDevice = outputDesc->mDevice;
- ALOGV("setOutputDevice() prevDevice %04x", prevDevice);
+ ALOGV("setOutputDevice() prevDevice 0x%04x", prevDevice);
if (device != AUDIO_DEVICE_NONE) {
outputDesc->mDevice = device;
@@ -3918,10 +4168,10 @@
// AND force is not specified
// AND the output is connected by a valid audio patch.
// Doing this check here allows the caller to call setOutputDevice() without conditions
- if ((device == AUDIO_DEVICE_NONE || device == prevDevice) && !force &&
- outputDesc->mPatchHandle != 0) {
- ALOGV("setOutputDevice() setting same device %04x or null device for output %d",
- device, output);
+ if ((device == AUDIO_DEVICE_NONE || device == prevDevice) &&
+ !force &&
+ outputDesc->mPatchHandle != 0) {
+ ALOGV("setOutputDevice() setting same device 0x%04x or null device", device);
return muteWaitMs;
}
@@ -3929,7 +4179,7 @@
// do the routing
if (device == AUDIO_DEVICE_NONE) {
- resetOutputDevice(output, delayMs, NULL);
+ resetOutputDevice(outputDesc, delayMs, NULL);
} else {
DeviceVector deviceList = (address == NULL) ?
mAvailableOutputDevices.getDevicesFromType(device)
@@ -3996,16 +4246,15 @@
}
// update stream volumes according to new device
- applyStreamVolumes(output, device, delayMs);
+ applyStreamVolumes(outputDesc, device, delayMs);
return muteWaitMs;
}
-status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
+status_t AudioPolicyManager::resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
int delayMs,
audio_patch_handle_t *patchHandle)
{
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
ssize_t index;
if (patchHandle) {
index = mAudioPatches.indexOfKey(*patchHandle);
@@ -4115,12 +4364,15 @@
sp<IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device,
String8 address,
uint32_t& samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
+ audio_format_t& format,
+ audio_channel_mask_t& channelMask,
audio_input_flags_t flags)
{
// Choose an input profile based on the requested capture parameters: select the first available
// profile supporting all requested parameters.
+ //
+ // TODO: perhaps isCompatibleProfile should return a "matching" score so we can return
+ // the best matching profile, not the first one.
for (size_t i = 0; i < mHwModules.size(); i++)
{
@@ -4133,7 +4385,11 @@
// profile->log();
if (profile->isCompatibleProfile(device, address, samplingRate,
&samplingRate /*updatedSamplingRate*/,
- format, channelMask, (audio_output_flags_t) flags)) {
+ format,
+ &format /*updatedFormat*/,
+ channelMask,
+ &channelMask /*updatedChannelMask*/,
+ (audio_output_flags_t) flags)) {
return profile;
}
@@ -4158,21 +4414,21 @@
audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource)
{
- return mEngine->getDeviceForInputSource(inputSource);
+ for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) {
+ sp<SessionRoute> route = mInputRoutes.valueAt(routeIndex);
+ if (inputSource == route->mSource && route->isActive()) {
+ return route->mDeviceDescriptor->type();
+ }
+ }
+
+ return mEngine->getDeviceForInputSource(inputSource);
}
float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
- int index,
- audio_io_handle_t output,
- audio_devices_t device)
+ int index,
+ audio_devices_t device)
{
- float volume = 1.0;
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-
- if (device == AUDIO_DEVICE_NONE) {
- device = outputDesc->device();
- }
- volume = mEngine->volIndexToAmpl(Volume::getDeviceCategory(device), stream, index);
+ float volumeDb = mEngine->volIndexToDb(Volume::getDeviceCategory(device), stream, index);
// if a headset is connected, apply the following rules to ring tones and notifications
// to avoid sound level bursts in user's ears:
@@ -4190,41 +4446,39 @@
|| ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) &&
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
mStreams.canBeMuted(stream)) {
- volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
+ volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
// when the phone is ringing we must consider that music could have been paused just before
// by the music application and behave as if music was active if the last music track was
// just stopped
if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) ||
mLimitRingtoneVolume) {
audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/);
- float musicVol = computeVolume(AUDIO_STREAM_MUSIC,
- mStreams[AUDIO_STREAM_MUSIC].getVolumeIndex(musicDevice),
- output,
+ float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC,
+ mStreams.valueFor(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice),
musicDevice);
- float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ?
- musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
- if (volume > minVol) {
- volume = minVol;
- ALOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
+ float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
+ musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB;
+ if (volumeDb > minVolDB) {
+ volumeDb = minVolDB;
+ ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB);
}
}
}
- return volume;
+ return volumeDb;
}
status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
- int index,
- audio_io_handle_t output,
- audio_devices_t device,
- int delayMs,
- bool force)
+ int index,
+ const sp<AudioOutputDescriptor>& outputDesc,
+ audio_devices_t device,
+ int delayMs,
+ bool force)
{
-
// do not change actual stream volume if the stream is muted
- if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
+ if (outputDesc->mMuteCount[stream] != 0) {
ALOGVV("checkAndSetVolume() stream %d muted count %d",
- stream, mOutputs.valueFor(output)->mMuteCount[stream]);
+ stream, outputDesc->mMuteCount[stream]);
return NO_ERROR;
}
audio_policy_forced_cfg_t forceUseForComm =
@@ -4237,45 +4491,28 @@
return INVALID_OPERATION;
}
- float volume = computeVolume(stream, index, output, device);
- // unit gain if rerouting to external policy
- if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) {
- ssize_t index = mOutputs.indexOfKey(output);
- if (index >= 0) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
- if (outputDesc->mPolicyMix != NULL) {
- ALOGV("max gain when rerouting for output=%d", output);
- volume = 1.0f;
- }
- }
+ if (device == AUDIO_DEVICE_NONE) {
+ device = outputDesc->device();
+ }
+ float volumeDb = computeVolume(stream, index, device);
+ if (outputDesc->isFixedVolume(device)) {
+ volumeDb = 0.0f;
}
- // We actually change the volume if:
- // - the float value returned by computeVolume() changed
- // - the force flag is set
- if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
- force) {
- mOutputs.valueFor(output)->mCurVolume[stream] = volume;
- ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
- // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
- // enabled
- if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
- mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs);
- }
- mpClientInterface->setStreamVolume(stream, volume, output, delayMs);
- }
+
+ outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
if (stream == AUDIO_STREAM_VOICE_CALL ||
stream == AUDIO_STREAM_BLUETOOTH_SCO) {
float voiceVolume;
// Force voice volume to max for bluetooth SCO as volume is managed by the headset
if (stream == AUDIO_STREAM_VOICE_CALL) {
- voiceVolume = (float)index/(float)mStreams[stream].getVolumeIndexMax();
+ voiceVolume = (float)index/(float)mStreams.valueFor(stream).getVolumeIndexMax();
} else {
voiceVolume = 1.0;
}
- if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) {
+ if (voiceVolume != mLastVoiceVolume && outputDesc == mPrimaryOutput) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
@@ -4284,20 +4521,20 @@
return NO_ERROR;
}
-void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output,
- audio_devices_t device,
- int delayMs,
- bool force)
+void AudioPolicyManager::applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
+ audio_devices_t device,
+ int delayMs,
+ bool force)
{
- ALOGVV("applyStreamVolumes() for output %d and device %x", output, device);
+ ALOGVV("applyStreamVolumes() for device %08x", device);
for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
if (stream == AUDIO_STREAM_PATCH) {
continue;
}
checkAndSetVolume((audio_stream_type_t)stream,
- mStreams[stream].getVolumeIndex(device),
- output,
+ mStreams.valueFor((audio_stream_type_t)stream).getVolumeIndex(device),
+ outputDesc,
device,
delayMs,
force);
@@ -4305,10 +4542,10 @@
}
void AudioPolicyManager::setStrategyMute(routing_strategy strategy,
- bool on,
- audio_io_handle_t output,
- int delayMs,
- audio_devices_t device)
+ bool on,
+ const sp<AudioOutputDescriptor>& outputDesc,
+ int delayMs,
+ audio_devices_t device)
{
ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
@@ -4316,32 +4553,31 @@
continue;
}
if (getStrategy((audio_stream_type_t)stream) == strategy) {
- setStreamMute((audio_stream_type_t)stream, on, output, delayMs, device);
+ setStreamMute((audio_stream_type_t)stream, on, outputDesc, delayMs, device);
}
}
}
void AudioPolicyManager::setStreamMute(audio_stream_type_t stream,
- bool on,
- audio_io_handle_t output,
- int delayMs,
- audio_devices_t device)
+ bool on,
+ const sp<AudioOutputDescriptor>& outputDesc,
+ int delayMs,
+ audio_devices_t device)
{
- const StreamDescriptor &streamDesc = mStreams[stream];
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ const StreamDescriptor& streamDesc = mStreams.valueFor(stream);
if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->device();
}
- ALOGVV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d device %04x",
- stream, on, output, outputDesc->mMuteCount[stream], device);
+ ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x",
+ stream, on, outputDesc->mMuteCount[stream], device);
if (on) {
if (outputDesc->mMuteCount[stream] == 0) {
if (streamDesc.canBeMuted() &&
((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) ||
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) {
- checkAndSetVolume(stream, 0, output, device, delayMs);
+ checkAndSetVolume(stream, 0, outputDesc, device, delayMs);
}
}
// increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
@@ -4354,7 +4590,7 @@
if (--outputDesc->mMuteCount[stream] == 0) {
checkAndSetVolume(stream,
streamDesc.getVolumeIndex(device),
- output,
+ outputDesc,
device,
delayMs);
}
@@ -4373,7 +4609,7 @@
const routing_strategy stream_strategy = getStrategy(stream);
if ((stream_strategy == STRATEGY_SONIFICATION) ||
((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
+ sp<SwAudioOutputDescriptor> outputDesc = mPrimaryOutput;
ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
stream, starting, outputDesc->mDevice, stateChange);
if (outputDesc->mRefCount[stream]) {
@@ -4406,18 +4642,113 @@
}
}
+// --- SessionRoute class implementation
+void AudioPolicyManager::SessionRoute::log(const char* prefix) {
+ ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d",
+ prefix, mStreamType, mSource, mSession,
+ mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE,
+ mRefCount, mActivityCount);
+}
+
+// --- SessionRouteMap class implementation
+bool AudioPolicyManager::SessionRouteMap::hasRoute(audio_session_t session)
+{
+ return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0;
+}
+
+bool AudioPolicyManager::SessionRouteMap::hasRouteChanged(audio_session_t session)
+{
+ if (indexOfKey(session) >= 0) {
+ if (valueFor(session)->mChanged) {
+ valueFor(session)->mChanged = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioPolicyManager::SessionRouteMap::removeRoute(audio_session_t session)
+{
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+ if (route != 0) {
+ ALOG_ASSERT(route->mRefCount > 0);
+ --route->mRefCount;
+ if (route->mRefCount <= 0) {
+ removeItem(session);
+ }
+ }
+}
+
+int AudioPolicyManager::SessionRouteMap::incRouteActivity(audio_session_t session)
+{
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+ return route != 0 ? ++(route->mActivityCount) : -1;
+}
+
+int AudioPolicyManager::SessionRouteMap::decRouteActivity(audio_session_t session)
+{
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+ if (route != 0 && route->mActivityCount > 0) {
+ return --(route->mActivityCount);
+ } else {
+ return -1;
+ }
+}
+
+void AudioPolicyManager::SessionRouteMap::log(const char* caption) {
+ ALOGI("%s ----", caption);
+ for(size_t index = 0; index < size(); index++) {
+ valueAt(index)->log(" ");
+ }
+}
+
+void AudioPolicyManager::SessionRouteMap::addRoute(audio_session_t session,
+ audio_stream_type_t streamType,
+ audio_source_t source,
+ sp<DeviceDescriptor> descriptor,
+ uid_t uid)
+{
+ if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) {
+ ALOGE("Adding Output Route to InputRouteMap");
+ return;
+ } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) {
+ ALOGE("Adding Input Route to OutputRouteMap");
+ return;
+ }
+
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+
+ if (route != 0) {
+ if (((route->mDeviceDescriptor == 0) && (descriptor != 0)) ||
+ ((route->mDeviceDescriptor != 0) &&
+ ((descriptor == 0) || (!route->mDeviceDescriptor->equals(descriptor))))) {
+ route->mChanged = true;
+ }
+ route->mRefCount++;
+ route->mDeviceDescriptor = descriptor;
+ } else {
+ route = new AudioPolicyManager::SessionRoute(session, streamType, source, descriptor, uid);
+ route->mRefCount++;
+ add(session, route);
+ if (descriptor != 0) {
+ route->mChanged = true;
+ }
+ }
+}
+
void AudioPolicyManager::defaultAudioPolicyConfig(void)
{
sp<HwModule> module;
sp<IOProfile> profile;
sp<DeviceDescriptor> defaultInputDevice =
- new DeviceDescriptor(String8("builtin-mic"), AUDIO_DEVICE_IN_BUILTIN_MIC);
+ new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
mAvailableOutputDevices.add(mDefaultOutputDevice);
mAvailableInputDevices.add(defaultInputDevice);
module = new HwModule("primary");
- profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE, module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE);
+ profile->attach(module);
profile->mSamplingRates.add(44100);
profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO);
@@ -4425,7 +4756,8 @@
profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY;
module->mOutputProfiles.add(profile);
- profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK, module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK);
+ profile->attach(module);
profile->mSamplingRates.add(8000);
profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 02b678a..ea16864 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -49,8 +49,11 @@
// Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB
#define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5
+#define SONIFICATION_HEADSET_VOLUME_FACTOR_DB (-6)
// Min volume for STRATEGY_SONIFICATION streams when limited by music volume: -36dB
#define SONIFICATION_HEADSET_VOLUME_MIN 0.016
+#define SONIFICATION_HEADSET_VOLUME_MIN_DB (-36)
+
// Time in milliseconds during which we consider that music is still active after a music
// track was stopped - see computeVolume()
#define SONIFICATION_HEADSET_MUSIC_DELAY 5000
@@ -106,10 +109,12 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -123,10 +128,12 @@
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
input_type_t *inputType);
// indicates to the audio policy manager that the input starts being used.
@@ -172,19 +179,15 @@
return mEffects.setEffectEnabled(id, enabled);
}
- virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const
- {
- return mOutputs.isStreamActive(stream, inPastMs);
- }
+ virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
// return whether a stream is playing remotely, override to change the definition of
// local/remote playback, used for instance by notification manager to not make
// media players lose audio focus when not playing locally
// For the base implementation, "remotely" means playing during screen mirroring which
// uses an output for playback with a non-empty, non "0" address.
- virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const
- {
- return mOutputs.isStreamActiveRemotely(stream, inPastMs);
- }
+ virtual bool isStreamActiveRemotely(audio_stream_type_t stream,
+ uint32_t inPastMs = 0) const;
+
virtual bool isSourceActive(audio_source_t source) const;
virtual status_t dump(int fd);
@@ -206,7 +209,6 @@
struct audio_patch *patches,
unsigned int *generation);
virtual status_t setAudioPortConfig(const struct audio_port_config *config);
- virtual void clearAudioPatches(uid_t uid);
virtual status_t acquireSoundTriggerSession(audio_session_t *session,
audio_io_handle_t *ioHandle,
@@ -220,6 +222,13 @@
virtual status_t registerPolicyMixes(Vector<AudioMix> mixes);
virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes);
+ virtual status_t startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle);
+ virtual status_t stopAudioSource(audio_io_handle_t handle);
+
+ virtual void releaseResourcesForUid(uid_t uid);
+
// Audio policy configuration file parsing (audio_policy.conf)
// TODO candidates to be moved to ConfigParsingUtils
void defaultAudioPolicyConfig(void);
@@ -227,6 +236,94 @@
// return the strategy corresponding to a given stream type
routing_strategy getStrategy(audio_stream_type_t stream) const;
+protected:
+ class SessionRoute : public RefBase {
+ public:
+ // For Input (Source) routes, use STREAM_TYPE_NA ("NA" = "not applicable)for the
+ // streamType argument
+ static const audio_stream_type_t STREAM_TYPE_NA = AUDIO_STREAM_DEFAULT;
+
+ // For Output (Sink) routes, use SOURCE_TYPE_NA ("NA" = "not applicable") for the
+ // source argument
+
+ static const audio_source_t SOURCE_TYPE_NA = AUDIO_SOURCE_DEFAULT;
+
+ SessionRoute(audio_session_t session,
+ audio_stream_type_t streamType,
+ audio_source_t source,
+ sp<DeviceDescriptor> deviceDescriptor,
+ uid_t uid)
+ : mUid(uid),
+ mSession(session),
+ mDeviceDescriptor(deviceDescriptor),
+ mRefCount(0),
+ mActivityCount(0),
+ mChanged(false),
+ mStreamType(streamType),
+ mSource(source)
+ {}
+
+ void log(const char* prefix);
+
+ bool isActive() {
+ return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0));
+ }
+
+ uid_t mUid;
+ audio_session_t mSession;
+ sp<DeviceDescriptor> mDeviceDescriptor;
+
+ // "reference" counting
+ int mRefCount; // +/- on references
+ int mActivityCount; // +/- on start/stop
+ bool mChanged;
+ // for outputs
+ const audio_stream_type_t mStreamType;
+ // for inputs
+ const audio_source_t mSource;
+ };
+
+ class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute>> {
+ public:
+ // These constants identify the SessionRoutMap as holding EITHER input routes,
+ // or output routes. An error will occur if an attempt is made to add a SessionRoute
+ // object with mStreamType == STREAM_TYPE_NA (i.e. an input SessionRoute) to a
+ // SessionRoutMap that is marked for output (i.e. mMapType == SESSION_ROUTE_MAP_OUTPUT)
+ // and similarly for output SessionRoutes and Input SessionRouteMaps.
+ typedef enum {
+ MAPTYPE_INPUT = 0,
+ MAPTYPE_OUTPUT = 1
+ } session_route_map_type_t;
+
+ SessionRouteMap(session_route_map_type_t mapType) :
+ mMapType(mapType) {
+ }
+
+ bool hasRoute(audio_session_t session);
+
+ void removeRoute(audio_session_t session);
+
+ int incRouteActivity(audio_session_t session);
+ int decRouteActivity(audio_session_t session);
+ bool hasRouteChanged(audio_session_t session); // also clears the changed flag
+ void log(const char* caption);
+
+ // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the
+ // source argument.
+ // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT
+ // in the streamType argument.
+ void addRoute(audio_session_t session,
+ audio_stream_type_t streamType,
+ audio_source_t source,
+ sp<DeviceDescriptor> deviceDescriptor,
+ uid_t uid);
+
+ private:
+ // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input)
+ // or outputs (mMapType == kSessionRouteMap_Output)
+ const session_route_map_type_t mMapType;
+ };
+
// From AudioPolicyManagerObserver
virtual const AudioPatchCollection &getAudioPatches() const
{
@@ -240,7 +337,7 @@
{
return mPolicyMixes;
}
- virtual const AudioOutputCollection &getOutputs() const
+ virtual const SwAudioOutputCollection &getOutputs() const
{
return mOutputs;
}
@@ -265,7 +362,7 @@
return mDefaultOutputDevice;
}
protected:
- void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc);
+ void addOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc);
void removeOutput(audio_io_handle_t output);
void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc);
@@ -288,13 +385,13 @@
// change the route of the specified output. Returns the number of ms we have slept to
// allow new routing to take effect in certain cases.
- virtual uint32_t setOutputDevice(audio_io_handle_t output,
+ virtual uint32_t setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
bool force = false,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL,
const char* address = NULL);
- status_t resetOutputDevice(audio_io_handle_t output,
+ status_t resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL);
status_t setInputDevice(audio_io_handle_t input,
@@ -309,29 +406,31 @@
// compute the actual volume for a given stream according to the requested index and a particular
// device
- virtual float computeVolume(audio_stream_type_t stream, int index,
- audio_io_handle_t output, audio_devices_t device);
+ virtual float computeVolume(audio_stream_type_t stream,
+ int index,
+ audio_devices_t device);
// check that volume change is permitted, compute and send new volume to audio hardware
virtual status_t checkAndSetVolume(audio_stream_type_t stream, int index,
- audio_io_handle_t output,
+ const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
int delayMs = 0, bool force = false);
// apply all stream volumes to the specified output and device
- void applyStreamVolumes(audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false);
+ void applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
+ audio_devices_t device, int delayMs = 0, bool force = false);
// Mute or unmute all streams handled by the specified strategy on the specified output
void setStrategyMute(routing_strategy strategy,
bool on,
- audio_io_handle_t output,
+ const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_devices_t device = (audio_devices_t)0);
// Mute or unmute the stream on the specified output
void setStreamMute(audio_stream_type_t stream,
bool on,
- audio_io_handle_t output,
+ const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_devices_t device = (audio_devices_t)0);
@@ -384,7 +483,8 @@
// must be called every time a condition that affects the device choice for a given output is
// changed: connected device, phone state, force use, output start, output stop..
// see getDeviceForStrategy() for the use of fromCache parameter
- audio_devices_t getNewOutputDevice(audio_io_handle_t output, bool fromCache);
+ audio_devices_t getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
+ bool fromCache);
// updates cache of device used by all strategies (mDeviceForStrategy[])
// must be called every time a condition that affects the device choice for a given strategy is
@@ -412,7 +512,7 @@
#endif //AUDIO_POLICY_TEST
SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device,
- AudioOutputCollection openOutputs);
+ SwAudioOutputCollection openOutputs);
bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1,
SortedVector<audio_io_handle_t>& outputs2);
@@ -427,12 +527,12 @@
audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs,
audio_output_flags_t flags,
audio_format_t format);
- // samplingRate parameter is an in/out and so may be modified
+ // samplingRate, format, channelMask are in/out and so may be modified
sp<IOProfile> getInputProfile(audio_devices_t device,
String8 address,
uint32_t& samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
+ audio_format_t& format,
+ audio_channel_mask_t& channelMask,
audio_input_flags_t flags);
sp<IOProfile> getProfileForDirectOutput(audio_devices_t device,
uint32_t samplingRate,
@@ -453,28 +553,46 @@
audio_devices_t availablePrimaryOutputDevices() const
{
- return mOutputs.getSupportedDevices(mPrimaryOutput) & mAvailableOutputDevices.types();
+ return mPrimaryOutput->supportedDevices() & mAvailableOutputDevices.types();
}
audio_devices_t availablePrimaryInputDevices() const
{
- return mAvailableInputDevices.getDevicesFromHwModule(
- mOutputs.valueFor(mPrimaryOutput)->getModuleHandle());
+ return mAvailableInputDevices.getDevicesFromHwModule(mPrimaryOutput->getModuleHandle());
}
void updateCallRouting(audio_devices_t rxDevice, int delayMs = 0);
+ // if argument "device" is different from AUDIO_DEVICE_NONE, startSource() will force
+ // the re-evaluation of the output device.
+ status_t startSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t *delayMs);
+ status_t stopSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream,
+ bool forceDeviceUpdate);
+
+ void clearAudioPatches(uid_t uid);
+ void clearSessionRoutes(uid_t uid);
+ void checkStrategyRoute(routing_strategy strategy, audio_io_handle_t ouptutToSkip);
+
uid_t mUidCached;
AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
- audio_io_handle_t mPrimaryOutput; // primary output handle
+ sp<SwAudioOutputDescriptor> mPrimaryOutput; // primary output descriptor
// list of descriptors for outputs currently opened
- AudioOutputCollection mOutputs;
+
+ SwAudioOutputCollection mOutputs;
// copy of mOutputs before setDeviceConnectionState() opens new outputs
// reset to mOutputs when updateDevicesAndOutputs() is called.
- AudioOutputCollection mPreviousOutputs;
+ SwAudioOutputCollection mPreviousOutputs;
AudioInputCollection mInputs; // list of input descriptors
+
DeviceVector mAvailableOutputDevices; // all available output devices
DeviceVector mAvailableInputDevices; // all available input devices
+ SessionRouteMap mOutputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_OUTPUT);
+ SessionRouteMap mInputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_INPUT);
+
StreamDescriptorCollection mStreams; // stream descriptors for volume control
bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected
audio_devices_t mDeviceForStrategy[NUM_STRATEGIES];
@@ -539,7 +657,7 @@
// in mProfile->mSupportedDevices) matches the device whose address is to be matched.
// see deviceDistinguishesOnAddress(audio_devices_t) for whether the device type is one
// where addresses are used to distinguish between one connected device and another.
- void findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/,
+ void findIoHandlesByAddress(sp<SwAudioOutputDescriptor> desc /*in*/,
const audio_devices_t device /*in*/,
const String8 address /*in*/,
SortedVector<audio_io_handle_t>& outputs /*out*/);
diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 3e090e9..489a9be 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -213,6 +213,12 @@
mAudioPolicyService->onAudioPatchListUpdate();
}
+void AudioPolicyService::AudioPolicyClient::onDynamicPolicyMixStateUpdate(
+ String8 regId, int32_t state)
+{
+ mAudioPolicyService->onDynamicPolicyMixStateUpdate(regId, state);
+}
+
audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId()
{
return AudioSystem::newAudioUniqueId();
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index e6ace20..282ddeb 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -109,8 +109,8 @@
Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0,
- audioSession, input);
+ sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0,
+ 0, audioSession, input);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("addInputEffects(): failed to create Fx %s on source %d",
@@ -254,7 +254,7 @@
Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, 0, 0, 0,
+ sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0,
audioSession, output);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index e9ff8389..65639c3 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -146,10 +146,12 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
if (mAudioPolicyManager == NULL) {
@@ -157,8 +159,17 @@
}
ALOGV("getOutput()");
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, samplingRate,
- format, channelMask, flags, offloadInfo);
+
+ // if the caller is us, trust the specified uid
+ if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) {
+ uid_t newclientUid = IPCThreadState::self()->getCallingUid();
+ if (uid != (uid_t)-1 && uid != newclientUid) {
+ ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid);
+ }
+ uid = newclientUid;
+ }
+ return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, samplingRate,
+ format, channelMask, flags, selectedDeviceId, offloadInfo);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -247,10 +258,12 @@
status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_input_flags_t flags)
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
@@ -261,19 +274,28 @@
return BAD_VALUE;
}
- if (((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) ||
- ((attr->source == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) {
+ if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
return BAD_VALUE;
}
sp<AudioPolicyEffects>audioPolicyEffects;
status_t status;
AudioPolicyInterface::input_type_t inputType;
+ // if the caller is us, trust the specified uid
+ if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) {
+ uid_t newclientUid = IPCThreadState::self()->getCallingUid();
+ if (uid != (uid_t)-1 && uid != newclientUid) {
+ ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid);
+ }
+ uid = newclientUid;
+ }
+
{
Mutex::Autolock _l(mLock);
// the audio_in_acoustics_t parameter is ignored by get_input()
- status = mAudioPolicyManager->getInputForAttr(attr, input, session,
+ status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,
samplingRate, format, channelMask,
- flags, &inputType);
+ flags, selectedDeviceId,
+ &inputType);
audioPolicyEffects = mAudioPolicyEffects;
if (status == NO_ERROR) {
@@ -546,9 +568,6 @@
unsigned int *generation)
{
Mutex::Autolock _l(mLock);
- if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
- }
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
@@ -559,9 +578,6 @@
status_t AudioPolicyService::getAudioPort(struct audio_port *port)
{
Mutex::Autolock _l(mLock);
- if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
- }
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
@@ -602,9 +618,6 @@
unsigned int *generation)
{
Mutex::Autolock _l(mLock);
- if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
- }
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
@@ -661,4 +674,26 @@
}
}
+status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle)
+{
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->startAudioSource(source, attributes, handle);
+}
+
+status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+{
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->stopAudioSource(handle);
+}
+
}; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
index 5a91192..13af3ef 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
@@ -234,10 +234,12 @@
status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid __unused,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_input_flags_t flags __unused)
+ audio_input_flags_t flags __unused,
+ audio_port_handle_t selectedDeviceId __unused)
{
if (mpAudioPolicy == NULL) {
return NO_INIT;
@@ -255,8 +257,7 @@
inputSource = AUDIO_SOURCE_MIC;
}
- if (((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) ||
- ((inputSource == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) {
+ if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
return BAD_VALUE;
}
@@ -565,10 +566,12 @@
audio_io_handle_t *output,
audio_session_t session __unused,
audio_stream_type_t *stream,
+ uid_t uid __unused,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId __unused,
const audio_offload_info_t *offloadInfo)
{
if (attr != NULL) {
@@ -604,4 +607,16 @@
return INVALID_OPERATION;
}
+status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle)
+{
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+{
+ return INVALID_OPERATION;
+}
+
}; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 00f188f..c5f4fb7 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -177,7 +177,7 @@
{
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager) {
- mAudioPolicyManager->clearAudioPatches(uid);
+ mAudioPolicyManager->releaseResourcesForUid(uid);
}
}
#endif
@@ -222,6 +222,21 @@
}
}
+void AudioPolicyService::onDynamicPolicyMixStateUpdate(String8 regId, int32_t state)
+{
+ ALOGV("AudioPolicyService::onDynamicPolicyMixStateUpdate(%s, %d)",
+ regId.string(), state);
+ mOutputCommandThread->dynamicPolicyMixStateUpdateCommand(regId, state);
+}
+
+void AudioPolicyService::doOnDynamicPolicyMixStateUpdate(String8 regId, int32_t state)
+{
+ Mutex::Autolock _l(mNotificationClientsLock);
+ for (size_t i = 0; i < mNotificationClients.size(); i++) {
+ mNotificationClients.valueAt(i)->onDynamicPolicyMixStateUpdate(regId, state);
+ }
+}
+
status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config,
int delayMs)
{
@@ -262,6 +277,14 @@
}
}
+void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate(
+ String8 regId, int32_t state)
+{
+ if (mAudioPolicyServiceClient != 0) {
+ mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state);
+ }
+}
+
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
IPCThreadState::self()->getCallingPid());
@@ -511,6 +534,20 @@
command->mStatus = af->setAudioPortConfig(&data->mConfig);
}
} break;
+ case DYN_POLICY_MIX_STATE_UPDATE: {
+ DynPolicyMixStateUpdateData *data =
+ (DynPolicyMixStateUpdateData *)command->mParam.get();
+ //###ALOGV("AudioCommandThread() processing dyn policy mix state update");
+ ALOGV("AudioCommandThread() processing dyn policy mix state update %s %d",
+ data->mRegId.string(), data->mState);
+ svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnDynamicPolicyMixStateUpdate(data->mRegId, data->mState);
+ mLock.lock();
+ } break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
}
@@ -747,6 +784,20 @@
return sendCommand(command, delayMs);
}
+void AudioPolicyService::AudioCommandThread::dynamicPolicyMixStateUpdateCommand(
+ String8 regId, int32_t state)
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = DYN_POLICY_MIX_STATE_UPDATE;
+ DynPolicyMixStateUpdateData *data = new DynPolicyMixStateUpdateData();
+ data->mRegId = regId;
+ data->mState = state;
+ command->mParam = data;
+ ALOGV("AudioCommandThread() sending dynamic policy mix (id=%s) state update to %d",
+ regId.string(), state);
+ sendCommand(command);
+}
+
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
{
@@ -888,6 +939,10 @@
delayMs = 1;
} break;
+ case DYN_POLICY_MIX_STATE_UPDATE: {
+
+ } break;
+
case START_TONE:
case STOP_TONE:
default:
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 0378384..eb50cdd 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -80,10 +80,12 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
+ uid_t uid,
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
const audio_offload_info_t *offloadInfo = NULL);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -97,10 +99,12 @@
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
+ uid_t uid,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_input_flags_t flags);
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
virtual status_t startInput(audio_io_handle_t input,
audio_session_t session);
virtual status_t stopInput(audio_io_handle_t input,
@@ -191,6 +195,11 @@
virtual status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration);
+ virtual status_t startAudioSource(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_io_handle_t *handle);
+ virtual status_t stopAudioSource(audio_io_handle_t handle);
+
status_t doStopOutput(audio_io_handle_t output,
audio_stream_type_t stream,
audio_session_t session);
@@ -212,6 +221,9 @@
void onAudioPatchListUpdate();
void doOnAudioPatchListUpdate();
+ void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
+ void doOnDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
+
private:
AudioPolicyService() ANDROID_API;
virtual ~AudioPolicyService();
@@ -242,6 +254,7 @@
UPDATE_AUDIOPORT_LIST,
UPDATE_AUDIOPATCH_LIST,
SET_AUDIOPORT_CONFIG,
+ DYN_POLICY_MIX_STATE_UPDATE
};
AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -279,6 +292,7 @@
void updateAudioPatchListCommand();
status_t setAudioPortConfigCommand(const struct audio_port_config *config,
int delayMs);
+ void dynamicPolicyMixStateUpdateCommand(String8 regId, int32_t state);
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
@@ -363,6 +377,12 @@
struct audio_port_config mConfig;
};
+ class DynPolicyMixStateUpdateData : public AudioCommandData {
+ public:
+ String8 mRegId;
+ int32_t mState;
+ };
+
Mutex mLock;
Condition mWaitWorkCV;
Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -468,6 +488,7 @@
virtual void onAudioPortListUpdate();
virtual void onAudioPatchListUpdate();
+ virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
virtual audio_unique_id_t newAudioUniqueId();
@@ -483,8 +504,9 @@
uid_t uid);
virtual ~NotificationClient();
- void onAudioPortListUpdate();
- void onAudioPatchListUpdate();
+ void onAudioPortListUpdate();
+ void onAudioPatchListUpdate();
+ void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e9c96c6..59e1c37 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -122,7 +122,7 @@
// should be ok for now.
static CameraService *gCameraService;
-CameraService::CameraService() : mEventLog(DEFAULT_EVICTION_LOG_LENGTH),
+CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) {
ALOGI("CameraService started (pid=%d)", getpid());
gCameraService = this;
@@ -140,76 +140,90 @@
BnCameraService::onFirstRef();
camera_module_t *rawModule;
- if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
- (const hw_module_t **)&rawModule) < 0) {
- ALOGE("Could not load camera HAL module");
+ int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&rawModule);
+ if (err < 0) {
+ ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
+ logServiceError("Could not load camera HAL module", err);
mNumberOfCameras = 0;
+ return;
}
- else {
- mModule = new CameraModule(rawModule);
- ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
- mNumberOfCameras = mModule->getNumberOfCameras();
- mFlashlight = new CameraFlashlight(*mModule, *this);
- status_t res = mFlashlight->findFlashUnits();
- if (res) {
- // impossible because we haven't open any camera devices.
- ALOGE("Failed to find flash units.");
- }
+ mModule = new CameraModule(rawModule);
+ ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
+ err = mModule->init();
+ if (err != OK) {
+ ALOGE("Could not initialize camera HAL module: %d (%s)", err,
+ strerror(-err));
+ logServiceError("Could not initialize camera HAL module", err);
- for (int i = 0; i < mNumberOfCameras; i++) {
- String8 cameraId = String8::format("%d", i);
-
- // Defaults to use for cost and conflicting devices
- int cost = 100;
- char** conflicting_devices = nullptr;
- size_t conflicting_devices_length = 0;
-
- // If using post-2.4 module version, query the cost + conflicting devices from the HAL
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
- struct camera_info info;
- status_t rc = mModule->getCameraInfo(i, &info);
- if (rc == NO_ERROR) {
- cost = info.resource_cost;
- conflicting_devices = info.conflicting_devices;
- conflicting_devices_length = info.conflicting_devices_length;
- } else {
- ALOGE("%s: Received error loading camera info for device %d, cost and"
- " conflicting devices fields set to defaults for this device.",
- __FUNCTION__, i);
- }
- }
-
- std::set<String8> conflicting;
- for (size_t i = 0; i < conflicting_devices_length; i++) {
- conflicting.emplace(String8(conflicting_devices[i]));
- }
-
- // Initialize state for each camera device
- {
- Mutex::Autolock lock(mCameraStatesLock);
- mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
- conflicting));
- }
-
- if (mFlashlight->hasFlashUnit(cameraId)) {
- mTorchStatusMap.add(cameraId,
- ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
- }
- }
-
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) {
- mModule->setCallbacks(this);
- }
-
- VendorTagDescriptor::clearGlobalVendorTagDescriptor();
-
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_2) {
- setUpVendorTags();
- }
-
- CameraDeviceFactory::registerService(this);
+ mNumberOfCameras = 0;
+ delete mModule;
+ mModule = nullptr;
+ return;
}
+
+ mNumberOfCameras = mModule->getNumberOfCameras();
+
+ mFlashlight = new CameraFlashlight(*mModule, *this);
+ status_t res = mFlashlight->findFlashUnits();
+ if (res) {
+ // impossible because we haven't open any camera devices.
+ ALOGE("Failed to find flash units.");
+ }
+
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ String8 cameraId = String8::format("%d", i);
+
+ // Defaults to use for cost and conflicting devices
+ int cost = 100;
+ char** conflicting_devices = nullptr;
+ size_t conflicting_devices_length = 0;
+
+ // If using post-2.4 module version, query the cost + conflicting devices from the HAL
+ if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
+ struct camera_info info;
+ status_t rc = mModule->getCameraInfo(i, &info);
+ if (rc == NO_ERROR) {
+ cost = info.resource_cost;
+ conflicting_devices = info.conflicting_devices;
+ conflicting_devices_length = info.conflicting_devices_length;
+ } else {
+ ALOGE("%s: Received error loading camera info for device %d, cost and"
+ " conflicting devices fields set to defaults for this device.",
+ __FUNCTION__, i);
+ }
+ }
+
+ std::set<String8> conflicting;
+ for (size_t i = 0; i < conflicting_devices_length; i++) {
+ conflicting.emplace(String8(conflicting_devices[i]));
+ }
+
+ // Initialize state for each camera device
+ {
+ Mutex::Autolock lock(mCameraStatesLock);
+ mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
+ conflicting));
+ }
+
+ if (mFlashlight->hasFlashUnit(cameraId)) {
+ mTorchStatusMap.add(cameraId,
+ ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+ }
+ }
+
+ if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) {
+ mModule->setCallbacks(this);
+ }
+
+ VendorTagDescriptor::clearGlobalVendorTagDescriptor();
+
+ if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_2) {
+ setUpVendorTags();
+ }
+
+ CameraDeviceFactory::registerService(this);
}
CameraService::~CameraService() {
@@ -242,6 +256,8 @@
}
if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+ logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
+ newStatus));
sp<BasicClient> clientToDisconnect;
{
// Don't do this in updateStatus to avoid deadlock over mServiceLock
@@ -274,6 +290,10 @@
}
} else {
+ if (oldStatus == ICameraServiceListener::Status::STATUS_NOT_PRESENT) {
+ logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus,
+ newStatus));
+ }
updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id);
}
@@ -293,12 +313,11 @@
ICameraServiceListener::TorchStatus status;
status_t res = getTorchStatusLocked(cameraId, &status);
if (res) {
- ALOGE("%s: cannot get torch status of camera %s", cameraId.string());
+ ALOGE("%s: cannot get torch status of camera %s: %s (%d)",
+ __FUNCTION__, cameraId.string(), strerror(-res), res);
return;
}
if (status == newStatus) {
- ALOGE("%s: Torch state transition to the same status 0x%x not allowed",
- __FUNCTION__, (uint32_t)newStatus);
return;
}
@@ -500,34 +519,12 @@
int CameraService::getCameraPriorityFromProcState(int procState) {
// Find the priority for the camera usage based on the process state. Higher priority clients
// win for evictions.
- // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose
- // the camera to the top/foreground applications.
- switch(procState) {
- case PROCESS_STATE_TOP: // User visible
- return 100;
- case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground
- return 90;
- case PROCESS_STATE_PERSISTENT: // Persistent system services
- case PROCESS_STATE_PERSISTENT_UI:
- return 80;
- case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes
- return 70;
- case PROCESS_STATE_BACKUP: // Everything else
- case PROCESS_STATE_HEAVY_WEIGHT:
- case PROCESS_STATE_SERVICE:
- case PROCESS_STATE_RECEIVER:
- case PROCESS_STATE_HOME:
- case PROCESS_STATE_LAST_ACTIVITY:
- case PROCESS_STATE_CACHED_ACTIVITY:
- case PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
- case PROCESS_STATE_CACHED_EMPTY:
- return 1;
- case PROCESS_STATE_NONEXISTENT:
- return -1;
- default:
- ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__);
- return -1;
+ if (procState < 0) {
+ ALOGE("%s: Received invalid process state %d from ActivityManagerService!", __FUNCTION__,
+ procState);
+ return -1;
}
+ return INT_MAX - procState;
}
status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) {
@@ -765,8 +762,8 @@
} else {
// We only trust our own process to forward client UIDs
if (callingPid != getpid()) {
- ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)",
- callingPid);
+ ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid %d)",
+ callingPid, clientUid);
return PERMISSION_DENIED;
}
}
@@ -796,10 +793,12 @@
return -EACCES;
}
- // Only allow clients who are being used by the current foreground device user.
- if (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID) {
- ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from non-foreground "
- "device user)", callingPid);
+ // Only allow clients who are being used by the current foreground device user, unless calling
+ // from our own process.
+ if (callingPid != getpid() &&
+ (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID)) {
+ ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from previous "
+ "device user %d, current device user %d)", callingPid, clientUserId, mLastUserId);
return PERMISSION_DENIED;
}
@@ -858,7 +857,7 @@
std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
status_t ret = NO_ERROR;
- std::vector<sp<BasicClient>> evictedClients;
+ std::vector<DescriptorPtr> evictedClients;
DescriptorPtr clientDescriptor;
{
if (effectiveApiLevel == API_1) {
@@ -869,9 +868,12 @@
if (current != nullptr) {
auto clientSp = current->getValue();
if (clientSp.get() != nullptr) { // should never be needed
- if (clientSp->getRemote() == remoteCallback) {
+ if (!clientSp->canCastToApiClient(effectiveApiLevel)) {
+ ALOGW("CameraService connect called from same client, but with a different"
+ " API level, evicting prior client...");
+ } else if (clientSp->getRemote() == remoteCallback) {
ALOGI("CameraService::connect X (PID %d) (second call from same"
- "app binder, returning the same client)", clientPid);
+ " app binder, returning the same client)", clientPid);
*client = clientSp;
return NO_ERROR;
}
@@ -934,7 +936,7 @@
mActiveClientManager.getIncompatibleClients(clientDescriptor);
String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
- "(PID %d, priority %d)", curTime.string(),
+ "(PID %d, priority %d) due to eviction policy", curTime.string(),
cameraId.string(), packageName.string(), clientPid,
getCameraPriorityFromProcState(priorities[priorities.size() - 1]));
@@ -946,6 +948,7 @@
}
// Log the client's attempt
+ Mutex::Autolock l(mLogLock);
mEventLog.add(msg);
return -EBUSY;
@@ -965,14 +968,12 @@
ALOGE("CameraService::connect evicting conflicting client for camera ID %s",
i->getKey().string());
- evictedClients.push_back(clientSp);
-
- String8 curTime = getFormattedCurrentTime();
+ evictedClients.push_back(i);
// Log the clients evicted
- mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
- PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for "
- "package %s (PID %d, priority %" PRId32 ")", curTime.string(),
+ logEvent(String8::format("EVICT device %s client held by package %s (PID"
+ " %" PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for"
+ " package %s (PID %d, priority %" PRId32 ")",
i->getKey().string(), String8{clientSp->getPackageName()}.string(),
i->getOwnerId(), i->getPriority(), cameraId.string(),
packageName.string(), clientPid,
@@ -994,12 +995,31 @@
// Destroy evicted clients
for (auto& i : evictedClients) {
// Disconnect is blocking, and should only have returned when HAL has cleaned up
- i->disconnect(); // Clients will remove themselves from the active client list here
+ i->getValue()->disconnect(); // Clients will remove themselves from the active client list
}
- evictedClients.clear();
IPCThreadState::self()->restoreCallingIdentity(token);
+ for (const auto& i : evictedClients) {
+ ALOGV("%s: Waiting for disconnect to complete for client for device %s (PID %" PRId32 ")",
+ __FUNCTION__, i->getKey().string(), i->getOwnerId());
+ ret = mActiveClientManager.waitUntilRemoved(i, DEFAULT_DISCONNECT_TIMEOUT_NS);
+ if (ret == TIMED_OUT) {
+ ALOGE("%s: Timed out waiting for client for device %s to disconnect, "
+ "current clients:\n%s", __FUNCTION__, i->getKey().string(),
+ mActiveClientManager.toString().string());
+ return -EBUSY;
+ }
+ if (ret != NO_ERROR) {
+ ALOGE("%s: Received error waiting for client for device %s to disconnect: %s (%d), "
+ "current clients:\n%s", __FUNCTION__, i->getKey().string(), strerror(-ret),
+ ret, mActiveClientManager.toString().string());
+ return ret;
+ }
+ }
+
+ evictedClients.clear();
+
// Once clients have been disconnected, relock
mServiceLock.lock();
@@ -1027,6 +1047,8 @@
clientPackageName, clientUid, API_1, false, false, /*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1042,6 +1064,7 @@
/*out*/
sp<ICamera>& device) {
+ String8 id = String8::format("%d", cameraId);
int apiVersion = mModule->getModuleApiVersion();
if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
apiVersion < CAMERA_MODULE_API_VERSION_2_3) {
@@ -1053,16 +1076,19 @@
*/
ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!",
__FUNCTION__, apiVersion);
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8("HAL module version doesn't support legacy HAL connections"));
return INVALID_OPERATION;
}
status_t ret = NO_ERROR;
- String8 id = String8::format("%d", cameraId);
sp<Client> client = nullptr;
ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName,
clientUid, API_1, true, false, /*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1086,6 +1112,8 @@
/*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1105,14 +1133,14 @@
// verify id is valid.
auto state = getCameraState(id);
if (state == nullptr) {
- ALOGE("%s: camera id is invalid %s", id.string());
+ ALOGE("%s: camera id is invalid %s", __FUNCTION__, id.string());
return -EINVAL;
}
ICameraServiceListener::Status cameraStatus = state->getStatus();
if (cameraStatus != ICameraServiceListener::STATUS_PRESENT &&
cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
- ALOGE("%s: camera id is invalid %s", id.string());
+ ALOGE("%s: camera id is invalid %s", __FUNCTION__, id.string());
return -EINVAL;
}
@@ -1426,6 +1454,8 @@
newUserId = DEFAULT_LAST_USER_ID;
}
+ logUserSwitch(mLastUserId, newUserId);
+
mLastUserId = newUserId;
// Current user has switched, evict all current clients.
@@ -1444,12 +1474,12 @@
ALOGE("Evicting conflicting client for camera ID %s due to user change",
i->getKey().string());
+
// Log the clients evicted
- mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
+ logEvent(String8::format("EVICT device %s client held by package %s (PID %"
PRId32 ", priority %" PRId32 ")\n - Evicted due to user switch.",
- curTime.string(), i->getKey().string(),
- String8{clientSp->getPackageName()}.string(), i->getOwnerId(),
- i->getPriority()));
+ i->getKey().string(), String8{clientSp->getPackageName()}.string(),
+ i->getOwnerId(), i->getPriority()));
}
@@ -1470,22 +1500,57 @@
mServiceLock.lock();
}
-void CameraService::logDisconnected(const String8& cameraId, int clientPid,
- const String8& clientPackage) {
-
+void CameraService::logEvent(const char* event) {
String8 curTime = getFormattedCurrentTime();
- // Log the clients evicted
- mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)",
- curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+ Mutex::Autolock l(mLogLock);
+ mEventLog.add(String8::format("%s : %s", curTime.string(), event));
}
-void CameraService::logConnected(const String8& cameraId, int clientPid,
- const String8& clientPackage) {
-
- String8 curTime = getFormattedCurrentTime();
+void CameraService::logDisconnected(const char* cameraId, int clientPid,
+ const char* clientPackage) {
// Log the clients evicted
- mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)",
- curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+ logEvent(String8::format("DISCONNECT device %s client for package %s (PID %d)", cameraId,
+ clientPackage, clientPid));
+}
+
+void CameraService::logConnected(const char* cameraId, int clientPid,
+ const char* clientPackage) {
+ // Log the clients evicted
+ logEvent(String8::format("CONNECT device %s client for package %s (PID %d)", cameraId,
+ clientPackage, clientPid));
+}
+
+void CameraService::logRejected(const char* cameraId, int clientPid,
+ const char* clientPackage, const char* reason) {
+ // Log the client rejected
+ logEvent(String8::format("REJECT device %s client for package %s (PID %d), reason: (%s)",
+ cameraId, clientPackage, clientPid, reason));
+}
+
+void CameraService::logUserSwitch(int oldUserId, int newUserId) {
+ // Log the new and old users
+ logEvent(String8::format("USER_SWITCH from old user: %d , to new user: %d", oldUserId,
+ newUserId));
+}
+
+void CameraService::logDeviceRemoved(const char* cameraId, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("REMOVE device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logDeviceAdded(const char* cameraId, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("ADD device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logClientDied(int clientPid, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("DIED client(s) with PID %d, reason: (%s)", clientPid, reason));
+}
+
+void CameraService::logServiceError(const char* msg, int errorCode) {
+ String8 curTime = getFormattedCurrentTime();
+ logEvent(String8::format("SERVICE ERROR: %s : %d (%s)", msg, errorCode, strerror(errorCode)));
}
status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
@@ -1670,6 +1735,11 @@
return mClientPid;
}
+bool CameraService::BasicClient::canCastToApiClient(apiLevel level) const {
+ // Defaults to API2.
+ return level == API_2;
+}
+
status_t CameraService::BasicClient::startCameraOps() {
int32_t res;
// Notify app ops that the camera is not available
@@ -1685,12 +1755,18 @@
res = mAppOpsManager.startOp(AppOpsManager::OP_CAMERA,
mClientUid, mClientPackageName);
- if (res != AppOpsManager::MODE_ALLOWED) {
+ if (res == AppOpsManager::MODE_ERRORED) {
ALOGI("Camera %d: Access for \"%s\" has been revoked",
mCameraId, String8(mClientPackageName).string());
return PERMISSION_DENIED;
}
+ if (res == AppOpsManager::MODE_IGNORED) {
+ ALOGI("Camera %d: Access for \"%s\" has been restricted",
+ mCameraId, String8(mClientPackageName).string());
+ return INVALID_OPERATION;
+ }
+
mOpsActive = true;
// Transition device availability listeners from PRESENT -> NOT_AVAILABLE
@@ -1782,6 +1858,10 @@
BasicClient::disconnect();
}
+bool CameraService::Client::canCastToApiClient(apiLevel level) const {
+ return level == API_1;
+}
+
CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
mClient(client) {
}
@@ -1911,7 +1991,7 @@
}
status_t CameraService::dump(int fd, const Vector<String16>& args) {
- String8 result;
+ String8 result("Dump of the Camera Service:\n");
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
result.appendFormat("Permission Denial: "
"can't dump CameraService from pid=%d, uid=%d\n",
@@ -1930,6 +2010,10 @@
if (!mModule) {
result = String8::format("No camera module available!\n");
write(fd, result.string(), result.size());
+
+ // Dump event log for error information
+ dumpEventLog(fd);
+
if (locked) mServiceLock.unlock();
return NO_ERROR;
}
@@ -1955,17 +2039,7 @@
desc->dump(fd, /*verbosity*/2, /*indentation*/4);
}
- result = String8("Prior client events (most recent at top):\n");
-
- for (const auto& msg : mEventLog) {
- result.appendFormat("%s\n", msg.string());
- }
-
- if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) {
- result.append("...\n");
- }
-
- write(fd, result.string(), result.size());
+ dumpEventLog(fd);
bool stateLocked = tryLock(mCameraStatesLock);
if (!stateLocked) {
@@ -2073,6 +2147,24 @@
return NO_ERROR;
}
+void CameraService::dumpEventLog(int fd) {
+ String8 result = String8("\nPrior client events (most recent at top):\n");
+
+ Mutex::Autolock l(mLogLock);
+ for (const auto& msg : mEventLog) {
+ result.appendFormat(" %s\n", msg.string());
+ }
+
+ if (mEventLog.size() == DEFAULT_EVENT_LOG_LENGTH) {
+ result.append(" ...\n");
+ } else if (mEventLog.size() == 0) {
+ result.append(" [no events yet]\n");
+ }
+ result.append("\n");
+
+ write(fd, result.string(), result.size());
+}
+
void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) {
Mutex::Autolock al(mTorchClientMapMutex);
for (size_t i = 0; i < mTorchClientMap.size(); i++) {
@@ -2094,10 +2186,12 @@
/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {
/**
- * While tempting to promote the wp<IBinder> into a sp,
- * it's actually not supported by the binder driver
+ * While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
+ * binder driver
*/
+ logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
+
// check torch client
handleTorchClientBinderDied(who);
@@ -2133,16 +2227,20 @@
state->updateStatus(status, cameraId, rejectSourceStates, [this]
(const String8& cameraId, ICameraServiceListener::Status status) {
- // Update torch status
- if (status == ICameraServiceListener::STATUS_NOT_PRESENT ||
- status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
- // Update torch status to not available when the camera device becomes not present
- // or not available.
- onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE);
- } else if (status == ICameraServiceListener::STATUS_PRESENT) {
- // Update torch status to available when the camera device becomes present or
- // available
- onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+ if (status != ICameraServiceListener::STATUS_ENUMERATING) {
+ // Update torch status if it has a flash unit.
+ Mutex::Autolock al(mTorchStatusMutex);
+ ICameraServiceListener::TorchStatus torchStatus;
+ if (getTorchStatusLocked(cameraId, &torchStatus) !=
+ NAME_NOT_FOUND) {
+ ICameraServiceListener::TorchStatus newTorchStatus =
+ status == ICameraServiceListener::STATUS_PRESENT ?
+ ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF :
+ ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+ if (torchStatus != newTorchStatus) {
+ onTorchStatusChangedLocked(cameraId, newTorchStatus);
+ }
+ }
}
Mutex::Autolock lock(mStatusListenerLock);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index ca1c504..1041550 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -38,9 +38,9 @@
#include "CameraFlashlight.h"
#include "common/CameraModule.h"
+#include "media/RingBuffer.h"
#include "utils/AutoConditionLock.h"
#include "utils/ClientManager.h"
-#include "utils/RingBuffer.h"
#include <set>
#include <string>
@@ -65,33 +65,23 @@
class Client;
class BasicClient;
+ // The effective API level. The Camera2 API running in LEGACY mode counts as API_1.
enum apiLevel {
API_1 = 1,
API_2 = 2
};
- // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java)
+ // Process state (mirrors frameworks/base/core/java/android/app/ActivityManager.java)
static const int PROCESS_STATE_NONEXISTENT = -1;
- static const int PROCESS_STATE_PERSISTENT = 0;
- static const int PROCESS_STATE_PERSISTENT_UI = 1;
- static const int PROCESS_STATE_TOP = 2;
- static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
- static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
- static const int PROCESS_STATE_BACKUP = 5;
- static const int PROCESS_STATE_HEAVY_WEIGHT = 6;
- static const int PROCESS_STATE_SERVICE = 7;
- static const int PROCESS_STATE_RECEIVER = 8;
- static const int PROCESS_STATE_HOME = 9;
- static const int PROCESS_STATE_LAST_ACTIVITY = 10;
- static const int PROCESS_STATE_CACHED_ACTIVITY = 11;
- static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12;
- static const int PROCESS_STATE_CACHED_EMPTY = 13;
// 3 second busy timeout when other clients are connecting
static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000;
+ // 1 second busy timeout when other clients are disconnecting
+ static const nsecs_t DEFAULT_DISCONNECT_TIMEOUT_NS = 1000000000;
+
// Default number of messages to store in eviction log
- static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50;
+ static const size_t DEFAULT_EVENT_LOG_LENGTH = 100;
enum {
// Default last user id
@@ -212,6 +202,10 @@
// Get the PID of the application client using this
virtual int getClientPid() const;
+
+ // Check what API level is used for this client. This is used to determine which
+ // superclass this can be cast to.
+ virtual bool canCastToApiClient(apiLevel level) const;
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
@@ -320,6 +314,10 @@
virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
const CaptureResultExtras& resultExtras);
+
+ // Check what API level is used for this client. This is used to determine which
+ // superclass this can be cast to.
+ virtual bool canCastToApiClient(apiLevel level) const;
protected:
// Convert client from cookie.
static sp<CameraService::Client> getClientFromCookie(void* user);
@@ -492,6 +490,7 @@
// Circular buffer for storing event logging for dumps
RingBuffer<String8> mEventLog;
+ Mutex mLogLock;
// UID of last user.
int mLastUserId;
@@ -546,14 +545,55 @@
void doUserSwitch(int newUserId);
/**
- * Add a event log message that a client has been disconnected.
+ * Add an event log message.
*/
- void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+ void logEvent(const char* event);
/**
- * Add a event log message that a client has been connected.
+ * Add an event log message that a client has been disconnected.
*/
- void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+ void logDisconnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+ /**
+ * Add an event log message that a client has been connected.
+ */
+ void logConnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+ /**
+ * Add an event log message that a client's connect attempt has been rejected.
+ */
+ void logRejected(const char* cameraId, int clientPid, const char* clientPackage,
+ const char* reason);
+
+ /**
+ * Add an event log message that the current device user has been switched.
+ */
+ void logUserSwitch(int oldUserId, int newUserId);
+
+ /**
+ * Add an event log message that a device has been removed by the HAL
+ */
+ void logDeviceRemoved(const char* cameraId, const char* reason);
+
+ /**
+ * Add an event log message that a device has been added by the HAL
+ */
+ void logDeviceAdded(const char* cameraId, const char* reason);
+
+ /**
+ * Add an event log message that a client has unexpectedly died.
+ */
+ void logClientDied(int clientPid, const char* reason);
+
+ /**
+ * Add a event log message that a serious service-level error has occured
+ */
+ void logServiceError(const char* msg, int errorCode);
+
+ /**
+ * Dump the event log to an FD
+ */
+ void dumpEventLog(int fd);
int mNumberOfCameras;
@@ -714,9 +754,10 @@
String8 clientName8(clientPackageName);
int clientPid = getCallingPid();
- ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and "
+ ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
"Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
- halVersion, static_cast<int>(effectiveApiLevel));
+ (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
+ static_cast<int>(effectiveApiLevel));
sp<CLIENT> client = nullptr;
{
@@ -734,7 +775,15 @@
if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) {
return ret;
}
- mLastUserId = multiuser_get_user_id(clientUid);
+ int userId = multiuser_get_user_id(clientUid);
+
+ if (userId != mLastUserId && clientPid != getpid() ) {
+ // If no previous user ID had been set, set to the user of the caller.
+ logUserSwitch(mLastUserId, userId);
+ LOG_ALWAYS_FATAL_IF(mLastUserId != DEFAULT_LAST_USER_ID,
+ "Invalid state: Should never update user ID here unless was default");
+ mLastUserId = userId;
+ }
// Check the shim parameters after acquiring lock, if they have already been updated and
// we were doing a shim update, return immediately
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 6f44aee..05ede92 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -121,7 +121,8 @@
}
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2: {
+ case CAMERA_DEVICE_API_VERSION_3_2:
+ case CAMERA_DEVICE_API_VERSION_3_3: {
sp<ZslProcessor3> zslProc =
new ZslProcessor3(this, mCaptureSequencer);
mZslProcessor = zslProc;
@@ -1709,6 +1710,40 @@
return mStreamingProcessor->setRecordingBufferCount(count);
}
+void Camera2Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
+ const CaptureResultExtras& resultExtras) {
+ int32_t err = CAMERA_ERROR_UNKNOWN;
+ switch(errorCode) {
+ case ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED:
+ err = CAMERA_ERROR_RELEASED;
+ break;
+ case ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE:
+ err = CAMERA_ERROR_UNKNOWN;
+ break;
+ case ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE:
+ err = CAMERA_ERROR_SERVER_DIED;
+ break;
+ case ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
+ case ICameraDeviceCallbacks::ERROR_CAMERA_RESULT:
+ case ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
+ ALOGW("%s: Received recoverable error %d from HAL - ignoring, requestId %" PRId32,
+ __FUNCTION__, errorCode, resultExtras.requestId);
+ return;
+ default:
+ err = CAMERA_ERROR_UNKNOWN;
+ break;
+ }
+
+ ALOGE("%s: Error condition %d reported by HAL, requestId %" PRId32, __FUNCTION__, errorCode,
+ resultExtras.requestId);
+
+ SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+ if (l.mRemoteCallback != nullptr) {
+ l.mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, err, 0);
+ }
+}
+
+
/** Device-related methods */
void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
ALOGV("%s: Autofocus state now %d, last trigger %d",
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 5a8241f..a988037 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -77,6 +77,8 @@
virtual status_t setParameters(const String8& params);
virtual String8 getParameters() const;
virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
+ virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
+ const CaptureResultExtras& resultExtras);
/**
* Interface used by CameraService
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 5c8f750..88c5811 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -395,7 +395,7 @@
heapIdx = mCallbackHeapHead;
- mCallbackHeapHead = (mCallbackHeapHead + 1) & kCallbackHeapCount;
+ mCallbackHeapHead = (mCallbackHeapHead + 1) % kCallbackHeapCount;
mCallbackHeapFree--;
// TODO: Get rid of this copy by passing the gralloc queue all the way
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 6b0f8b5..c3a6842 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2100,12 +2100,7 @@
delete[] reqMeteringAreas;
- /* don't include jpeg thumbnail size - it's valid for
- it to be set to (0,0), meaning 'no thumbnail' */
- CropRegion crop = calculateCropRegion( (CropRegion::Outputs)(
- CropRegion::OUTPUT_PREVIEW |
- CropRegion::OUTPUT_VIDEO |
- CropRegion::OUTPUT_PICTURE ));
+ CropRegion crop = calculateCropRegion(/*previewOnly*/ false);
int32_t reqCropRegion[4] = {
static_cast<int32_t>(crop.left),
static_cast<int32_t>(crop.top),
@@ -2603,7 +2598,7 @@
ALOG_ASSERT(x >= 0, "Crop-relative X coordinate = '%d' is out of bounds"
"(lower = 0)", x);
- CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
ALOG_ASSERT(x < previewCrop.width, "Crop-relative X coordinate = '%d' "
"is out of bounds (upper = %f)", x, previewCrop.width);
@@ -2619,7 +2614,7 @@
ALOG_ASSERT(y >= 0, "Crop-relative Y coordinate = '%d' is out of bounds "
"(lower = 0)", y);
- CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
ALOG_ASSERT(y < previewCrop.height, "Crop-relative Y coordinate = '%d' is "
"out of bounds (upper = %f)", y, previewCrop.height);
@@ -2634,12 +2629,12 @@
}
int Parameters::normalizedXToCrop(int x) const {
- CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
return (x + 1000) * (previewCrop.width - 1) / 2000;
}
int Parameters::normalizedYToCrop(int y) const {
- CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
return (y + 1000) * (previewCrop.height - 1) / 2000;
}
@@ -2855,8 +2850,7 @@
return jpegSizes;
}
-Parameters::CropRegion Parameters::calculateCropRegion(
- Parameters::CropRegion::Outputs outputs) const {
+Parameters::CropRegion Parameters::calculateCropRegion(bool previewOnly) const {
float zoomLeft, zoomTop, zoomWidth, zoomHeight;
@@ -2880,90 +2874,45 @@
maxDigitalZoom.data.f[0], zoomIncrement, zoomRatio, previewWidth,
previewHeight, fastInfo.arrayWidth, fastInfo.arrayHeight);
- /*
- * Assumption: On the HAL side each stream buffer calculates its crop
- * rectangle as follows:
- * cropRect = (zoomLeft, zoomRight,
- * zoomWidth, zoomHeight * zoomWidth / outputWidth);
- *
- * Note that if zoomWidth > bufferWidth, the new cropHeight > zoomHeight
- * (we can then get into trouble if the cropHeight > arrayHeight).
- * By selecting the zoomRatio based on the smallest outputRatio, we
- * guarantee this will never happen.
- */
+ if (previewOnly) {
+ // Calculate a tight crop region for the preview stream only
+ float previewRatio = static_cast<float>(previewWidth) / previewHeight;
- // Enumerate all possible output sizes, select the one with the smallest
- // aspect ratio
- float minOutputWidth, minOutputHeight, minOutputRatio;
- {
- float outputSizes[][2] = {
- { static_cast<float>(previewWidth),
- static_cast<float>(previewHeight) },
- { static_cast<float>(videoWidth),
- static_cast<float>(videoHeight) },
- { static_cast<float>(jpegThumbSize[0]),
- static_cast<float>(jpegThumbSize[1]) },
- { static_cast<float>(pictureWidth),
- static_cast<float>(pictureHeight) },
- };
+ /* Ensure that the width/height never go out of bounds
+ * by scaling across a diffent dimension if an out-of-bounds
+ * possibility exists.
+ *
+ * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by
+ * calculating the zoomWidth from zoomHeight we'll actually get a
+ * zoomheight > arrayheight
+ */
+ float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight;
+ if (previewRatio >= arrayRatio) {
+ // Adjust the height based on the width
+ zoomWidth = fastInfo.arrayWidth / zoomRatio;
+ zoomHeight = zoomWidth *
+ previewHeight / previewWidth;
- minOutputWidth = outputSizes[0][0];
- minOutputHeight = outputSizes[0][1];
- minOutputRatio = minOutputWidth / minOutputHeight;
- for (unsigned int i = 0;
- i < sizeof(outputSizes) / sizeof(outputSizes[0]);
- ++i) {
-
- // skip over outputs we don't want to consider for the crop region
- if ( !((1 << i) & outputs) ) {
- continue;
- }
-
- float outputWidth = outputSizes[i][0];
- float outputHeight = outputSizes[i][1];
- float outputRatio = outputWidth / outputHeight;
-
- if (minOutputRatio > outputRatio) {
- minOutputRatio = outputRatio;
- minOutputWidth = outputWidth;
- minOutputHeight = outputHeight;
- }
-
- // and then use this output ratio instead of preview output ratio
- ALOGV("Enumerating output ratio %f = %f / %f, min is %f",
- outputRatio, outputWidth, outputHeight, minOutputRatio);
+ } else {
+ // Adjust the width based on the height
+ zoomHeight = fastInfo.arrayHeight / zoomRatio;
+ zoomWidth = zoomHeight *
+ previewWidth / previewHeight;
}
- }
-
- /* Ensure that the width/height never go out of bounds
- * by scaling across a diffent dimension if an out-of-bounds
- * possibility exists.
- *
- * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by
- * calculating the zoomWidth from zoomHeight we'll actually get a
- * zoomheight > arrayheight
- */
- float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight;
- if (minOutputRatio >= arrayRatio) {
- // Adjust the height based on the width
- zoomWidth = fastInfo.arrayWidth / zoomRatio;
- zoomHeight = zoomWidth *
- minOutputHeight / minOutputWidth;
-
} else {
- // Adjust the width based on the height
+ // Calculate the global crop region with a shape matching the active
+ // array.
+ zoomWidth = fastInfo.arrayWidth / zoomRatio;
zoomHeight = fastInfo.arrayHeight / zoomRatio;
- zoomWidth = zoomHeight *
- minOutputWidth / minOutputHeight;
}
- // centering the zoom area within the active area
+
+ // center the zoom area within the active area
zoomLeft = (fastInfo.arrayWidth - zoomWidth) / 2;
zoomTop = (fastInfo.arrayHeight - zoomHeight) / 2;
ALOGV("Crop region calculated (x=%d,y=%d,w=%f,h=%f) for zoom=%d",
(int32_t)zoomLeft, (int32_t)zoomTop, zoomWidth, zoomHeight, this->zoom);
-
CropRegion crop = { zoomLeft, zoomTop, zoomWidth, zoomHeight };
return crop;
}
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index e628a7e..46d48bc 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -271,21 +271,16 @@
// if video snapshot size is currently overridden
bool isJpegSizeOverridden();
- // Calculate the crop region rectangle based on current stream sizes
+ // Calculate the crop region rectangle, either tightly about the preview
+ // resolution, or a region just based on the active array; both take
+ // into account the current zoom level.
struct CropRegion {
float left;
float top;
float width;
float height;
-
- enum Outputs {
- OUTPUT_PREVIEW = 0x01,
- OUTPUT_VIDEO = 0x02,
- OUTPUT_JPEG_THUMBNAIL = 0x04,
- OUTPUT_PICTURE = 0x08,
- };
};
- CropRegion calculateCropRegion(CropRegion::Outputs outputs) const;
+ CropRegion calculateCropRegion(bool previewOnly) const;
// Calculate the field of view of the high-resolution JPEG capture
status_t calculatePictureFovs(float *horizFov, float *vertFov) const;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 8587e0e..b6f6677 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -65,6 +65,7 @@
int servicePid) :
Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
cameraId, cameraFacing, clientPid, clientUid, servicePid),
+ mInputStream(),
mRequestIdCounter(0) {
ATRACE_CALL();
@@ -134,6 +135,15 @@
ALOGE("%s: Camera %d: Sent null request.",
__FUNCTION__, mCameraId);
return BAD_VALUE;
+ } else if (request->mIsReprocess) {
+ if (!mInputStream.configured) {
+ ALOGE("%s: Camera %d: no input stream is configured.", __FUNCTION__, mCameraId);
+ return BAD_VALUE;
+ } else if (streaming) {
+ ALOGE("%s: Camera %d: streaming reprocess requests not supported.", __FUNCTION__,
+ mCameraId);
+ return BAD_VALUE;
+ }
}
CameraMetadata metadata(request->mMetadata);
@@ -182,6 +192,10 @@
metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0],
outputStreamIds.size());
+ if (request->mIsReprocess) {
+ metadata.update(ANDROID_REQUEST_INPUT_STREAMS, &mInputStream.id, 1);
+ }
+
metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1);
loopCounter++; // loopCounter starts from 1
ALOGV("%s: Camera %d: Creating request with ID %d (%d of %zu)",
@@ -260,8 +274,8 @@
}
status_t CameraDeviceClient::endConfigure() {
- ALOGV("%s: ending configure (%zu streams)",
- __FUNCTION__, mStreamMap.size());
+ ALOGV("%s: ending configure (%d input stream, %zu output streams)",
+ __FUNCTION__, mInputStream.configured ? 1 : 0, mStreamMap.size());
status_t res;
if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
@@ -284,19 +298,25 @@
if (!mDevice.get()) return DEAD_OBJECT;
- // Guard against trying to delete non-created streams
+ bool isInput = false;
ssize_t index = NAME_NOT_FOUND;
- for (size_t i = 0; i < mStreamMap.size(); ++i) {
- if (streamId == mStreamMap.valueAt(i)) {
- index = i;
- break;
- }
- }
- if (index == NAME_NOT_FOUND) {
- ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
- "created yet", __FUNCTION__, mCameraId, streamId);
- return BAD_VALUE;
+ if (mInputStream.configured && mInputStream.id == streamId) {
+ isInput = true;
+ } else {
+ // Guard against trying to delete non-created streams
+ for (size_t i = 0; i < mStreamMap.size(); ++i) {
+ if (streamId == mStreamMap.valueAt(i)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == NAME_NOT_FOUND) {
+ ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
+ "created yet", __FUNCTION__, mCameraId, streamId);
+ return BAD_VALUE;
+ }
}
// Also returns BAD_VALUE if stream ID was not valid
@@ -307,8 +327,11 @@
" already checked and the stream ID (%d) should be valid.",
__FUNCTION__, mCameraId, streamId);
} else if (res == OK) {
- mStreamMap.removeItemsAt(index);
-
+ if (isInput) {
+ mInputStream.configured = false;
+ } else {
+ mStreamMap.removeItemsAt(index);
+ }
}
return res;
@@ -450,6 +473,58 @@
}
+status_t CameraDeviceClient::createInputStream(int width, int height,
+ int format) {
+
+ ATRACE_CALL();
+ ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ if (mInputStream.configured) {
+ ALOGE("%s: Camera %d: Already has an input stream "
+ " configuration. (ID %zd)", __FUNCTION__, mCameraId,
+ mInputStream.id);
+ return ALREADY_EXISTS;
+ }
+
+ int streamId = -1;
+ res = mDevice->createInputStream(width, height, format, &streamId);
+ if (res == OK) {
+ mInputStream.configured = true;
+ mInputStream.width = width;
+ mInputStream.height = height;
+ mInputStream.format = format;
+ mInputStream.id = streamId;
+
+ ALOGV("%s: Camera %d: Successfully created a new input stream ID %d",
+ __FUNCTION__, mCameraId, streamId);
+
+ return streamId;
+ }
+
+ return res;
+}
+
+status_t CameraDeviceClient::getInputBufferProducer(
+ /*out*/sp<IGraphicBufferProducer> *producer) {
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ if (producer == NULL) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ return mDevice->getInputBufferProducer(producer);
+}
+
bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height,
int32_t format, android_dataspace dataSpace, const CameraMetadata& info,
/*out*/int32_t* outWidth, /*out*/int32_t* outHeight) {
@@ -592,6 +667,37 @@
return mDevice->flush(lastFrameNumber);
}
+status_t CameraDeviceClient::prepare(int streamId) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ status_t res = OK;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ // Guard against trying to prepare non-created streams
+ ssize_t index = NAME_NOT_FOUND;
+ for (size_t i = 0; i < mStreamMap.size(); ++i) {
+ if (streamId == mStreamMap.valueAt(i)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == NAME_NOT_FOUND) {
+ ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
+ "created yet", __FUNCTION__, mCameraId, streamId);
+ return BAD_VALUE;
+ }
+
+ // Also returns BAD_VALUE if stream ID was not valid, or stream already
+ // has been used
+ res = mDevice->prepare(streamId);
+
+ return res;
+}
+
status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
String8 result;
result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n",
@@ -602,13 +708,19 @@
result.append(" State:\n");
result.appendFormat(" Request ID counter: %d\n", mRequestIdCounter);
+ if (mInputStream.configured) {
+ result.appendFormat(" Current input stream ID: %d\n",
+ mInputStream.id);
+ } else {
+ result.append(" No input stream configured.\n");
+ }
if (!mStreamMap.isEmpty()) {
- result.append(" Current stream IDs:\n");
+ result.append(" Current output stream IDs:\n");
for (size_t i = 0; i < mStreamMap.size(); i++) {
result.appendFormat(" Stream %d\n", mStreamMap.valueAt(i));
}
} else {
- result.append(" No streams configured.\n");
+ result.append(" No output streams configured.\n");
}
write(fd, result.string(), result.size());
// TODO: print dynamic/request section from most recent requests
@@ -645,6 +757,14 @@
}
}
+void CameraDeviceClient::notifyPrepared(int streamId) {
+ // Thread safe. Don't bother locking.
+ sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+ if (remoteCb != 0) {
+ remoteCb->onPrepared(streamId);
+ }
+}
+
void CameraDeviceClient::detachDevice() {
if (mDevice == 0) return;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index a3dbb90..b8d8bea 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -86,6 +86,13 @@
virtual status_t createStream(const OutputConfiguration &outputConfiguration);
+ // Create an input stream of width, height, and format.
+ virtual status_t createInputStream(int width, int height, int format);
+
+ // Get the buffer producer of the input stream
+ virtual status_t getInputBufferProducer(
+ /*out*/sp<IGraphicBufferProducer> *producer);
+
// Create a request object from a template.
virtual status_t createDefaultRequest(int templateId,
/*out*/
@@ -102,6 +109,9 @@
virtual status_t flush(/*out*/
int64_t* lastFrameNumber = NULL);
+ // Prepare stream by preallocating its buffers
+ virtual status_t prepare(int streamId);
+
/**
* Interface used by CameraService
*/
@@ -128,6 +138,7 @@
virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
const CaptureResultExtras& resultExtras);
virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp);
+ virtual void notifyPrepared(int streamId);
/**
* Interface used by independent components of CameraDeviceClient.
@@ -161,10 +172,18 @@
android_dataspace dataSpace, const CameraMetadata& info,
/*out*/int32_t* outWidth, /*out*/int32_t* outHeight);
- // IGraphicsBufferProducer binder -> Stream ID
+ // IGraphicsBufferProducer binder -> Stream ID for output streams
KeyedVector<sp<IBinder>, int> mStreamMap;
- // Stream ID
+ struct InputStreamConfiguration {
+ bool configured;
+ int32_t width;
+ int32_t height;
+ int32_t format;
+ int32_t id;
+ } mInputStream;
+
+ // Request ID
Vector<int> mStreamingRequestList;
int32_t mRequestIdCounter;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index c0c2314..ba0b264 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -280,6 +280,14 @@
}
template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyPrepared(int streamId) {
+ (void)streamId;
+
+ ALOGV("%s: Stream %d now prepared",
+ __FUNCTION__, streamId);
+}
+
+template <typename TClientBase>
int Camera2ClientBase<TClientBase>::getCameraId() const {
return TClientBase::mCameraId;
}
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 168ea0a..f1cacdf 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -72,7 +72,7 @@
virtual void notifyAutoExposure(uint8_t newState, int triggerId);
virtual void notifyAutoWhitebalance(uint8_t newState,
int triggerId);
-
+ virtual void notifyPrepared(int streamId);
int getCameraId() const;
const sp<CameraDeviceBase>&
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index fe55b9e..f02fc32 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -30,6 +30,7 @@
#include "camera/CameraMetadata.h"
#include "camera/CaptureResult.h"
#include "common/CameraModule.h"
+#include "gui/IGraphicBufferProducer.h"
namespace android {
@@ -110,6 +111,14 @@
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0;
/**
+ * Create an input stream of width, height, and format.
+ *
+ * Return value is the stream ID if non-negative and an error if negative.
+ */
+ virtual status_t createInputStream(uint32_t width, uint32_t height,
+ int32_t format, /*out*/ int32_t *id) = 0;
+
+ /**
* Create an input reprocess stream that uses buffers from an existing
* output stream.
*/
@@ -150,6 +159,10 @@
*/
virtual status_t configureStreams() = 0;
+ // get the buffer producer of the input stream
+ virtual status_t getInputBufferProducer(
+ sp<IGraphicBufferProducer> *producer) = 0;
+
/**
* Create a metadata buffer with fields that the HAL device believes are
* best for the given use case
@@ -186,6 +199,7 @@
virtual void notifyIdle() = 0;
virtual void notifyShutter(const CaptureResultExtras &resultExtras,
nsecs_t timestamp) = 0;
+ virtual void notifyPrepared(int streamId) = 0;
// Required only for API1
virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
@@ -268,6 +282,12 @@
virtual status_t flush(int64_t *lastFrameNumber = NULL) = 0;
/**
+ * Prepare stream by preallocating buffers for it asynchronously.
+ * Calls notifyPrepared() once allocation is complete.
+ */
+ virtual status_t prepare(int streamId) = 0;
+
+ /**
* Get the HAL device version.
*/
virtual uint32_t getDeviceVersion() = 0;
diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp
index e5b12ae..c662853 100644
--- a/services/camera/libcameraservice/common/CameraModule.cpp
+++ b/services/camera/libcameraservice/common/CameraModule.cpp
@@ -31,18 +31,65 @@
// Keys added in HAL3.3
if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) {
+ const size_t NUM_DERIVED_KEYS_HAL3_3 = 3;
Vector<uint8_t> controlModes;
uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE;
chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1);
data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE;
chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1);
- controlModes.push(ANDROID_CONTROL_MODE_OFF);
controlModes.push(ANDROID_CONTROL_MODE_AUTO);
camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) {
controlModes.push(ANDROID_CONTROL_MODE_USE_SCENE_MODE);
}
+
+ // Only advertise CONTROL_OFF mode if 3A manual controls are supported.
+ bool isManualAeSupported = false;
+ bool isManualAfSupported = false;
+ bool isManualAwbSupported = false;
+ entry = chars.find(ANDROID_CONTROL_AE_AVAILABLE_MODES);
+ if (entry.count > 0) {
+ for (size_t i = 0; i < entry.count; i++) {
+ if (entry.data.u8[i] == ANDROID_CONTROL_AE_MODE_OFF) {
+ isManualAeSupported = true;
+ break;
+ }
+ }
+ }
+ entry = chars.find(ANDROID_CONTROL_AF_AVAILABLE_MODES);
+ if (entry.count > 0) {
+ for (size_t i = 0; i < entry.count; i++) {
+ if (entry.data.u8[i] == ANDROID_CONTROL_AF_MODE_OFF) {
+ isManualAfSupported = true;
+ break;
+ }
+ }
+ }
+ entry = chars.find(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
+ if (entry.count > 0) {
+ for (size_t i = 0; i < entry.count; i++) {
+ if (entry.data.u8[i] == ANDROID_CONTROL_AWB_MODE_OFF) {
+ isManualAwbSupported = true;
+ break;
+ }
+ }
+ }
+ if (isManualAeSupported && isManualAfSupported && isManualAwbSupported) {
+ controlModes.push(ANDROID_CONTROL_MODE_OFF);
+ }
+
chars.update(ANDROID_CONTROL_AVAILABLE_MODES, controlModes);
+
+ entry = chars.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+ Vector<int32_t> availableCharsKeys;
+ availableCharsKeys.setCapacity(entry.count + NUM_DERIVED_KEYS_HAL3_3);
+ for (size_t i = 0; i < entry.count; i++) {
+ availableCharsKeys.push(entry.data.i32[i]);
+ }
+ availableCharsKeys.push(ANDROID_CONTROL_AE_LOCK_AVAILABLE);
+ availableCharsKeys.push(ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE);
+ availableCharsKeys.push(ANDROID_CONTROL_AVAILABLE_MODES);
+ chars.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, availableCharsKeys);
}
return;
}
@@ -57,6 +104,14 @@
mCameraInfoMap.setCapacity(getNumberOfCameras());
}
+int CameraModule::init() {
+ if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 &&
+ mModule->init != NULL) {
+ return mModule->init();
+ }
+ return OK;
+}
+
int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) {
Mutex::Autolock lock(mCameraInfoLock);
if (cameraId < 0) {
@@ -78,6 +133,12 @@
if (ret != 0) {
return ret;
}
+ int deviceVersion = rawInfo.device_version;
+ if (deviceVersion < CAMERA_DEVICE_API_VERSION_2_0) {
+ // static_camera_characteristics is invalid
+ *info = rawInfo;
+ return ret;
+ }
CameraMetadata m;
m = rawInfo.static_camera_characteristics;
deriveCameraCharacteristicsKeys(rawInfo.device_version, m);
@@ -92,7 +153,7 @@
assert(index != NAME_NOT_FOUND);
// return the cached camera info
*info = mCameraInfoMap[index];
- return 0;
+ return OK;
}
int CameraModule::open(const char* id, struct hw_device_t** device) {
@@ -160,4 +221,3 @@
}
}; // namespace android
-
diff --git a/services/camera/libcameraservice/common/CameraModule.h b/services/camera/libcameraservice/common/CameraModule.h
index e285b21..c21092e 100644
--- a/services/camera/libcameraservice/common/CameraModule.h
+++ b/services/camera/libcameraservice/common/CameraModule.h
@@ -34,6 +34,10 @@
public:
CameraModule(camera_module_t *module);
+ // Must be called after construction
+ // Returns OK on success, NO_INIT on failure
+ int init();
+
int getCameraInfo(int cameraId, struct camera_info *info);
int getNumberOfCameras(void);
int open(const char* id, struct hw_device_t** device);
@@ -63,4 +67,3 @@
} // namespace android
#endif
-
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index 878986b..f6645f3 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -618,6 +618,12 @@
return waitUntilDrained();
}
+status_t Camera2Device::prepare(int streamId) {
+ ATRACE_CALL();
+ ALOGE("%s: Camera %d: unimplemented", __FUNCTION__, mId);
+ return NO_INIT;
+}
+
uint32_t Camera2Device::getDeviceVersion() {
ATRACE_CALL();
return mDeviceVersion;
@@ -1581,4 +1587,18 @@
return OK;
}
+// camera 2 devices don't support reprocessing
+status_t Camera2Device::createInputStream(
+ uint32_t width, uint32_t height, int format, int *id) {
+ ALOGE("%s: camera 2 devices don't support reprocessing", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+// camera 2 devices don't support reprocessing
+status_t Camera2Device::getInputBufferProducer(
+ sp<IGraphicBufferProducer> *producer) {
+ ALOGE("%s: camera 2 devices don't support reprocessing", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index 9b32fa6..fd1240a 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -59,6 +59,8 @@
virtual status_t createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
+ virtual status_t createInputStream(
+ uint32_t width, uint32_t height, int format, int *id);
virtual status_t createReprocessStreamFromStream(int outputId, int *id);
virtual status_t getStreamInfo(int id,
uint32_t *width, uint32_t *height, uint32_t *format);
@@ -67,6 +69,8 @@
virtual status_t deleteReprocessStream(int id);
// No-op on HAL2 devices
virtual status_t configureStreams();
+ virtual status_t getInputBufferProducer(
+ sp<IGraphicBufferProducer> *producer);
virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
virtual status_t waitUntilDrained();
virtual status_t setNotifyCallback(NotificationListener *listener);
@@ -80,6 +84,9 @@
buffer_handle_t *buffer, wp<BufferReleasedListener> listener);
// Flush implemented as just a wait
virtual status_t flush(int64_t *lastFrameNumber = NULL);
+ // Prepare is a no-op
+ virtual status_t prepare(int streamId);
+
virtual uint32_t getDeviceVersion();
virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 8236788..445c9c2 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -62,6 +62,7 @@
mUsePartialResult(false),
mNumPartialResults(1),
mNextResultFrameNumber(0),
+ mNextReprocessResultFrameNumber(0),
mNextShutterFrameNumber(0),
mListener(NULL)
{
@@ -174,6 +175,8 @@
return res;
}
+ mPreparerThread = new PreparerThread();
+
/** Everything is good to go */
mDeviceVersion = device->common.version;
@@ -201,6 +204,17 @@
}
}
+ camera_metadata_entry configs =
+ mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ for (uint32_t i = 0; i < configs.count; i += 4) {
+ if (configs.data.i32[i] == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+ configs.data.i32[i + 3] ==
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
+ mSupportedOpaqueInputSizes.add(Size(configs.data.i32[i + 1],
+ configs.data.i32[i + 2]));
+ }
+ }
+
return OK;
}
@@ -1019,6 +1033,20 @@
return configureStreamsLocked();
}
+status_t Camera3Device::getInputBufferProducer(
+ sp<IGraphicBufferProducer> *producer) {
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ if (producer == NULL) {
+ return BAD_VALUE;
+ } else if (mInputStream == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ return mInputStream->getInputBufferProducer(producer);
+}
+
status_t Camera3Device::createDefaultRequest(int templateId,
CameraMetadata *request) {
ATRACE_CALL();
@@ -1054,9 +1082,9 @@
mHal3Device, templateId);
ATRACE_END();
if (rawRequest == NULL) {
- SET_ERR_L("HAL is unable to construct default settings for template %d",
- templateId);
- return DEAD_OBJECT;
+ ALOGI("%s: template %d is not supported on this camera device",
+ __FUNCTION__, templateId);
+ return BAD_VALUE;
}
*request = rawRequest;
mRequestTemplateCache[templateId] = rawRequest;
@@ -1164,7 +1192,8 @@
ALOGW("%s: Replacing old callback listener", __FUNCTION__);
}
mListener = listener;
- mRequestThread->setNotifyCallback(listener);
+ mRequestThread->setNotificationListener(listener);
+ mPreparerThread->setNotificationListener(listener);
return OK;
}
@@ -1310,6 +1339,34 @@
return res;
}
+status_t Camera3Device::prepare(int streamId) {
+ ATRACE_CALL();
+ ALOGV("%s: Camera %d: Preparing stream %d", __FUNCTION__, mId, streamId);
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ sp<Camera3StreamInterface> stream;
+ ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
+ if (outputStreamIdx == NAME_NOT_FOUND) {
+ CLOGE("Stream %d does not exist", streamId);
+ return BAD_VALUE;
+ }
+
+ stream = mOutputStreams.editValueAt(outputStreamIdx);
+
+ if (stream->isUnpreparable() || stream->hasOutstandingBuffers() ) {
+ CLOGE("Stream %d has already been a request target", streamId);
+ return BAD_VALUE;
+ }
+
+ if (mRequestThread->isStreamPending(stream)) {
+ CLOGE("Stream %d is already a target in a pending request", streamId);
+ return BAD_VALUE;
+ }
+
+ return mPreparerThread->prepare(stream);
+}
+
uint32_t Camera3Device::getDeviceVersion() {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
@@ -1383,6 +1440,11 @@
return NULL;
}
}
+ // Check if stream is being prepared
+ if (mInputStream->isPreparing()) {
+ CLOGE("Request references an input stream that's being prepared!");
+ return NULL;
+ }
newRequest->mInputStream = mInputStream;
newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS);
@@ -1415,6 +1477,11 @@
return NULL;
}
}
+ // Check if stream is being prepared
+ if (stream->isPreparing()) {
+ CLOGE("Request references an output stream that's being prepared!");
+ return NULL;
+ }
newRequest->mOutputStreams.push(stream);
}
@@ -1423,6 +1490,17 @@
return newRequest;
}
+bool Camera3Device::isOpaqueInputSizeSupported(uint32_t width, uint32_t height) {
+ for (uint32_t i = 0; i < mSupportedOpaqueInputSizes.size(); i++) {
+ Size size = mSupportedOpaqueInputSizes[i];
+ if (size.width == width && size.height == height) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
status_t Camera3Device::configureStreamsLocked() {
ATRACE_CALL();
status_t res;
@@ -1879,7 +1957,6 @@
return true;
}
-
void Camera3Device::returnOutputBuffers(
const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
nsecs_t timestamp) {
@@ -1947,20 +2024,31 @@
void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata,
CaptureResultExtras &resultExtras,
CameraMetadata &collectedPartialResult,
- uint32_t frameNumber) {
+ uint32_t frameNumber,
+ bool reprocess) {
if (pendingMetadata.isEmpty())
return;
Mutex::Autolock l(mOutputLock);
// TODO: need to track errors for tighter bounds on expected frame number
- if (frameNumber < mNextResultFrameNumber) {
- SET_ERR("Out-of-order capture result metadata submitted! "
+ if (reprocess) {
+ if (frameNumber < mNextReprocessResultFrameNumber) {
+ SET_ERR("Out-of-order reprocess capture result metadata submitted! "
"(got frame number %d, expecting %d)",
- frameNumber, mNextResultFrameNumber);
- return;
+ frameNumber, mNextReprocessResultFrameNumber);
+ return;
+ }
+ mNextReprocessResultFrameNumber = frameNumber + 1;
+ } else {
+ if (frameNumber < mNextResultFrameNumber) {
+ SET_ERR("Out-of-order capture result metadata submitted! "
+ "(got frame number %d, expecting %d)",
+ frameNumber, mNextResultFrameNumber);
+ return;
+ }
+ mNextResultFrameNumber = frameNumber + 1;
}
- mNextResultFrameNumber = frameNumber + 1;
CaptureResult captureResult;
captureResult.mResultExtras = resultExtras;
@@ -2170,7 +2258,7 @@
CameraMetadata metadata;
metadata = result->result;
sendCaptureResult(metadata, request.resultExtras,
- collectedPartialResult, frameNumber);
+ collectedPartialResult, frameNumber, hasInputBufferInRequest);
}
}
@@ -2332,7 +2420,8 @@
// send pending result and buffers
sendCaptureResult(r.pendingMetadata, r.resultExtras,
- r.partialResult.collectedResult, msg.frame_number);
+ r.partialResult.collectedResult, msg.frame_number,
+ r.hasInputBuffer);
returnOutputBuffers(r.pendingOutputBuffers.array(),
r.pendingOutputBuffers.size(), r.shutterTimestamp);
r.pendingOutputBuffers.clear();
@@ -2367,7 +2456,7 @@
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
sp<StatusTracker> statusTracker,
camera3_device_t *hal3Device) :
- Thread(false),
+ Thread(/*canCallJava*/false),
mParent(parent),
mStatusTracker(statusTracker),
mHal3Device(hal3Device),
@@ -2383,7 +2472,7 @@
mStatusId = statusTracker->addComponent();
}
-void Camera3Device::RequestThread::setNotifyCallback(
+void Camera3Device::RequestThread::setNotificationListener(
NotificationListener *listener) {
Mutex::Autolock l(mRequestLock);
mListener = listener;
@@ -2669,7 +2758,6 @@
// Fill in buffers
if (nextRequest->mInputStream != NULL) {
- request.input_buffer = &inputBuffer;
res = nextRequest->mInputStream->getInputBuffer(&inputBuffer);
if (res != OK) {
// Can't get input buffer from gralloc queue - this could be due to
@@ -2686,6 +2774,7 @@
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return true;
}
+ request.input_buffer = &inputBuffer;
totalNumBuffers += 1;
} else {
request.input_buffer = NULL;
@@ -2797,6 +2886,26 @@
return mLatestRequest;
}
+bool Camera3Device::RequestThread::isStreamPending(
+ sp<Camera3StreamInterface>& stream) {
+ Mutex::Autolock l(mRequestLock);
+
+ for (const auto& request : mRequestQueue) {
+ for (const auto& s : request->mOutputStreams) {
+ if (stream == s) return true;
+ }
+ if (stream == request->mInputStream) return true;
+ }
+
+ for (const auto& request : mRepeatingRequests) {
+ for (const auto& s : request->mOutputStreams) {
+ if (stream == s) return true;
+ }
+ if (stream == request->mInputStream) return true;
+ }
+
+ return false;
+}
void Camera3Device::RequestThread::cleanUpFailedRequest(
camera3_capture_request_t &request,
@@ -3144,6 +3253,138 @@
return OK;
}
+/**
+ * PreparerThread inner class methods
+ */
+
+Camera3Device::PreparerThread::PreparerThread() :
+ Thread(/*canCallJava*/false), mActive(false), mCancelNow(false) {
+}
+
+Camera3Device::PreparerThread::~PreparerThread() {
+ Thread::requestExitAndWait();
+ if (mCurrentStream != nullptr) {
+ mCurrentStream->cancelPrepare();
+ ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
+ mCurrentStream.clear();
+ }
+ clear();
+}
+
+status_t Camera3Device::PreparerThread::prepare(sp<Camera3StreamInterface>& stream) {
+ status_t res;
+
+ Mutex::Autolock l(mLock);
+
+ res = stream->startPrepare();
+ if (res == OK) {
+ // No preparation needed, fire listener right off
+ ALOGV("%s: Stream %d already prepared", __FUNCTION__, stream->getId());
+ if (mListener) {
+ mListener->notifyPrepared(stream->getId());
+ }
+ return OK;
+ } else if (res != NOT_ENOUGH_DATA) {
+ return res;
+ }
+
+ // Need to prepare, start up thread if necessary
+ if (!mActive) {
+ // mRunning will change to false before the thread fully shuts down, so wait to be sure it
+ // isn't running
+ Thread::requestExitAndWait();
+ res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND);
+ if (res != OK) {
+ ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__, res, strerror(-res));
+ if (mListener) {
+ mListener->notifyPrepared(stream->getId());
+ }
+ return res;
+ }
+ mCancelNow = false;
+ mActive = true;
+ ALOGV("%s: Preparer stream started", __FUNCTION__);
+ }
+
+ // queue up the work
+ mPendingStreams.push_back(stream);
+ ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId());
+
+ return OK;
+}
+
+status_t Camera3Device::PreparerThread::clear() {
+ status_t res;
+
+ Mutex::Autolock l(mLock);
+
+ for (const auto& stream : mPendingStreams) {
+ stream->cancelPrepare();
+ }
+ mPendingStreams.clear();
+ mCancelNow = true;
+
+ return OK;
+}
+
+void Camera3Device::PreparerThread::setNotificationListener(NotificationListener *listener) {
+ Mutex::Autolock l(mLock);
+ mListener = listener;
+}
+
+bool Camera3Device::PreparerThread::threadLoop() {
+ status_t res;
+ {
+ Mutex::Autolock l(mLock);
+ if (mCurrentStream == nullptr) {
+ // End thread if done with work
+ if (mPendingStreams.empty()) {
+ ALOGV("%s: Preparer stream out of work", __FUNCTION__);
+ // threadLoop _must not_ re-acquire mLock after it sets mActive to false; would
+ // cause deadlock with prepare()'s requestExitAndWait triggered by !mActive.
+ mActive = false;
+ return false;
+ }
+
+ // Get next stream to prepare
+ auto it = mPendingStreams.begin();
+ mCurrentStream = *it;
+ mPendingStreams.erase(it);
+ ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId());
+ ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId());
+ } else if (mCancelNow) {
+ mCurrentStream->cancelPrepare();
+ ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
+ ALOGV("%s: Cancelling stream %d prepare", __FUNCTION__, mCurrentStream->getId());
+ mCurrentStream.clear();
+ mCancelNow = false;
+ return true;
+ }
+ }
+
+ res = mCurrentStream->prepareNextBuffer();
+ if (res == NOT_ENOUGH_DATA) return true;
+ if (res != OK) {
+ // Something bad happened; try to recover by cancelling prepare and
+ // signalling listener anyway
+ ALOGE("%s: Stream %d returned error %d (%s) during prepare", __FUNCTION__,
+ mCurrentStream->getId(), res, strerror(-res));
+ mCurrentStream->cancelPrepare();
+ }
+
+ // This stream has finished, notify listener
+ Mutex::Autolock l(mLock);
+ if (mListener) {
+ ALOGV("%s: Stream %d prepare done, signaling listener", __FUNCTION__,
+ mCurrentStream->getId());
+ mListener->notifyPrepared(mCurrentStream->getId());
+ }
+
+ ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
+ mCurrentStream.clear();
+
+ return true;
+}
/**
* Static callback forwarding methods from HAL to instance
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index a77548d..4fbcb2e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -116,6 +116,8 @@
virtual status_t deleteReprocessStream(int id);
virtual status_t configureStreams();
+ virtual status_t getInputBufferProducer(
+ sp<IGraphicBufferProducer> *producer);
virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
@@ -136,6 +138,8 @@
virtual status_t flush(int64_t *lastFrameNumber = NULL);
+ virtual status_t prepare(int streamId);
+
virtual uint32_t getDeviceVersion();
virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
@@ -179,6 +183,14 @@
uint32_t mDeviceVersion;
+ struct Size {
+ uint32_t width;
+ uint32_t height;
+ Size(uint32_t w = 0, uint32_t h = 0) : width(w), height(h){}
+ };
+ // Map from format to size.
+ Vector<Size> mSupportedOpaqueInputSizes;
+
enum Status {
STATUS_ERROR,
STATUS_UNINITIALIZED,
@@ -324,11 +336,11 @@
*/
bool tryLockSpinRightRound(Mutex& lock);
- struct Size {
- int width;
- int height;
- Size(int w, int h) : width(w), height(h){}
- };
+ /**
+ * Helper function to determine if an input size for implementation defined
+ * format is supported.
+ */
+ bool isOpaqueInputSizeSupported(uint32_t width, uint32_t height);
/**
* Helper function to get the largest Jpeg resolution (in area)
@@ -364,7 +376,7 @@
sp<camera3::StatusTracker> statusTracker,
camera3_device_t *hal3Device);
- void setNotifyCallback(NotificationListener *listener);
+ void setNotificationListener(NotificationListener *listener);
/**
* Call after stream (re)-configuration is completed.
@@ -428,6 +440,12 @@
*/
CameraMetadata getLatestRequest() const;
+ /**
+ * Returns true if the stream is a target of any queued or repeating
+ * capture request
+ */
+ bool isStreamPending(sp<camera3::Camera3StreamInterface>& stream);
+
protected:
virtual bool threadLoop();
@@ -549,7 +567,6 @@
Vector<camera3_stream_buffer_t> pendingOutputBuffers;
-
// Fields used by the partial result only
struct PartialResultInFlight {
// Set by process_capture_result once 3A has been sent to clients
@@ -600,7 +617,8 @@
resultExtras(extras),
hasInputBuffer(hasInput){
}
-};
+ };
+
// Map from frame number to the in-flight request state
typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
@@ -632,6 +650,45 @@
sp<camera3::StatusTracker> mStatusTracker;
/**
+ * Thread for preparing streams
+ */
+ class PreparerThread : private Thread, public virtual RefBase {
+ public:
+ PreparerThread();
+ ~PreparerThread();
+
+ void setNotificationListener(NotificationListener *listener);
+
+ /**
+ * Queue up a stream to be prepared. Streams are processed by
+ * a background thread in FIFO order
+ */
+ status_t prepare(sp<camera3::Camera3StreamInterface>& stream);
+
+ /**
+ * Cancel all current and pending stream preparation
+ */
+ status_t clear();
+
+ private:
+ Mutex mLock;
+
+ virtual bool threadLoop();
+
+ // Guarded by mLock
+
+ NotificationListener *mListener;
+ List<sp<camera3::Camera3StreamInterface> > mPendingStreams;
+ bool mActive;
+ bool mCancelNow;
+
+ // Only accessed by threadLoop and the destructor
+
+ sp<camera3::Camera3StreamInterface> mCurrentStream;
+ };
+ sp<PreparerThread> mPreparerThread;
+
+ /**
* Output result queue and current HAL device 3A state
*/
@@ -639,8 +696,10 @@
Mutex mOutputLock;
/**** Scope for mOutputLock ****/
-
+ // the minimal frame number of the next non-reprocess result
uint32_t mNextResultFrameNumber;
+ // the minimal frame number of the next reprocess result
+ uint32_t mNextReprocessResultFrameNumber;
uint32_t mNextShutterFrameNumber;
List<CaptureResult> mResultQueue;
Condition mResultSignal;
@@ -669,7 +728,8 @@
// partial results, and the frame number to the result queue.
void sendCaptureResult(CameraMetadata &pendingMetadata,
CaptureResultExtras &resultExtras,
- CameraMetadata &collectedPartialResult, uint32_t frameNumber);
+ CameraMetadata &collectedPartialResult, uint32_t frameNumber,
+ bool reprocess);
/**** Scope for mInFlightLock ****/
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 01edfff..ecb8ac8 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -87,7 +87,7 @@
return OK;
}
-status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) const {
*usage = DUMMY_USAGE;
return OK;
}
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index d023c57..3a3dbf4 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -89,7 +89,7 @@
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage);
+ virtual status_t getEndpointUsage(uint32_t *usage) const;
}; // class Camera3DummyStream
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 8696413..23b1c45 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -67,13 +67,18 @@
void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const {
(void) args;
String8 lines;
+
+ uint32_t consumerUsage = 0;
+ status_t res = getEndpointUsage(&consumerUsage);
+ if (res != OK) consumerUsage = 0;
+
lines.appendFormat(" State: %d\n", mState);
- lines.appendFormat(" Dims: %d x %d, format 0x%x\n",
+ lines.appendFormat(" Dims: %d x %d, format 0x%x, dataspace 0x%x\n",
camera3_stream::width, camera3_stream::height,
- camera3_stream::format);
+ camera3_stream::format, camera3_stream::data_space);
lines.appendFormat(" Max size: %zu\n", mMaxSize);
- lines.appendFormat(" Usage: %d, max HAL buffers: %d\n",
- camera3_stream::usage, camera3_stream::max_buffers);
+ lines.appendFormat(" Combined usage: %d, max HAL buffers: %d\n",
+ camera3_stream::usage | consumerUsage, camera3_stream::max_buffers);
lines.appendFormat(" Frames produced: %d, last timestamp: %" PRId64 " ns\n",
mFrameCount, mLastTimestamp);
lines.appendFormat(" Total buffers: %zu, currently dequeued: %zu\n",
@@ -156,13 +161,11 @@
// Inform tracker about becoming busy
if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG &&
- mState != STATE_IN_RECONFIG) {
+ mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) {
/**
* Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers
* before/after register_stream_buffers during initial configuration
- * or re-configuration.
- *
- * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2
+ * or re-configuration, or during prepare pre-allocation
*/
sp<StatusTracker> statusTracker = mStatusTracker.promote();
if (statusTracker != 0) {
@@ -177,9 +180,11 @@
}
status_t Camera3IOStreamBase::getBufferPreconditionCheckLocked() const {
- // Allow dequeue during IN_[RE]CONFIG for registration
+ // Allow dequeue during IN_[RE]CONFIG for registration, in
+ // PREPARING for pre-allocation
if (mState != STATE_CONFIGURED &&
- mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) {
+ mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG &&
+ mState != STATE_PREPARING) {
ALOGE("%s: Stream %d: Can't get buffers in unconfigured state %d",
__FUNCTION__, mId, mState);
return INVALID_OPERATION;
@@ -240,13 +245,11 @@
mHandoutTotalBufferCount--;
if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG &&
- mState != STATE_IN_RECONFIG) {
+ mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) {
/**
* Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers
* before/after register_stream_buffers during initial configuration
- * or re-configuration.
- *
- * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2
+ * or re-configuration, or during prepare pre-allocation
*/
ALOGV("%s: Stream %d: All buffers returned; now idle", __FUNCTION__,
mId);
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index abcf2b1..f5727e8 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -84,7 +84,7 @@
virtual size_t getHandoutInputBufferCountLocked();
- virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+ virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
status_t getBufferPreconditionCheckLocked() const;
status_t returnBufferPreconditionCheckLocked() const;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 6bf671e..2504bfd 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -65,8 +65,8 @@
assert(mConsumer != 0);
BufferItem bufferItem;
- res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false);
+ res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false);
if (res != OK) {
ALOGE("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
@@ -162,6 +162,21 @@
return returnAnyBufferLocked(buffer, /*timestamp*/0, /*output*/false);
}
+status_t Camera3InputStream::getInputBufferProducerLocked(
+ sp<IGraphicBufferProducer> *producer) {
+ ATRACE_CALL();
+
+ if (producer == NULL) {
+ return BAD_VALUE;
+ } else if (mProducer == NULL) {
+ ALOGE("%s: No input stream is configured");
+ return INVALID_OPERATION;
+ }
+
+ *producer = mProducer;
+ return OK;
+}
+
status_t Camera3InputStream::disconnectLocked() {
status_t res;
@@ -172,6 +187,8 @@
assert(mBuffersInFlight.size() == 0);
+ mConsumer->abandon();
+
/**
* no-op since we can't disconnect the producer from the consumer-side
*/
@@ -212,10 +229,17 @@
res = producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
if (res != OK || minUndequeuedBuffers < 0) {
ALOGE("%s: Stream %d: Could not query min undequeued buffers (error %d, bufCount %d)",
- __FUNCTION__, mId, res, minUndequeuedBuffers);
+ __FUNCTION__, mId, res, minUndequeuedBuffers);
return res;
}
size_t minBufs = static_cast<size_t>(minUndequeuedBuffers);
+
+ if (camera3_stream::max_buffers == 0) {
+ ALOGE("%s: %d: HAL sets max_buffer to 0. Must be at least 1.",
+ __FUNCTION__, __LINE__);
+ return INVALID_OPERATION;
+ }
+
/*
* We promise never to 'acquire' more than camera3_stream::max_buffers
* at any one time.
@@ -232,6 +256,8 @@
mConsumer = new BufferItemConsumer(consumer, camera3_stream::usage,
mTotalBufferCount);
mConsumer->setName(String8::format("Camera3-InputStream-%d", mId));
+
+ mProducer = producer;
}
res = mConsumer->setDefaultBufferSize(camera3_stream::width,
@@ -251,7 +277,7 @@
return OK;
}
-status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) const {
// Per HAL3 spec, input streams have 0 for their initial usage field.
*usage = 0;
return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index fd17f4f..9f3de10 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -49,6 +49,7 @@
private:
sp<BufferItemConsumer> mConsumer;
+ sp<IGraphicBufferProducer> mProducer;
Vector<BufferItem> mBuffersInFlight;
/**
@@ -68,11 +69,13 @@
virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
virtual status_t returnInputBufferLocked(
const camera3_stream_buffer &buffer);
+ virtual status_t getInputBufferProducerLocked(
+ sp<IGraphicBufferProducer> *producer);
virtual status_t disconnectLocked();
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage);
+ virtual status_t getEndpointUsage(uint32_t *usage) const;
}; // class Camera3InputStream
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 0c739e9..7a0331b 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -209,6 +209,13 @@
}
}
mLock.lock();
+
+ // Once a valid buffer has been returned to the queue, can no longer
+ // dequeue all buffers for preallocation.
+ if (buffer.status != CAMERA3_BUFFER_STATUS_ERROR) {
+ mStreamUnpreparable = true;
+ }
+
if (res != OK) {
close(anwReleaseFence);
}
@@ -390,14 +397,28 @@
return OK;
}
-status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) const {
status_t res;
int32_t u = 0;
res = mConsumer->query(mConsumer.get(),
NATIVE_WINDOW_CONSUMER_USAGE_BITS, &u);
- *usage = u;
+ // If an opaque output stream's endpoint is ImageReader, add
+ // GRALLOC_USAGE_HW_CAMERA_ZSL to the usage so HAL knows it will be used
+ // for the ZSL use case.
+ // Assume it's for ImageReader if the consumer usage doesn't have any of these bits set:
+ // 1. GRALLOC_USAGE_HW_TEXTURE
+ // 2. GRALLOC_USAGE_HW_RENDER
+ // 3. GRALLOC_USAGE_HW_COMPOSER
+ // 4. GRALLOC_USAGE_HW_VIDEO_ENCODER
+ if (camera3_stream::format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+ (u & (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER |
+ GRALLOC_USAGE_HW_VIDEO_ENCODER)) == 0) {
+ u |= GRALLOC_USAGE_HW_CAMERA_ZSL;
+ }
+
+ *usage = u;
return res;
}
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 12b2ebb..513b695 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -99,7 +99,7 @@
virtual status_t configureQueueLocked();
- virtual status_t getEndpointUsage(uint32_t *usage);
+ virtual status_t getEndpointUsage(uint32_t *usage) const;
}; // class Camera3OutputStream
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 4acbce3..4c40bb6 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -109,11 +109,7 @@
// oldUsage/oldMaxBuffers
return this;
case STATE_CONFIGURED:
- if (stream_type == CAMERA3_STREAM_INPUT) {
- ALOGE("%s: Cannot configure an input stream twice",
- __FUNCTION__);
- return NULL;
- } else if (hasOutstandingBuffersLocked()) {
+ if (hasOutstandingBuffersLocked()) {
ALOGE("%s: Cannot configure stream; has outstanding buffers",
__FUNCTION__);
return NULL;
@@ -194,6 +190,11 @@
return OK;
}
+ // Reset prepared state, since buffer config has changed, and existing
+ // allocations are no longer valid
+ mPrepared = false;
+ mStreamUnpreparable = false;
+
status_t res;
res = configureQueueLocked();
if (res != OK) {
@@ -244,6 +245,125 @@
return OK;
}
+bool Camera3Stream::isUnpreparable() {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ return mStreamUnpreparable;
+}
+
+status_t Camera3Stream::startPrepare() {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ status_t res = OK;
+
+ // This function should be only called when the stream is configured already.
+ if (mState != STATE_CONFIGURED) {
+ ALOGE("%s: Stream %d: Can't prepare stream if stream is not in CONFIGURED "
+ "state %d", __FUNCTION__, mId, mState);
+ return INVALID_OPERATION;
+ }
+
+ // This function can't be called if the stream has already received filled
+ // buffers
+ if (mStreamUnpreparable) {
+ ALOGE("%s: Stream %d: Can't prepare stream that's already in use",
+ __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ if (getHandoutOutputBufferCountLocked() > 0) {
+ ALOGE("%s: Stream %d: Can't prepare stream that has outstanding buffers",
+ __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ if (mPrepared) return OK;
+
+ size_t bufferCount = getBufferCountLocked();
+
+ mPreparedBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount);
+ mPreparedBufferIdx = 0;
+
+ mState = STATE_PREPARING;
+
+ return NOT_ENOUGH_DATA;
+}
+
+bool Camera3Stream::isPreparing() const {
+ Mutex::Autolock l(mLock);
+ return mState == STATE_PREPARING;
+}
+
+status_t Camera3Stream::prepareNextBuffer() {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ status_t res = OK;
+
+ // This function should be only called when the stream is preparing
+ if (mState != STATE_PREPARING) {
+ ALOGE("%s: Stream %d: Can't prepare buffer if stream is not in PREPARING "
+ "state %d", __FUNCTION__, mId, mState);
+ return INVALID_OPERATION;
+ }
+
+ // Get next buffer - this may allocate, and take a while for large buffers
+ res = getBufferLocked( &mPreparedBuffers.editItemAt(mPreparedBufferIdx) );
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Unable to allocate buffer %d during preparation",
+ __FUNCTION__, mId, mPreparedBufferIdx);
+ return NO_INIT;
+ }
+
+ mPreparedBufferIdx++;
+
+ // Check if we still have buffers left to allocate
+ if (mPreparedBufferIdx < mPreparedBuffers.size()) {
+ return NOT_ENOUGH_DATA;
+ }
+
+ // Done with prepare - mark stream as such, and return all buffers
+ // via cancelPrepare
+ mPrepared = true;
+
+ return cancelPrepareLocked();
+}
+
+status_t Camera3Stream::cancelPrepare() {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+
+ return cancelPrepareLocked();
+}
+
+status_t Camera3Stream::cancelPrepareLocked() {
+ status_t res = OK;
+
+ // This function should be only called when the stream is mid-preparing.
+ if (mState != STATE_PREPARING) {
+ ALOGE("%s: Stream %d: Can't cancel prepare stream if stream is not in "
+ "PREPARING state %d", __FUNCTION__, mId, mState);
+ return INVALID_OPERATION;
+ }
+
+ // Return all valid buffers to stream, in ERROR state to indicate
+ // they weren't filled.
+ for (size_t i = 0; i < mPreparedBufferIdx; i++) {
+ mPreparedBuffers.editItemAt(i).release_fence = -1;
+ mPreparedBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
+ returnBufferLocked(mPreparedBuffers[i], 0);
+ }
+ mPreparedBuffers.clear();
+ mPreparedBufferIdx = 0;
+
+ mState = STATE_CONFIGURED;
+
+ return res;
+}
+
status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
@@ -346,6 +466,13 @@
return res;
}
+status_t Camera3Stream::getInputBufferProducer(sp<IGraphicBufferProducer> *producer) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+
+ return getInputBufferProducerLocked(producer);
+}
+
void Camera3Stream::fireBufferListenersLocked(
const camera3_stream_buffer& /*buffer*/, bool acquired, bool output) {
List<wp<Camera3StreamBufferListener> >::iterator it, end;
@@ -420,15 +547,13 @@
ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; "
"must be set to NULL in camera3_device::ops", __FUNCTION__);
return INVALID_OPERATION;
- } else {
- ALOGD("%s: Skipping NULL check for deprecated register_stream_buffers", __FUNCTION__);
}
return OK;
- } else {
- ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__);
}
+ ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__);
+
status_t res;
size_t bufferCount = getBufferCountLocked();
@@ -484,6 +609,8 @@
returnBufferLocked(streamBuffers[i], 0);
}
+ mPrepared = true;
+
return res;
}
@@ -505,6 +632,10 @@
ALOGE("%s: This type of stream does not support input", __FUNCTION__);
return INVALID_OPERATION;
}
+status_t Camera3Stream::getInputBufferProducerLocked(sp<IGraphicBufferProducer> *producer) {
+ ALOGE("%s: This type of stream does not support input", __FUNCTION__);
+ return INVALID_OPERATION;
+}
void Camera3Stream::addBufferListener(
wp<Camera3StreamBufferListener> listener) {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index aba27fe..0543c66 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -57,8 +57,15 @@
* re-registering buffers with HAL.
*
* STATE_CONFIGURED: Stream is configured, and has registered buffers with the
- * HAL. The stream's getBuffer/returnBuffer work. The priv pointer may still be
- * modified.
+ * HAL (if necessary). The stream's getBuffer/returnBuffer work. The priv
+ * pointer may still be modified.
+ *
+ * STATE_PREPARING: The stream's buffers are being pre-allocated for use. On
+ * older HALs, this is done as part of configuration, but in newer HALs
+ * buffers may be allocated at time of first use. But some use cases require
+ * buffer allocation upfront, to minmize disruption due to lengthy allocation
+ * duration. In this state, only prepareNextBuffer() and cancelPrepare()
+ * may be called.
*
* Transition table:
*
@@ -82,6 +89,12 @@
* STATE_CONFIGURED => STATE_CONSTRUCTED:
* When disconnect() is called after making sure stream is idle with
* waitUntilIdle().
+ * STATE_CONFIGURED => STATE_PREPARING:
+ * When startPrepare is called before the stream has a buffer
+ * queued back into it for the first time.
+ * STATE_PREPARING => STATE_CONFIGURED:
+ * When sufficient prepareNextBuffer calls have been made to allocate
+ * all stream buffers, or cancelPrepare is called.
*
* Status Tracking:
* Each stream is tracked by StatusTracker as a separate component,
@@ -167,6 +180,73 @@
status_t cancelConfiguration();
/**
+ * Determine whether the stream has already become in-use (has received
+ * a valid filled buffer), which determines if a stream can still have
+ * prepareNextBuffer called on it.
+ */
+ bool isUnpreparable();
+
+ /**
+ * Start stream preparation. May only be called in the CONFIGURED state,
+ * when no valid buffers have yet been returned to this stream.
+ *
+ * If no prepartion is necessary, returns OK and does not transition to
+ * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions
+ * to PREPARING.
+ *
+ * This call performs no allocation, so is quick to call.
+ *
+ * Returns:
+ * OK if no more buffers need to be preallocated
+ * NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish
+ * buffer pre-allocation, and transitions to the PREPARING state.
+ * NO_INIT in case of a serious error from the HAL device
+ * INVALID_OPERATION if called when not in CONFIGURED state, or a
+ * valid buffer has already been returned to this stream.
+ */
+ status_t startPrepare();
+
+ /**
+ * Check if the stream is mid-preparing.
+ */
+ bool isPreparing() const;
+
+ /**
+ * Continue stream buffer preparation by allocating the next
+ * buffer for this stream. May only be called in the PREPARED state.
+ *
+ * Returns OK and transitions to the CONFIGURED state if all buffers
+ * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA.
+ *
+ * This call allocates one buffer, which may take several milliseconds for
+ * large buffers.
+ *
+ * Returns:
+ * OK if no more buffers need to be preallocated, and transitions
+ * to the CONFIGURED state.
+ * NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish
+ * buffer pre-allocation.
+ * NO_INIT in case of a serious error from the HAL device
+ * INVALID_OPERATION if called when not in CONFIGURED state, or a
+ * valid buffer has already been returned to this stream.
+ */
+ status_t prepareNextBuffer();
+
+ /**
+ * Cancel stream preparation early. In case allocation needs to be
+ * stopped, this method transitions the stream back to the CONFIGURED state.
+ * Buffers that have been allocated with prepareNextBuffer remain that way,
+ * but a later use of prepareNextBuffer will require just as many
+ * calls as if the earlier prepare attempt had not existed.
+ *
+ * Returns:
+ * OK if cancellation succeeded, and transitions to the CONFIGURED state
+ * INVALID_OPERATION if not in the PREPARING state
+ * NO_INIT in case of a serious error from the HAL device
+ */
+ status_t cancelPrepare();
+
+ /**
* Fill in the camera3_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
@@ -205,6 +285,10 @@
*/
status_t returnInputBuffer(const camera3_stream_buffer &buffer);
+ // get the buffer producer of the input buffer queue.
+ // only apply to input streams.
+ status_t getInputBufferProducer(sp<IGraphicBufferProducer> *producer);
+
/**
* Whether any of the stream's buffers are currently in use by the HAL,
* including buffers that have been returned but not yet had their
@@ -259,7 +343,8 @@
STATE_CONSTRUCTED,
STATE_IN_CONFIG,
STATE_IN_RECONFIG,
- STATE_CONFIGURED
+ STATE_CONFIGURED,
+ STATE_PREPARING
} mState;
mutable Mutex mLock;
@@ -285,6 +370,9 @@
virtual status_t returnInputBufferLocked(
const camera3_stream_buffer &buffer);
virtual bool hasOutstandingBuffersLocked() const = 0;
+ // Get the buffer producer of the input buffer queue. Only apply to input streams.
+ virtual status_t getInputBufferProducerLocked(sp<IGraphicBufferProducer> *producer);
+
// Can return -ENOTCONN when we are already disconnected (not an error)
virtual status_t disconnectLocked() = 0;
@@ -305,13 +393,17 @@
// Get the usage flags for the other endpoint, or return
// INVALID_OPERATION if they cannot be obtained.
- virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+ virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
// Tracking for idle state
wp<StatusTracker> mStatusTracker;
// Status tracker component ID
int mStatusId;
+ // Tracking for stream prepare - whether this stream can still have
+ // prepareNextBuffer called on it.
+ bool mStreamUnpreparable;
+
private:
uint32_t oldUsage;
uint32_t oldMaxBuffers;
@@ -326,6 +418,18 @@
bool acquired, bool output);
List<wp<Camera3StreamBufferListener> > mBufferListenerList;
+ status_t cancelPrepareLocked();
+
+ // Tracking for PREPARING state
+
+ // State of buffer preallocation. Only true if either prepareNextBuffer
+ // has been called sufficient number of times, or stream configuration
+ // had to register buffers with the HAL
+ bool mPrepared;
+
+ Vector<camera3_stream_buffer_t> mPreparedBuffers;
+ size_t mPreparedBufferIdx;
+
}; // class Camera3Stream
}; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index da989cd..d177b57 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -89,6 +89,68 @@
virtual status_t cancelConfiguration() = 0;
/**
+ * Determine whether the stream has already become in-use (has received
+ * a valid filled buffer), which determines if a stream can still have
+ * prepareNextBuffer called on it.
+ */
+ virtual bool isUnpreparable() = 0;
+
+ /**
+ * Start stream preparation. May only be called in the CONFIGURED state,
+ * when no valid buffers have yet been returned to this stream.
+ *
+ * If no prepartion is necessary, returns OK and does not transition to
+ * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions
+ * to PREPARING.
+ *
+ * Returns:
+ * OK if no more buffers need to be preallocated
+ * NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish
+ * buffer pre-allocation, and transitions to the PREPARING state.
+ * NO_INIT in case of a serious error from the HAL device
+ * INVALID_OPERATION if called when not in CONFIGURED state, or a
+ * valid buffer has already been returned to this stream.
+ */
+ virtual status_t startPrepare() = 0;
+
+ /**
+ * Check if the stream is mid-preparing.
+ */
+ virtual bool isPreparing() const = 0;
+
+ /**
+ * Continue stream buffer preparation by allocating the next
+ * buffer for this stream. May only be called in the PREPARED state.
+ *
+ * Returns OK and transitions to the CONFIGURED state if all buffers
+ * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA.
+ *
+ * Returns:
+ * OK if no more buffers need to be preallocated, and transitions
+ * to the CONFIGURED state.
+ * NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish
+ * buffer pre-allocation.
+ * NO_INIT in case of a serious error from the HAL device
+ * INVALID_OPERATION if called when not in CONFIGURED state, or a
+ * valid buffer has already been returned to this stream.
+ */
+ virtual status_t prepareNextBuffer() = 0;
+
+ /**
+ * Cancel stream preparation early. In case allocation needs to be
+ * stopped, this method transitions the stream back to the CONFIGURED state.
+ * Buffers that have been allocated with prepareNextBuffer remain that way,
+ * but a later use of prepareNextBuffer will require just as many
+ * calls as if the earlier prepare attempt had not existed.
+ *
+ * Returns:
+ * OK if cancellation succeeded, and transitions to the CONFIGURED state
+ * INVALID_OPERATION if not in the PREPARING state
+ * NO_INIT in case of a serious error from the HAL device
+ */
+ virtual status_t cancelPrepare() = 0;
+
+ /**
* Fill in the camera3_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
@@ -128,6 +190,13 @@
virtual status_t returnInputBuffer(const camera3_stream_buffer &buffer) = 0;
/**
+ * Get the buffer producer of the input buffer queue.
+ *
+ * This method only applies to input streams.
+ */
+ virtual status_t getInputBufferProducer(sp<IGraphicBufferProducer> *producer) = 0;
+
+ /**
* Whether any of the stream's buffers are currently in use by the HAL,
* including buffers that have been returned but not yet had their
* release fence signaled.
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index ad5486d..aa40a2d 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
+#include <utils/Condition.h>
#include <utils/Mutex.h>
+#include <utils/Timers.h>
#include <algorithm>
#include <utility>
@@ -263,6 +265,16 @@
*/
std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
+ /**
+ * Block until the given client is no longer in the active clients list, or the timeout
+ * occurred.
+ *
+ * Returns NO_ERROR if this succeeded, -ETIMEDOUT on a timeout, or a negative error code on
+ * failure.
+ */
+ status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
+ nsecs_t timeout) const;
+
protected:
~ClientManager();
@@ -284,6 +296,7 @@
int64_t getCurrentCostLocked() const;
mutable Mutex mLock;
+ mutable Condition mRemovedCondition;
int32_t mMaxCost;
// LRU ordered, most recent at end
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
@@ -430,6 +443,7 @@
}), mClients.end());
mClients.push_back(client);
+ mRemovedCondition.broadcast();
return evicted;
}
@@ -487,6 +501,7 @@
void ClientManager<KEY, VALUE>::removeAll() {
Mutex::Autolock lock(mLock);
mClients.clear();
+ mRemovedCondition.broadcast();
}
template<class KEY, class VALUE>
@@ -505,6 +520,39 @@
return false;
}), mClients.end());
+ mRemovedCondition.broadcast();
+ return ret;
+}
+
+template<class KEY, class VALUE>
+status_t ClientManager<KEY, VALUE>::waitUntilRemoved(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
+ nsecs_t timeout) const {
+ status_t ret = NO_ERROR;
+ Mutex::Autolock lock(mLock);
+
+ bool isRemoved = false;
+
+ // Figure out what time in the future we should hit the timeout
+ nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + timeout;
+
+ while (!isRemoved) {
+ isRemoved = true;
+ for (const auto& i : mClients) {
+ if (i == client) {
+ isRemoved = false;
+ }
+ }
+
+ if (!isRemoved) {
+ ret = mRemovedCondition.waitRelative(mLock, timeout);
+ if (ret != NO_ERROR) {
+ break;
+ }
+ timeout = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+ }
+
return ret;
}
@@ -520,6 +568,7 @@
}
return false;
}), mClients.end());
+ mRemovedCondition.broadcast();
}
template<class KEY, class VALUE>
diff --git a/services/mediaresourcemanager/Android.mk b/services/mediaresourcemanager/Android.mk
index 84218cf..b72230f 100644
--- a/services/mediaresourcemanager/Android.mk
+++ b/services/mediaresourcemanager/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := ResourceManagerService.cpp
+LOCAL_SRC_FILES := ResourceManagerService.cpp ServiceLog.cpp
LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog
@@ -13,6 +13,9 @@
LOCAL_C_INCLUDES += \
$(TOPDIR)frameworks/av/include
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 7296d47..e2b6695 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -29,6 +29,7 @@
#include <unistd.h>
#include "ResourceManagerService.h"
+#include "ServiceLog.h"
namespace android {
@@ -88,29 +89,76 @@
return infos.editItemAt(infos.size() - 1);
}
+status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */) {
+ Mutex::Autolock lock(mLock);
+
+ String8 result;
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this);
+ result.append(buffer);
+ result.append(" Policies:\n");
+ snprintf(buffer, SIZE, " SupportsMultipleSecureCodecs: %d\n", mSupportsMultipleSecureCodecs);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " SupportsSecureWithNonSecureCodec: %d\n", mSupportsSecureWithNonSecureCodec);
+ result.append(buffer);
+
+ result.append(" Processes:\n");
+ for (size_t i = 0; i < mMap.size(); ++i) {
+ snprintf(buffer, SIZE, " Pid: %d\n", mMap.keyAt(i));
+ result.append(buffer);
+
+ const ResourceInfos &infos = mMap.valueAt(i);
+ for (size_t j = 0; j < infos.size(); ++j) {
+ result.append(" Client:\n");
+ snprintf(buffer, SIZE, " Id: %lld\n", (long long)infos[j].clientId);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, " Name: %s\n", infos[j].client->getName().string());
+ result.append(buffer);
+
+ Vector<MediaResource> resources = infos[j].resources;
+ result.append(" Resources:\n");
+ for (size_t k = 0; k < resources.size(); ++k) {
+ snprintf(buffer, SIZE, " %s\n", resources[k].toString().string());
+ result.append(buffer);
+ }
+ }
+ }
+ result.append(" Events logs (most recent at top):\n");
+ result.append(mServiceLog->toString(" " /* linePrefix */));
+
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
ResourceManagerService::ResourceManagerService()
: mProcessInfo(new ProcessInfo()),
+ mServiceLog(new ServiceLog()),
mSupportsMultipleSecureCodecs(true),
mSupportsSecureWithNonSecureCodec(true) {}
ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo)
: mProcessInfo(processInfo),
+ mServiceLog(new ServiceLog()),
mSupportsMultipleSecureCodecs(true),
mSupportsSecureWithNonSecureCodec(true) {}
ResourceManagerService::~ResourceManagerService() {}
void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) {
- ALOGV("config(%s)", getString(policies).string());
+ String8 log = String8::format("config(%s)", getString(policies).string());
+ mServiceLog->add(log);
Mutex::Autolock lock(mLock);
for (size_t i = 0; i < policies.size(); ++i) {
String8 type = policies[i].mType;
- uint64_t value = policies[i].mValue;
+ String8 value = policies[i].mValue;
if (type == kPolicySupportsMultipleSecureCodecs) {
- mSupportsMultipleSecureCodecs = (value != 0);
+ mSupportsMultipleSecureCodecs = (value == "true");
} else if (type == kPolicySupportsSecureWithNonSecureCodec) {
- mSupportsSecureWithNonSecureCodec = (value != 0);
+ mSupportsSecureWithNonSecureCodec = (value == "true");
}
}
}
@@ -120,17 +168,20 @@
int64_t clientId,
const sp<IResourceManagerClient> client,
const Vector<MediaResource> &resources) {
- ALOGV("addResource(pid %d, clientId %lld, resources %s)",
+ String8 log = String8::format("addResource(pid %d, clientId %lld, resources %s)",
pid, (long long) clientId, getString(resources).string());
+ mServiceLog->add(log);
Mutex::Autolock lock(mLock);
ResourceInfos& infos = getResourceInfosForEdit(pid, mMap);
ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos);
+ // TODO: do the merge instead of append.
info.resources.appendVector(resources);
}
void ResourceManagerService::removeResource(int64_t clientId) {
- ALOGV("removeResource(%lld)", (long long) clientId);
+ String8 log = String8::format("removeResource(%lld)", (long long) clientId);
+ mServiceLog->add(log);
Mutex::Autolock lock(mLock);
bool found = false;
@@ -155,8 +206,9 @@
bool ResourceManagerService::reclaimResource(
int callingPid, const Vector<MediaResource> &resources) {
- ALOGV("reclaimResource(callingPid %d, resources %s)",
+ String8 log = String8::format("reclaimResource(callingPid %d, resources %s)",
callingPid, getString(resources).string());
+ mServiceLog->add(log);
Vector<sp<IResourceManagerClient>> clients;
{
@@ -197,19 +249,59 @@
}
}
}
+
+ if (clients.size() == 0) {
+ // if we are here, run the third pass to free one codec with the same type.
+ for (size_t i = 0; i < resources.size(); ++i) {
+ String8 type = resources[i].mType;
+ if (type == kResourceSecureCodec || type == kResourceNonSecureCodec) {
+ sp<IResourceManagerClient> client;
+ if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) {
+ return false;
+ }
+ clients.push_back(client);
+ }
+ }
+ }
}
if (clients.size() == 0) {
return false;
}
+ sp<IResourceManagerClient> failedClient;
for (size_t i = 0; i < clients.size(); ++i) {
- ALOGV("reclaimResource from client %p", clients[i].get());
+ log = String8::format("reclaimResource from client %p", clients[i].get());
+ mServiceLog->add(log);
if (!clients[i]->reclaimResource()) {
- return false;
+ failedClient = clients[i];
+ break;
}
}
- return true;
+
+ {
+ Mutex::Autolock lock(mLock);
+ bool found = false;
+ for (size_t i = 0; i < mMap.size(); ++i) {
+ ResourceInfos &infos = mMap.editValueAt(i);
+ for (size_t j = 0; j < infos.size();) {
+ if (infos[j].client == failedClient) {
+ j = infos.removeAt(j);
+ found = true;
+ } else {
+ ++j;
+ }
+ }
+ if (found) {
+ break;
+ }
+ }
+ if (!found) {
+ ALOGV("didn't find failed client");
+ }
+ }
+
+ return (failedClient == NULL);
}
bool ResourceManagerService::getAllClients_l(
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 2ed9bf8..0d9d878 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -30,6 +30,7 @@
namespace android {
+class ServiceLog;
struct ProcessInfoInterface;
struct ResourceInfo {
@@ -48,6 +49,8 @@
public:
static char const *getServiceName() { return "media.resource_manager"; }
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
ResourceManagerService();
ResourceManagerService(sp<ProcessInfoInterface> processInfo);
@@ -94,6 +97,7 @@
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
+ sp<ServiceLog> mServiceLog;
PidResourceInfosMap mMap;
bool mSupportsMultipleSecureCodecs;
bool mSupportsSecureWithNonSecureCodec;
diff --git a/services/mediaresourcemanager/ServiceLog.cpp b/services/mediaresourcemanager/ServiceLog.cpp
new file mode 100644
index 0000000..791e797
--- /dev/null
+++ b/services/mediaresourcemanager/ServiceLog.cpp
@@ -0,0 +1,63 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ServiceLog"
+#include <utils/Log.h>
+
+#include <time.h>
+
+#include "ServiceLog.h"
+
+static const size_t kDefaultMaxNum = 100;
+
+namespace android {
+
+ServiceLog::ServiceLog() : mMaxNum(kDefaultMaxNum), mLogs(mMaxNum) {}
+ServiceLog::ServiceLog(size_t maxNum) : mMaxNum(maxNum), mLogs(mMaxNum) {}
+
+void ServiceLog::add(const String8 &log) {
+ Mutex::Autolock lock(mLock);
+ time_t now = time(0);
+ char buf[64];
+ strftime(buf, sizeof(buf), "%m-%d %T", localtime(&now));
+ mLogs.add(String8::format("%s %s", buf, log.string()));
+}
+
+String8 ServiceLog::toString(const char *linePrefix) const {
+ Mutex::Autolock lock(mLock);
+ String8 result;
+ for (const auto& log : mLogs) {
+ addLine(log.string(), linePrefix, &result);
+ }
+ if (mLogs.size() == mMaxNum) {
+ addLine("...", linePrefix, &result);
+ } else if (mLogs.size() == 0) {
+ addLine("[no events yet]", linePrefix, &result);
+ }
+ return result;
+}
+
+void ServiceLog::addLine(const char *log, const char *prefix, String8 *result) const {
+ if (prefix != NULL) {
+ result->append(prefix);
+ }
+ result->append(log);
+ result->append("\n");
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/ServiceLog.h b/services/mediaresourcemanager/ServiceLog.h
new file mode 100644
index 0000000..a6f16eb
--- /dev/null
+++ b/services/mediaresourcemanager/ServiceLog.h
@@ -0,0 +1,50 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_SERVICELOG_H
+#define ANDROID_SERVICELOG_H
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+
+#include "media/RingBuffer.h"
+
+namespace android {
+
+class ServiceLog : public RefBase {
+public:
+ ServiceLog();
+ ServiceLog(size_t maxNum);
+
+ void add(const String8 &log);
+ String8 toString(const char *linePrefix = NULL) const;
+
+private:
+ size_t mMaxNum;
+ mutable Mutex mLock;
+ RingBuffer<String8> mLogs;
+
+ void addLine(const char *log, const char *prefix, String8 *result) const;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_SERVICELOG_H
diff --git a/services/mediaresourcemanager/test/Android.mk b/services/mediaresourcemanager/test/Android.mk
index 228b62a..3b4ef0d 100644
--- a/services/mediaresourcemanager/test/Android.mk
+++ b/services/mediaresourcemanager/test/Android.mk
@@ -20,6 +20,35 @@
frameworks/av/include \
frameworks/av/services/mediaresourcemanager \
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
+LOCAL_32_BIT_ONLY := true
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := ServiceLog_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ ServiceLog_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libmedia \
+ libresourcemanagerservice \
+ libutils \
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/include \
+ frameworks/av/services/mediaresourcemanager \
+
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
LOCAL_32_BIT_ONLY := true
include $(BUILD_NATIVE_TEST)
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index b73e1bc..3d53f1f 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,6 +55,10 @@
return true;
}
+ virtual String8 getName() {
+ return String8("test_client");
+ }
+
bool reclaimed() const {
return mReclaimed;
}
@@ -118,6 +122,20 @@
client3->reset();
}
+ // test set up
+ // ---------------------------------------------------------------------------------
+ // pid priority client type number
+ // ---------------------------------------------------------------------------------
+ // kTestPid1(30) 30 mTestClient1 secure codec 1
+ // graphic memory 200
+ // graphic memory 200
+ // ---------------------------------------------------------------------------------
+ // kTestPid2(20) 20 mTestClient2 non-secure codec 1
+ // graphic memory 300
+ // -------------------------------------------
+ // mTestClient3 secure codec 1
+ // graphic memory 100
+ // ---------------------------------------------------------------------------------
void addResource() {
// kTestPid1 mTestClient1
Vector<MediaResource> resources1;
@@ -162,17 +180,27 @@
EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
Vector<MediaResourcePolicy> policies1;
- policies1.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 1));
policies1.push_back(
- MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 0));
+ MediaResourcePolicy(
+ String8(kPolicySupportsMultipleSecureCodecs),
+ String8("true")));
+ policies1.push_back(
+ MediaResourcePolicy(
+ String8(kPolicySupportsSecureWithNonSecureCodec),
+ String8("false")));
mService->config(policies1);
EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs);
EXPECT_FALSE(mService->mSupportsSecureWithNonSecureCodec);
Vector<MediaResourcePolicy> policies2;
- policies2.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 0));
policies2.push_back(
- MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 1));
+ MediaResourcePolicy(
+ String8(kPolicySupportsMultipleSecureCodecs),
+ String8("false")));
+ policies2.push_back(
+ MediaResourcePolicy(
+ String8(kPolicySupportsSecureWithNonSecureCodec),
+ String8("true")));
mService->config(policies2);
EXPECT_FALSE(mService->mSupportsMultipleSecureCodecs);
EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
@@ -202,10 +230,12 @@
int lowPriorityPid = 100;
EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients));
int midPriorityPid = 25;
- EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients));
+ // some higher priority process (e.g. kTestPid2) owns the resource, so getAllClients_l
+ // will fail.
+ EXPECT_FALSE(mService->getAllClients_l(midPriorityPid, type, &clients));
int highPriorityPid = 10;
- EXPECT_TRUE(mService->getAllClients_l(10, unknowType, &clients));
- EXPECT_TRUE(mService->getAllClients_l(10, type, &clients));
+ EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, unknowType, &clients));
+ EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, type, &clients));
EXPECT_EQ(2u, clients.size());
EXPECT_EQ(mTestClient3, clients[0]);
@@ -308,6 +338,30 @@
// nothing left
EXPECT_FALSE(mService->reclaimResource(10, resources));
}
+
+ // ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
+ {
+ addResource();
+ mService->mSupportsMultipleSecureCodecs = true;
+ mService->mSupportsSecureWithNonSecureCodec = true;
+
+ Vector<MediaResource> resources;
+ resources.push_back(MediaResource(String8(kResourceSecureCodec), 1));
+
+ EXPECT_TRUE(mService->reclaimResource(10, resources));
+ // secure codec from lowest process got reclaimed
+ verifyClients(true, false, false);
+
+ // call again should reclaim another secure codec from lowest process
+ EXPECT_TRUE(mService->reclaimResource(10, resources));
+ verifyClients(false, false, true);
+
+ // nothing left
+ EXPECT_FALSE(mService->reclaimResource(10, resources));
+
+ // clean up client 2 which still has non secure codec left
+ mService->removeResource((int64_t) mTestClient2.get());
+ }
}
void testReclaimResourceNonSecure() {
@@ -360,6 +414,26 @@
// nothing left
EXPECT_FALSE(mService->reclaimResource(10, resources));
}
+
+ // ### secure codec can coexist with non-secure codec ###
+ {
+ addResource();
+ mService->mSupportsSecureWithNonSecureCodec = true;
+
+ Vector<MediaResource> resources;
+ resources.push_back(MediaResource(String8(kResourceNonSecureCodec), 1));
+
+ EXPECT_TRUE(mService->reclaimResource(10, resources));
+ // one non secure codec from lowest process got reclaimed
+ verifyClients(false, true, false);
+
+ // nothing left
+ EXPECT_FALSE(mService->reclaimResource(10, resources));
+
+ // clean up client 1 and 3 which still have secure codec left
+ mService->removeResource((int64_t) mTestClient1.get());
+ mService->removeResource((int64_t) mTestClient3.get());
+ }
}
void testGetLowestPriorityBiggestClient() {
diff --git a/services/mediaresourcemanager/test/ServiceLog_test.cpp b/services/mediaresourcemanager/test/ServiceLog_test.cpp
new file mode 100644
index 0000000..9172499
--- /dev/null
+++ b/services/mediaresourcemanager/test/ServiceLog_test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ServiceLog_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "ServiceLog.h"
+
+namespace android {
+
+class ServiceLogTest : public ::testing::Test {
+public:
+ ServiceLogTest() : mServiceLog(new ServiceLog(3)) {
+ }
+
+protected:
+ sp<ServiceLog> mServiceLog;
+};
+
+TEST_F(ServiceLogTest, addThenToString) {
+ String8 logString;
+
+ mServiceLog->add(String8("log1"));
+ logString = mServiceLog->toString();
+ EXPECT_TRUE(logString.contains("log1"));
+ ALOGV("toString:\n%s", logString.string());
+
+ static const char kTestLogPrefix[] = "testlogprefix: ";
+ logString = mServiceLog->toString(kTestLogPrefix);
+ EXPECT_TRUE(logString.contains(kTestLogPrefix));
+ EXPECT_TRUE(logString.contains("log1"));
+ ALOGV("toString:\n%s", logString.string());
+
+ mServiceLog->add(String8("log2"));
+ logString = mServiceLog->toString();
+ EXPECT_TRUE(logString.contains("log1"));
+ EXPECT_TRUE(logString.contains("log2"));
+ ALOGV("toString:\n%s", logString.string());
+
+ mServiceLog->add(String8("log3"));
+ logString = mServiceLog->toString();
+ EXPECT_TRUE(logString.contains("log1"));
+ EXPECT_TRUE(logString.contains("log2"));
+ EXPECT_TRUE(logString.contains("log3"));
+ ALOGV("toString:\n%s", logString.string());
+
+ mServiceLog->add(String8("log4"));
+ logString = mServiceLog->toString();
+ EXPECT_FALSE(logString.contains("log1"));
+ EXPECT_TRUE(logString.contains("log2"));
+ EXPECT_TRUE(logString.contains("log3"));
+ EXPECT_TRUE(logString.contains("log4"));
+ ALOGV("toString:\n%s", logString.string());
+
+ mServiceLog->add(String8("log5"));
+ logString = mServiceLog->toString();
+ EXPECT_FALSE(logString.contains("log1"));
+ EXPECT_FALSE(logString.contains("log2"));
+ EXPECT_TRUE(logString.contains("log3"));
+ EXPECT_TRUE(logString.contains("log4"));
+ EXPECT_TRUE(logString.contains("log5"));
+ ALOGV("toString:\n%s", logString.string());
+}
+
+} // namespace android
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index a6c2bdf..cd0f5f3 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -146,7 +146,7 @@
radio = module->addClient(client, config, withAudio);
if (radio == 0) {
- NO_INIT;
+ return NO_INIT;
}
return NO_ERROR;
}
@@ -500,13 +500,12 @@
if (audio) {
notifyDeviceConnection(true, "");
}
+ ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
} else {
+ ALOGW("%s open_tuner failed with error %d", __FUNCTION__, ret);
moduleClient.clear();
}
-
- ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
-
return moduleClient;
}
diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp
index 75f68b8..e14a771 100644
--- a/soundtrigger/ISoundTriggerHwService.cpp
+++ b/soundtrigger/ISoundTriggerHwService.cpp
@@ -40,6 +40,8 @@
SET_CAPTURE_STATE,
};
+#define MAX_ITEMS_PER_LIST 1024
+
class BpSoundTriggerHwService: public BpInterface<ISoundTriggerHwService>
{
public:
@@ -116,10 +118,18 @@
case LIST_MODULES: {
CHECK_INTERFACE(ISoundTriggerHwService, data, reply);
unsigned int numModulesReq = data.readInt32();
+ if (numModulesReq > MAX_ITEMS_PER_LIST) {
+ numModulesReq = MAX_ITEMS_PER_LIST;
+ }
unsigned int numModules = numModulesReq;
struct sound_trigger_module_descriptor *modules =
(struct sound_trigger_module_descriptor *)calloc(numModulesReq,
sizeof(struct sound_trigger_module_descriptor));
+ if (modules == NULL) {
+ reply->writeInt32(NO_MEMORY);
+ reply->writeInt32(0);
+ return NO_ERROR;
+ }
status_t status = listModules(modules, &numModules);
reply->writeInt32(status);
reply->writeInt32(numModules);