Merge "Record profile, level, bytes for codecs" into oc-mr1-dev
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index 8308095..28252c0 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -42,7 +42,9 @@
* Repeating request encountered an error and was stopped.
*
* @param lastFrameNumber Frame number of the last frame of the streaming request.
+ * @param repeatingRequestId the ID of the repeating request being stopped
*/
- oneway void onRepeatingRequestError(in long lastFrameNumber);
+ oneway void onRepeatingRequestError(in long lastFrameNumber,
+ in int repeatingRequestId);
oneway void onRequestQueueEmpty();
}
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 229b159..3ae208a 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -1353,7 +1353,8 @@
}
binder::Status
-CameraDevice::ServiceCallback::onRepeatingRequestError(int64_t lastFrameNumber) {
+CameraDevice::ServiceCallback::onRepeatingRequestError(
+ int64_t lastFrameNumber, int32_t stoppedSequenceId) {
binder::Status ret = binder::Status::ok();
sp<CameraDevice> dev = mDevice.promote();
@@ -1364,7 +1365,9 @@
Mutex::Autolock _l(dev->mDeviceLock);
int repeatingSequenceId = dev->mRepeatingSequenceId;
- dev->mRepeatingSequenceId = REQUEST_ID_NONE;
+ if (stoppedSequenceId == repeatingSequenceId) {
+ dev->mRepeatingSequenceId = REQUEST_ID_NONE;
+ }
dev->checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber);
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index c566cd2..855efe1 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -75,7 +75,8 @@
const CaptureResultExtras& resultExtras) override;
binder::Status onPrepared(int streamId) override;
binder::Status onRequestQueueEmpty() override;
- binder::Status onRepeatingRequestError(int64_t lastFrameNumber) override;
+ binder::Status onRepeatingRequestError(int64_t lastFrameNumber,
+ int32_t stoppedSequenceId) override;
private:
const wp<CameraDevice> mDevice;
};
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 946e3b8..51d9214 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -217,8 +217,10 @@
return binder::Status::ok();
}
- virtual binder::Status onRepeatingRequestError(int64_t lastFrameNumber) {
+ virtual binder::Status onRepeatingRequestError(
+ int64_t lastFrameNumber, int32_t stoppedSequenceId) {
(void) lastFrameNumber;
+ (void) stoppedSequenceId;
Mutex::Autolock l(mLock);
mLastStatus = REPEATING_REQUEST_ERROR;
mStatusesHit.push_back(mLastStatus);
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index a9302ea..57ff5b8 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -44,6 +44,13 @@
analyticsItem.setInt64(kParentAttribute, *parentId);
}
+ // Report the package name.
+ if (metricsGroup.has_app_package_name()) {
+ AString app_package_name(metricsGroup.app_package_name().c_str(),
+ metricsGroup.app_package_name().size());
+ analyticsItem.setPkgName(app_package_name);
+ }
+
for (int i = 0; i < metricsGroup.metric_size(); ++i) {
const MetricsGroup_Metric& metric = metricsGroup.metric(i);
if (!metric.has_name()) {
@@ -73,7 +80,12 @@
}
analyticsItem.setFinalized(true);
- analyticsItem.selfrecord();
+ if (!analyticsItem.selfrecord()) {
+ // Note the cast to int is because we build on 32 and 64 bit.
+ // The cast prevents a peculiar printf problem where one format cannot
+ // satisfy both.
+ ALOGE("selfrecord() returned false. sessioId %d", (int) sessionId);
+ }
for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
index 2d26f14..7e3bcf5 100644
--- a/drm/libmediadrm/protos/plugin_metrics.proto
+++ b/drm/libmediadrm/protos/plugin_metrics.proto
@@ -44,4 +44,7 @@
// Allow multiple sub groups of metrics.
repeated MetricsGroup metric_sub_group = 2;
+
+ // Name of the application package associated with the metrics.
+ optional string app_package_name = 3;
}
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
index 5fdac5c..ec07d87 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -25,10 +25,28 @@
#include "Session.h"
+namespace {
+const android::String8 kStreaming("Streaming");
+const android::String8 kOffline("Offline");
+const android::String8 kTrue("True");
+
+const android::String8 kQueryKeyLicenseType("LicenseType");
+ // Value: "Streaming" or "Offline"
+const android::String8 kQueryKeyPlayAllowed("PlayAllowed");
+ // Value: "True" or "False"
+const android::String8 kQueryKeyRenewAllowed("RenewAllowed");
+ // Value: "True" or "False"
+};
+
namespace clearkeydrm {
using android::sp;
+DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary)
+ : mSessionLibrary(sessionLibrary) {
+ mPlayPolicy.clear();
+}
+
status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) {
sp<Session> session = mSessionLibrary->createSession();
sessionId = session->sessionId();
@@ -60,18 +78,28 @@
if (scope.size() == 0) {
return android::BAD_VALUE;
}
+
if (keyType != kKeyType_Streaming) {
return android::ERROR_DRM_CANNOT_HANDLE;
}
+
*keyRequestType = DrmPlugin::kKeyRequestType_Initial;
defaultUrl.clear();
sp<Session> session = mSessionLibrary->findSession(scope);
if (!session.get()) {
return android::ERROR_DRM_SESSION_NOT_OPENED;
}
+
return session->getKeyRequest(initData, mimeType, &request);
}
+void DrmPlugin::setPlayPolicy() {
+ mPlayPolicy.clear();
+ mPlayPolicy.add(kQueryKeyLicenseType, kStreaming);
+ mPlayPolicy.add(kQueryKeyPlayAllowed, kTrue);
+ mPlayPolicy.add(kQueryKeyRenewAllowed, kTrue);
+}
+
status_t DrmPlugin::provideKeyResponse(
const Vector<uint8_t>& scope,
const Vector<uint8_t>& response,
@@ -83,6 +111,8 @@
if (!session.get()) {
return android::ERROR_DRM_SESSION_NOT_OPENED;
}
+
+ setPlayPolicy();
status_t res = session->provideKeyResponse(response);
if (res == android::OK) {
// This is for testing AMediaDrm_setOnEventListener only.
@@ -111,4 +141,18 @@
return android::OK;
}
+status_t DrmPlugin::queryKeyStatus(
+ const Vector<uint8_t>& sessionId,
+ KeyedVector<String8, String8>& infoMap) const {
+
+ if (sessionId.size() == 0) {
+ return android::BAD_VALUE;
+ }
+
+ infoMap.clear();
+ for (size_t i = 0; i < mPlayPolicy.size(); ++i) {
+ infoMap.add(mPlayPolicy.keyAt(i), mPlayPolicy.valueAt(i));
+ }
+ return android::OK;
+}
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
index 58421b9..f37a706 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -39,8 +39,8 @@
class DrmPlugin : public android::DrmPlugin {
public:
- explicit DrmPlugin(SessionLibrary* sessionLibrary)
- : mSessionLibrary(sessionLibrary) {}
+ explicit DrmPlugin(SessionLibrary* sessionLibrary);
+
virtual ~DrmPlugin() {}
virtual status_t openSession(Vector<uint8_t>& sessionId);
@@ -81,13 +81,7 @@
virtual status_t queryKeyStatus(
const Vector<uint8_t>& sessionId,
- KeyedVector<String8, String8>& infoMap) const {
- if (sessionId.size() == 0) {
- return android::BAD_VALUE;
- }
- UNUSED(infoMap);
- return android::ERROR_DRM_CANNOT_HANDLE;
- }
+ KeyedVector<String8, String8>& infoMap) const;
virtual status_t getProvisionRequest(
const String8& cert_type,
@@ -248,9 +242,12 @@
}
private:
- DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
+ void setPlayPolicy();
+ android::KeyedVector<android::String8, android::String8> mPlayPolicy;
SessionLibrary* mSessionLibrary;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
};
} // namespace clearkeydrm
diff --git a/media/libaaudio/examples/utils/AAudioExampleUtils.h b/media/libaaudio/examples/utils/AAudioExampleUtils.h
index 9ef62c9..c179ce6 100644
--- a/media/libaaudio/examples/utils/AAudioExampleUtils.h
+++ b/media/libaaudio/examples/utils/AAudioExampleUtils.h
@@ -31,18 +31,51 @@
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000)
const char *getSharingModeText(aaudio_sharing_mode_t mode) {
- const char *modeText = "unknown";
+ const char *text = "unknown";
switch (mode) {
- case AAUDIO_SHARING_MODE_EXCLUSIVE:
- modeText = "EXCLUSIVE";
- break;
- case AAUDIO_SHARING_MODE_SHARED:
- modeText = "SHARED";
- break;
- default:
- break;
+ case AAUDIO_SHARING_MODE_EXCLUSIVE:
+ text = "EXCLUSIVE";
+ break;
+ case AAUDIO_SHARING_MODE_SHARED:
+ text = "SHARED";
+ break;
+ default:
+ break;
}
- return modeText;
+ return text;
+}
+
+const char *getPerformanceModeText(aaudio_performance_mode_t mode) {
+ const char *text = "unknown";
+ switch (mode) {
+ case AAUDIO_PERFORMANCE_MODE_NONE:
+ text = "NONE";
+ break;
+ case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+ text = "LOW_LATENCY";
+ break;
+ case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+ text = "POWER_SAVING";
+ break;
+ default:
+ break;
+ }
+ return text;
+}
+
+const char *getDirectionText(aaudio_direction_t direction) {
+ const char *text = "unknown";
+ switch (direction) {
+ case AAUDIO_DIRECTION_INPUT:
+ text = "INPUT";
+ break;
+ case AAUDIO_DIRECTION_OUTPUT:
+ text = "OUTPUT";
+ break;
+ default:
+ break;
+ }
+ return text;
}
static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index d2e7f23..606c4ba 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -36,6 +36,13 @@
// How long to sleep in a callback to cause an intentional glitch. For testing.
#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
+#define MAX_TIMESTAMPS 16
+
+typedef struct Timestamp {
+ int64_t position;
+ int64_t nanoseconds;
+} Timestamp;
+
/**
* Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
*/
@@ -227,10 +234,12 @@
SineGenerator sineOsc1;
SineGenerator sineOsc2;
+ Timestamp timestamps[MAX_TIMESTAMPS];
int64_t framesTotal = 0;
int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
int32_t minNumFrames = INT32_MAX;
int32_t maxNumFrames = 0;
+ int32_t timestampCount = 0; // in timestamps
int scheduler = 0;
bool schedulerChecked = false;
@@ -273,6 +282,17 @@
sineData->schedulerChecked = true;
}
+ if (sineData->timestampCount < MAX_TIMESTAMPS) {
+ Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
+ aaudio_result_t result = AAudioStream_getTimestamp(stream,
+ CLOCK_MONOTONIC, ×tamp->position, ×tamp->nanoseconds);
+ if (result == AAUDIO_OK && // valid?
+ (sineData->timestampCount == 0 || // first one?
+ (timestamp->position != (timestamp - 1)->position))) { // advanced position?
+ sineData->timestampCount++; // keep this one
+ }
+ }
+
if (numFrames > sineData->maxNumFrames) {
sineData->maxNumFrames = numFrames;
}
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 2280b72..4f9cde6 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -120,6 +120,18 @@
goto error;
}
+ for (int i = 0; i < myData.timestampCount; i++) {
+ Timestamp *timestamp = &myData.timestamps[i];
+ bool retro = (i > 0 &&
+ ((timestamp->position < (timestamp - 1)->position)
+ || ((timestamp->nanoseconds < (timestamp - 1)->nanoseconds))));
+ const char *message = retro ? " <= RETROGRADE!" : "";
+ printf("Timestamp %3d : %8lld, %8lld %s\n", i,
+ (long long) timestamp->position,
+ (long long) timestamp->nanoseconds,
+ message);
+ }
+
if (myData.schedulerChecked) {
printf("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n",
myData.scheduler,
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 7f2e495..2fdbfaf 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -23,7 +23,6 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include <stdint.h>
-#include <assert.h>
#include <binder/IServiceManager.h>
@@ -55,7 +54,7 @@
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
-#define LOG_TIMESTAMPS 0
+#define LOG_TIMESTAMPS 0
AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService)
: AudioStream()
@@ -63,12 +62,11 @@
, mAudioEndpoint()
, mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
, mFramesPerBurst(16)
- , mStreamVolume(1.0f)
, mInService(inService)
, mServiceInterface(serviceInterface)
+ , mAtomicTimestamp()
, mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND)
, mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND)
- , mAtomicTimestamp()
{
ALOGD("AudioStreamInternal(): mWakeupDelayNanos = %d, mMinimumSleepNanos = %d",
mWakeupDelayNanos, mMinimumSleepNanos);
@@ -196,10 +194,6 @@
}
setState(AAUDIO_STREAM_STATE_OPEN);
- // Only connect to AudioManager if this is a playback stream running in client process.
- if (!mInService && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
- init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
- }
return result;
@@ -209,7 +203,8 @@
}
aaudio_result_t AudioStreamInternal::close() {
- ALOGD("AudioStreamInternal::close(): mServiceStreamHandle = 0x%08X",
+ aaudio_result_t result = AAUDIO_OK;
+ ALOGD("close(): mServiceStreamHandle = 0x%08X",
mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
// Don't close a stream while it is running.
@@ -218,10 +213,10 @@
requestStop();
aaudio_stream_state_t nextState;
int64_t timeoutNanoseconds = MIN_TIMEOUT_NANOS;
- aaudio_result_t result = waitForStateChange(currentState, &nextState,
+ result = waitForStateChange(currentState, &nextState,
timeoutNanoseconds);
if (result != AAUDIO_OK) {
- ALOGE("AudioStreamInternal::close() waitForStateChange() returned %d %s",
+ ALOGE("close() waitForStateChange() returned %d %s",
result, AAudio_convertResultToText(result));
}
}
@@ -232,8 +227,11 @@
mServiceInterface.closeStream(serviceStreamHandle);
delete[] mCallbackBuffer;
mCallbackBuffer = nullptr;
+
setState(AAUDIO_STREAM_STATE_CLOSED);
- return mEndPointParcelable.close();
+ result = mEndPointParcelable.close();
+ aaudio_result_t result2 = AudioStream::close();
+ return (result != AAUDIO_OK) ? result : result2;
} else {
return AAUDIO_ERROR_INVALID_HANDLE;
}
@@ -250,26 +248,46 @@
}
}
+/*
+ * It normally takes about 20-30 msec to start a stream on the server.
+ * But the first time can take as much as 200-300 msec. The HW
+ * starts right away so by the time the client gets a chance to write into
+ * the buffer, it is already in a deep underflow state. That can cause the
+ * XRunCount to be non-zero, which could lead an app to tune its latency higher.
+ * To avoid this problem, we set a request for the processing code to start the
+ * client stream at the same position as the server stream.
+ * The processing code will then save the current offset
+ * between client and server and apply that to any position given to the app.
+ */
aaudio_result_t AudioStreamInternal::requestStart()
{
int64_t startTime;
- ALOGD("AudioStreamInternal()::requestStart()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::requestStart() mServiceStreamHandle invalid");
+ ALOGE("requestStart() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
if (isActive()) {
- ALOGE("AudioStreamInternal::requestStart() already active");
+ ALOGE("requestStart() already active");
return AAUDIO_ERROR_INVALID_STATE;
}
- aaudio_stream_state_t originalState = getState();
+ aaudio_stream_state_t originalState = getState();
+ if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ ALOGE("requestStart() but DISCONNECTED");
+ return AAUDIO_ERROR_DISCONNECTED;
+ }
setState(AAUDIO_STREAM_STATE_STARTING);
- aaudio_result_t result = AAudioConvert_androidToAAudioResult(startWithStatus());
+
+ // Clear any stale timestamps from the previous run.
+ drainTimestampsFromService();
+
+ aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
startTime = AudioClock::getNanoseconds();
mClockModel.start(startTime);
+ mNeedCatchUp.request(); // Ask data processing code to catch up when first timestamp received.
+ // Start data callback thread.
if (result == AAUDIO_OK && getDataCallbackProc() != nullptr) {
// Launch the callback loop thread.
int64_t periodNanos = mCallbackFrames
@@ -314,14 +332,16 @@
aaudio_result_t AudioStreamInternal::requestStopInternal()
{
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
+ ALOGE("requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
mServiceStreamHandle);
return AAUDIO_ERROR_INVALID_STATE;
}
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_STOPPING);
- return AAudioConvert_androidToAAudioResult(stopWithStatus());
+ mAtomicTimestamp.clear();
+
+ return mServiceInterface.stopStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternal::requestStop()
@@ -336,7 +356,7 @@
aaudio_result_t AudioStreamInternal::registerThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::registerThread() mServiceStreamHandle invalid");
+ ALOGE("registerThread() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.registerAudioThread(mServiceStreamHandle,
@@ -346,7 +366,7 @@
aaudio_result_t AudioStreamInternal::unregisterThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::unregisterThread() mServiceStreamHandle invalid");
+ ALOGE("unregisterThread() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, gettid());
@@ -374,12 +394,14 @@
// Generated in server and passed to client. Return latest.
if (mAtomicTimestamp.isValid()) {
Timestamp timestamp = mAtomicTimestamp.read();
- *framePosition = timestamp.getPosition();
- *timeNanoseconds = timestamp.getNanoseconds();
- return AAUDIO_OK;
- } else {
- return AAUDIO_ERROR_UNAVAILABLE;
+ int64_t position = timestamp.getPosition() + mFramesOffsetFromService;
+ if (position >= 0) {
+ *framePosition = position;
+ *timeNanoseconds = timestamp.getNanoseconds();
+ return AAUDIO_OK;
+ }
}
+ return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_result_t AudioStreamInternal::updateStateMachine() {
@@ -394,14 +416,14 @@
static int64_t oldTime = 0;
int64_t framePosition = command.timestamp.position;
int64_t nanoTime = command.timestamp.timestamp;
- ALOGD("AudioStreamInternal: timestamp says framePosition = %08lld at nanoTime %lld",
+ ALOGD("logTimestamp: timestamp says framePosition = %8lld at nanoTime %lld",
(long long) framePosition,
(long long) nanoTime);
int64_t nanosDelta = nanoTime - oldTime;
if (nanosDelta > 0 && oldTime > 0) {
int64_t framesDelta = framePosition - oldPosition;
int64_t rate = (framesDelta * AAUDIO_NANOS_PER_SECOND) / nanosDelta;
- ALOGD("AudioStreamInternal: framesDelta = %08lld, nanosDelta = %08lld, rate = %lld",
+ ALOGD("logTimestamp: framesDelta = %8lld, nanosDelta = %8lld, rate = %lld",
(long long) framesDelta, (long long) nanosDelta, (long long) rate);
}
oldPosition = framePosition;
@@ -478,6 +500,34 @@
return result;
}
+aaudio_result_t AudioStreamInternal::drainTimestampsFromService() {
+ aaudio_result_t result = AAUDIO_OK;
+
+ while (result == AAUDIO_OK) {
+ AAudioServiceMessage message;
+ if (mAudioEndpoint.readUpCommand(&message) != 1) {
+ break; // no command this time, no problem
+ }
+ switch (message.what) {
+ // ignore most messages
+ case AAudioServiceMessage::code::TIMESTAMP_SERVICE:
+ case AAudioServiceMessage::code::TIMESTAMP_HARDWARE:
+ break;
+
+ case AAudioServiceMessage::code::EVENT:
+ result = onEventFromServer(&message);
+ break;
+
+ default:
+ ALOGE("WARNING - drainTimestampsFromService() Unrecognized what = %d",
+ (int) message.what);
+ result = AAUDIO_ERROR_INTERNAL;
+ break;
+ }
+ }
+ return result;
+}
+
// Process all the commands coming from the server.
aaudio_result_t AudioStreamInternal::processCommands() {
aaudio_result_t result = AAUDIO_OK;
@@ -502,7 +552,7 @@
break;
default:
- ALOGE("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
+ ALOGE("WARNING - processCommands() Unrecognized what = %d",
(int) message.what);
result = AAUDIO_ERROR_INTERNAL;
break;
@@ -613,7 +663,7 @@
}
aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(requestedFrames, &actualFrames);
- ALOGD("AudioStreamInternal::setBufferSize() req = %d => %d", requestedFrames, actualFrames);
+ ALOGD("setBufferSize() req = %d => %d", requestedFrames, actualFrames);
if (result < 0) {
return result;
} else {
@@ -636,32 +686,3 @@
aaudio_result_t AudioStreamInternal::joinThread(void** returnArg) {
return AudioStream::joinThread(returnArg, calculateReasonableTimeout(getFramesPerBurst()));
}
-
-void AudioStreamInternal::doSetVolume() {
- // No pan and only left volume is taken into account from IPLayer interface
- mVolumeRamp.setTarget(mStreamVolume * mVolumeMultiplierL /* * mPanMultiplierL */);
-}
-
-
-//------------------------------------------------------------------------------
-// Implementation of PlayerBase
-status_t AudioStreamInternal::playerStart() {
- return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.startStream(mServiceStreamHandle));
-}
-
-status_t AudioStreamInternal::playerPause() {
- return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.pauseStream(mServiceStreamHandle));
-}
-
-status_t AudioStreamInternal::playerStop() {
- return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.stopStream(mServiceStreamHandle));
-}
-
-status_t AudioStreamInternal::playerSetVolume() {
- doSetVolume();
- return NO_ERROR;
-}
-
-void AudioStreamInternal::destroy() {
- baseDestroy();
-}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 3523294..47024c0 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -18,7 +18,6 @@
#define ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
#include <stdint.h>
-#include <media/PlayerBase.h>
#include <aaudio/AAudio.h>
#include "binding/IAAudioService.h"
@@ -36,7 +35,7 @@
namespace aaudio {
// A stream that talks to the AAudioService or directly to a HAL.
-class AudioStreamInternal : public AudioStream, public android::PlayerBase {
+class AudioStreamInternal : public AudioStream {
public:
AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService);
@@ -85,9 +84,6 @@
// Calculate timeout based on framesPerBurst
int64_t calculateReasonableTimeout();
- //PlayerBase virtuals
- virtual void destroy();
-
aaudio_result_t startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle);
@@ -115,12 +111,15 @@
int64_t currentTimeNanos,
int64_t *wakeTimePtr) = 0;
+ aaudio_result_t drainTimestampsFromService();
+
aaudio_result_t processCommands();
aaudio_result_t requestStopInternal();
aaudio_result_t stopCallback();
+ virtual void advanceClientToMatchServerPosition() = 0;
virtual void onFlushFromServer() {}
@@ -135,14 +134,6 @@
// Calculate timeout for an operation involving framesPerOperation.
int64_t calculateReasonableTimeout(int32_t framesPerOperation);
- void doSetVolume();
-
- //PlayerBase virtuals
- virtual status_t playerStart();
- virtual status_t playerPause();
- virtual status_t playerStop();
- virtual status_t playerSetVolume();
-
aaudio_format_t mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
IsochronousClockModel mClockModel; // timing model for chasing the HAL
@@ -153,9 +144,6 @@
int32_t mFramesPerBurst; // frames per HAL transfer
int32_t mXRunCount = 0; // how many underrun events?
- LinearRamp mVolumeRamp;
- float mStreamVolume;
-
// Offset from underlying frame position.
int64_t mFramesOffsetFromService = 0; // offset for timestamps
@@ -167,6 +155,12 @@
AAudioServiceInterface &mServiceInterface; // abstract interface to the service
+ SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
+
+ AtomicRequestor mNeedCatchUp; // Ask read() or write() to sync on first timestamp.
+
+ float mStreamVolume = 1.0f;
+
private:
/*
* Asynchronous write with data conversion.
@@ -188,10 +182,7 @@
AudioEndpointParcelable mEndPointParcelable; // description of the buffers filled by service
EndpointDescriptor mEndpointDescriptor; // buffer description with resolved addresses
- SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
-
int64_t mServiceLatencyNanos = 0;
-
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 7b1e53e..b792ecd 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -39,6 +39,21 @@
AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
+void AudioStreamInternalCapture::advanceClientToMatchServerPosition() {
+ int64_t readCounter = mAudioEndpoint.getDataReadCounter();
+ int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+
+ // Bump offset so caller does not see the retrograde motion in getFramesRead().
+ int64_t offset = readCounter - writeCounter;
+ mFramesOffsetFromService += offset;
+ ALOGD("advanceClientToMatchServerPosition() readN = %lld, writeN = %lld, offset = %lld",
+ (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
+
+ // Force readCounter to match writeCounter.
+ // This is because we cannot change the write counter in the hardware.
+ mAudioEndpoint.setDataReadCounter(writeCounter);
+}
+
// Write the data, block if needed and timeoutMillis > 0
aaudio_result_t AudioStreamInternalCapture::read(void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
@@ -57,6 +72,18 @@
const char *traceName = "aaRdNow";
ATRACE_BEGIN(traceName);
+ if (mClockModel.isStarting()) {
+ // Still haven't got any timestamps from server.
+ // Keep waiting until we get some valid timestamps then start writing to the
+ // current buffer position.
+ ALOGD("processDataNow() wait for valid timestamps");
+ // Sleep very briefly and hope we get a timestamp soon.
+ *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
+ ATRACE_END();
+ return 0;
+ }
+ // If we have gotten this far then we have at least one timestamp from server.
+
if (mAudioEndpoint.isFreeRunning()) {
//ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
// Update data queue based on the timing model.
@@ -65,6 +92,14 @@
mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
}
+ // This code assumes that we have already received valid timestamps.
+ if (mNeedCatchUp.isRequested()) {
+ // Catch an MMAP pointer that is already advancing.
+ // This will avoid initial underruns caused by a slow cold start.
+ advanceClientToMatchServerPosition();
+ mNeedCatchUp.acknowledge();
+ }
+
// If the write index passed the read index then consider it an overrun.
if (mAudioEndpoint.getEmptyFramesAvailable() < 0) {
mXRunCount++;
@@ -100,8 +135,8 @@
// Calculate frame position based off of the readCounter because
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextReadPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
- wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
+ int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
+ wakeTime = mClockModel.convertPositionToTime(nextPosition);
}
break;
default:
@@ -186,8 +221,7 @@
}
int64_t AudioStreamInternalCapture::getFramesRead() {
- int64_t frames = mAudioEndpoint.getDataWriteCounter()
- + mFramesOffsetFromService;
+ int64_t frames = mAudioEndpoint.getDataReadCounter() + mFramesOffsetFromService;
//ALOGD("AudioStreamInternalCapture::getFramesRead() returns %lld", (long long)frames);
return frames;
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 17f37e8..294dbaf 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -46,6 +46,8 @@
}
protected:
+ void advanceClientToMatchServerPosition() override;
+
/**
* Low level data processing that will not block. It will just read or write as much as it can.
*
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 31e0a40..1e02eee 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -48,7 +48,8 @@
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_PAUSING);
- return AAudioConvert_androidToAAudioResult(pauseWithStatus());
+ mAtomicTimestamp.clear();
+ return mServiceInterface.pauseStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternalPlay::requestPause()
@@ -72,21 +73,25 @@
return mServiceInterface.flushStream(mServiceStreamHandle);
}
-void AudioStreamInternalPlay::onFlushFromServer() {
+void AudioStreamInternalPlay::advanceClientToMatchServerPosition() {
int64_t readCounter = mAudioEndpoint.getDataReadCounter();
int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
// Bump offset so caller does not see the retrograde motion in getFramesRead().
- int64_t framesFlushed = writeCounter - readCounter;
- mFramesOffsetFromService += framesFlushed;
- ALOGD("AudioStreamInternal::onFlushFromServer() readN = %lld, writeN = %lld, offset = %lld",
+ int64_t offset = writeCounter - readCounter;
+ mFramesOffsetFromService += offset;
+ ALOGD("advanceClientToMatchServerPosition() readN = %lld, writeN = %lld, offset = %lld",
(long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
- // Flush written frames by forcing writeCounter to readCounter.
- // This is because we cannot move the read counter in the hardware.
+ // Force writeCounter to match readCounter.
+ // This is because we cannot change the read counter in the hardware.
mAudioEndpoint.setDataWriteCounter(readCounter);
}
+void AudioStreamInternalPlay::onFlushFromServer() {
+ advanceClientToMatchServerPosition();
+}
+
// Write the data, block if needed and timeoutMillis > 0
aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
@@ -106,6 +111,18 @@
const char *traceName = "aaWrNow";
ATRACE_BEGIN(traceName);
+ if (mClockModel.isStarting()) {
+ // Still haven't got any timestamps from server.
+ // Keep waiting until we get some valid timestamps then start writing to the
+ // current buffer position.
+ ALOGD("processDataNow() wait for valid timestamps");
+ // Sleep very briefly and hope we get a timestamp soon.
+ *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
+ ATRACE_END();
+ return 0;
+ }
+ // If we have gotten this far then we have at least one timestamp from server.
+
// If a DMA channel or DSP is reading the other end then we have to update the readCounter.
if (mAudioEndpoint.isFreeRunning()) {
// Update data queue based on the timing model.
@@ -114,6 +131,13 @@
mAudioEndpoint.setDataReadCounter(estimatedReadCounter);
}
+ if (mNeedCatchUp.isRequested()) {
+ // Catch an MMAP pointer that is already advancing.
+ // This will avoid initial underruns caused by a slow cold start.
+ advanceClientToMatchServerPosition();
+ mNeedCatchUp.acknowledge();
+ }
+
// If the read index passed the write index then consider it an underrun.
if (mAudioEndpoint.getFullFramesAvailable() < 0) {
mXRunCount++;
@@ -153,9 +177,9 @@
// Calculate frame position based off of the writeCounter because
// the readCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
+ int64_t nextPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
- mAudioEndpoint.getBufferSizeInFrames();
- wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
+ wakeTime = mClockModel.convertPositionToTime(nextPosition);
}
break;
default:
@@ -266,7 +290,6 @@
return framesWritten;
}
-
int64_t AudioStreamInternalPlay::getFramesRead()
{
int64_t framesReadHardware;
@@ -340,3 +363,10 @@
result, (int) isActive());
return NULL;
}
+
+//------------------------------------------------------------------------------
+// Implementation of PlayerBase
+status_t AudioStreamInternalPlay::doSetVolume() {
+ mVolumeRamp.setTarget(mStreamVolume * getDuckAndMuteVolume());
+ return android::NO_ERROR;
+}
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index e59d02c..98783de 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -50,12 +50,19 @@
return AAUDIO_DIRECTION_OUTPUT;
}
+ // Only register client side streams.
+ bool needsSystemRegistration() override { return !mInService; }
+
protected:
aaudio_result_t requestPauseInternal();
+ void advanceClientToMatchServerPosition() override;
+
void onFlushFromServer() override;
+ android::status_t doSetVolume() override;
+
/**
* Low level write that will not block. It will just write as much as it can.
*
@@ -78,6 +85,9 @@
int32_t numFrames);
int64_t mLastFramesRead = 0; // used to prevent retrograde motion
+
+ LinearRamp mVolumeRamp;
+
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index c06c8a9..bac69f1 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -48,19 +48,26 @@
}
void IsochronousClockModel::start(int64_t nanoTime) {
- ALOGD("IsochronousClockModel::start(nanos = %lld)\n", (long long) nanoTime);
+ ALOGV("IsochronousClockModel::start(nanos = %lld)\n", (long long) nanoTime);
mMarkerNanoTime = nanoTime;
mState = STATE_STARTING;
}
void IsochronousClockModel::stop(int64_t nanoTime) {
- ALOGD("IsochronousClockModel::stop(nanos = %lld)\n", (long long) nanoTime);
+ ALOGV("IsochronousClockModel::stop(nanos = %lld)\n", (long long) nanoTime);
setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
// TODO should we set position?
mState = STATE_STOPPED;
}
+bool IsochronousClockModel::isStarting() {
+ return mState == STATE_STARTING;
+}
+
void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) {
+// ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
+// (long long)framePosition,
+// (long long)nanoTime);
int64_t framesDelta = framePosition - mMarkerFramePosition;
int64_t nanosDelta = nanoTime - mMarkerNanoTime;
if (nanosDelta < 1000) {
@@ -70,9 +77,6 @@
// ALOGD("processTimestamp() - mMarkerFramePosition = %lld at mMarkerNanoTime %llu",
// (long long)mMarkerFramePosition,
// (long long)mMarkerNanoTime);
-// ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
-// (long long)framePosition,
-// (long long)nanoTime);
int64_t expectedNanosDelta = convertDeltaPositionToTime(framesDelta);
// ALOGD("processTimestamp() - expectedNanosDelta = %lld, nanosDelta = %llu",
@@ -116,6 +120,8 @@
default:
break;
}
+
+// ALOGD("processTimestamp() - mState = %d", mState);
}
void IsochronousClockModel::setSampleRate(int32_t sampleRate) {
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 585f53a..7182376 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -36,6 +36,8 @@
void start(int64_t nanoTime);
void stop(int64_t nanoTime);
+ bool isStarting();
+
void processTimestamp(int64_t framePosition, int64_t nanoTime);
/**
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 5089b00..63569ad 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -219,6 +219,7 @@
ALOGD("AAudioStreamBuilder_openStream() returns %d = %s for (%p) ----------------",
result, AAudio_convertResultToText(result), audioStream);
if (result == AAUDIO_OK) {
+ audioStream->systemRegister();
*streamPtr = (AAudioStream*) audioStream;
} else {
*streamPtr = nullptr;
@@ -242,6 +243,7 @@
ALOGD("AAudioStream_close(%p)", stream);
if (audioStream != nullptr) {
audioStream->close();
+ audioStream->systemUnRegister();
delete audioStream;
return AAUDIO_OK;
}
@@ -252,7 +254,7 @@
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestStart(%p) called --------------", stream);
- aaudio_result_t result = audioStream->requestStart();
+ aaudio_result_t result = audioStream->systemStart();
ALOGD("AAudioStream_requestStart(%p) returned %d ---------", stream, result);
return result;
}
@@ -261,7 +263,7 @@
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestPause(%p)", stream);
- return audioStream->requestPause();
+ return audioStream->systemPause();
}
AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream)
@@ -275,7 +277,7 @@
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestStop(%p)", stream);
- return audioStream->requestStop();
+ return audioStream->systemStop();
}
AAUDIO_API aaudio_result_t AAudioStream_waitForStateChange(AAudioStream* stream,
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 4f1cc37..35a1c6a 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG "AAudioStream"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -29,13 +29,24 @@
using namespace aaudio;
AudioStream::AudioStream()
- : mCallbackEnabled(false)
+ : mPlayerBase(new MyPlayerBase(this))
{
// mThread is a pthread_t of unknown size so we need memset.
memset(&mThread, 0, sizeof(mThread));
setPeriodNanoseconds(0);
}
+AudioStream::~AudioStream() {
+ ALOGD("destroying %p, state = %s", this, AAudio_convertStreamStateToText(getState()));
+ // If the stream is deleted when OPEN or in use then audio resources will leak.
+ // This would indicate an internal error. So we want to find this ASAP.
+ LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
+ || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
+ || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
+ "aaudio stream still in use, state = %s",
+ AAudio_convertStreamStateToText(getState()));
+}
+
static const char *AudioStream_convertSharingModeToShortText(aaudio_sharing_mode_t sharingMode) {
const char *result;
switch (sharingMode) {
@@ -90,9 +101,6 @@
return AAUDIO_OK;
}
-AudioStream::~AudioStream() {
- close();
-}
aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
aaudio_stream_state_t *nextState,
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index ad18751..5800fa2 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -21,10 +21,18 @@
#include <mutex>
#include <stdint.h>
#include <aaudio/AAudio.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+#include "media/VolumeShaper.h"
+#include "media/PlayerBase.h"
#include "utility/AAudioUtilities.h"
#include "utility/MonotonicCounter.h"
+// Cannot get android::media::VolumeShaper to compile!
+#define AAUDIO_USE_VOLUME_SHAPER 0
+
namespace aaudio {
typedef void *(*aaudio_audio_thread_proc_t)(void *);
@@ -234,8 +242,122 @@
return AAUDIO_ERROR_UNIMPLEMENTED;
}
+ // This is used by the AudioManager to duck and mute the stream when changing audio focus.
+ void setDuckAndMuteVolume(float duckAndMuteVolume) {
+ mDuckAndMuteVolume = duckAndMuteVolume;
+ doSetVolume(); // apply this change
+ }
+
+ float getDuckAndMuteVolume() {
+ return mDuckAndMuteVolume;
+ }
+
+ // Implement this in the output subclasses.
+ virtual android::status_t doSetVolume() { return android::NO_ERROR; }
+
+#if AAUDIO_USE_VOLUME_SHAPER
+ virtual android::binder::Status applyVolumeShaper(
+ const ::android::media::VolumeShaper::Configuration& configuration __unused,
+ const ::android::media::VolumeShaper::Operation& operation __unused) {
+ ALOGW("applyVolumeShaper() is not supported");
+ return android::binder::Status::ok();
+ }
+#endif
+
+ // Should this object be registered with the AudioManager?
+ virtual bool needsSystemRegistration() { return false; }
+
+ // Register this stream's PlayerBase with the AudioManager
+ void systemRegister() {
+ if (needsSystemRegistration()) {
+ mPlayerBase->registerWithAudioManager();
+ }
+ }
+
+ // UnRegister this stream's PlayerBase with the AudioManager
+ void systemUnRegister() {
+ if (needsSystemRegistration()) {
+ mPlayerBase->destroy();
+ }
+ }
+
+ // Pass start request through PlayerBase for tracking.
+ aaudio_result_t systemStart() {
+ mPlayerBase->start();
+ // Pass aaudio_result_t around the PlayerBase interface, which uses status__t.
+ return mPlayerBase->getResult();
+ }
+
+ aaudio_result_t systemPause() {
+ mPlayerBase->pause();
+ return mPlayerBase->getResult();
+ }
+
+ aaudio_result_t systemStop() {
+ mPlayerBase->stop();
+ return mPlayerBase->getResult();
+ }
+
protected:
+ // PlayerBase allows the system to control the stream.
+ // Calling through PlayerBase->start() notifies the AudioManager of the player state.
+ // The AudioManager also can start/stop a stream by calling mPlayerBase->playerStart().
+ // systemStart() ==> mPlayerBase->start() mPlayerBase->playerStart() ==> requestStart()
+ // \ /
+ // ------ AudioManager -------
+ class MyPlayerBase : public android::PlayerBase {
+ public:
+ MyPlayerBase(AudioStream *parent) : mParent(parent) {}
+ virtual ~MyPlayerBase() {}
+
+ void registerWithAudioManager() {
+ init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+ }
+
+ void destroy() override {
+ // FIXME what else should this do? close()? disconnect()?
+ baseDestroy();
+ }
+
+ virtual android::status_t playerStart() {
+ mResult = mParent->requestStart();
+ return AAudioConvert_aaudioToAndroidStatus(mResult);
+ }
+
+ virtual android::status_t playerPause() {
+ mResult = mParent->requestPause();
+ return AAudioConvert_aaudioToAndroidStatus(mResult);
+ }
+
+ virtual android::status_t playerStop() {
+ mResult = mParent->requestStop();
+ return AAudioConvert_aaudioToAndroidStatus(mResult);
+ }
+
+ virtual android::status_t playerSetVolume() {
+ // No pan and only left volume is taken into account from IPLayer interface
+ mParent->setDuckAndMuteVolume(mVolumeMultiplierL /* * mPanMultiplierL */);
+ return android::NO_ERROR;
+ }
+
+#if AAUDIO_USE_VOLUME_SHAPER
+ android::binder::Status applyVolumeShaper(
+ const android::media::VolumeShaper::Configuration& configuration,
+ const android::media::VolumeShaper::Operation& operation) {
+ return mParent->applyVolumeShaper(configuration, operation);
+ }
+#endif
+
+ aaudio_result_t getResult() {
+ return mResult;
+ }
+
+ AudioStream *mParent;
+ aaudio_result_t mResult = AAUDIO_OK;
+ };
+
+
/**
* This should not be called after the open() call.
@@ -275,7 +397,9 @@
std::mutex mStreamMutex;
- std::atomic<bool> mCallbackEnabled;
+ std::atomic<bool> mCallbackEnabled{false};
+
+ float mDuckAndMuteVolume = 1.0f;
protected:
@@ -288,6 +412,8 @@
}
private:
+ const android::sp<MyPlayerBase> mPlayerBase;
+
// These do not change after open().
int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
int32_t mSampleRate = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 2816bac..ee2504d 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <utils/String16.h>
#include <media/AudioTrack.h>
+#include <media/AudioTimestamp.h>
#include <aaudio/AAudio.h>
#include "core/AudioStream.h"
@@ -30,7 +31,8 @@
using namespace aaudio;
AudioStreamLegacy::AudioStreamLegacy()
- : AudioStream(), mDeviceCallback(new StreamDeviceCallback(this)) {
+ : AudioStream()
+ , mDeviceCallback(new StreamDeviceCallback(this)) {
}
AudioStreamLegacy::~AudioStreamLegacy() {
@@ -46,33 +48,51 @@
return AudioStreamLegacy_callback;
}
-// Implement FixedBlockProcessor
-int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
- int32_t frameCount = numBytes / getBytesPerFrame();
+int32_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer, int32_t numFrames) {
+ if (getDirection() == AAUDIO_DIRECTION_INPUT) {
+ // Increment before because we already got the data from the device.
+ incrementFramesRead(numFrames);
+ }
+
// Call using the AAudio callback interface.
AAudioStream_dataCallback appCallback = getDataCallbackProc();
- return (*appCallback)(
+ aaudio_data_callback_result_t callbackResult = (*appCallback)(
(AAudioStream *) this,
getDataCallbackUserData(),
buffer,
- frameCount);
+ numFrames);
+
+ if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
+ && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
+ // Increment after because we are going to write the data to the device.
+ incrementFramesWritten(numFrames);
+ }
+ return callbackResult;
+}
+
+// Implement FixedBlockProcessor
+int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
+ int32_t numFrames = numBytes / getBytesPerFrame();
+ return callDataCallbackFrames(buffer, numFrames);
}
void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
aaudio_data_callback_result_t callbackResult;
- if (!mCallbackEnabled.load()) {
- return;
- }
-
switch (opcode) {
case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
- if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
- // Note that this code assumes an AudioTrack::Buffer is the same as
- // AudioRecord::Buffer
- // TODO define our own AudioBuffer and pass it from the subclasses.
- AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
- if (audioBuffer->frameCount == 0) return;
+ checkForDisconnectRequest();
+
+ // Note that this code assumes an AudioTrack::Buffer is the same as
+ // AudioRecord::Buffer
+ // TODO define our own AudioBuffer and pass it from the subclasses.
+ AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
+ if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED || !mCallbackEnabled.load()) {
+ audioBuffer->size = 0; // silence the buffer
+ } else {
+ if (audioBuffer->frameCount == 0) {
+ return;
+ }
// If the caller specified an exact size then use a block size adapter.
if (mBlockAdapter != nullptr) {
@@ -81,40 +101,28 @@
(uint8_t *) audioBuffer->raw, byteCount);
} else {
// Call using the AAudio callback interface.
- callbackResult = (*getDataCallbackProc())(
- (AAudioStream *) this,
- getDataCallbackUserData(),
- audioBuffer->raw,
- audioBuffer->frameCount
- );
+ callbackResult = callDataCallbackFrames((uint8_t *)audioBuffer->raw,
+ audioBuffer->frameCount);
}
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
- incrementClientFrameCounter(audioBuffer->frameCount);
} else {
audioBuffer->size = 0;
}
- if (updateStateMachine() == AAUDIO_OK) {
- break; // don't fall through
+ if (updateStateMachine() != AAUDIO_OK) {
+ forceDisconnect();
+ mCallbackEnabled.store(false);
}
}
}
- /// FALL THROUGH
+ break;
// Stream got rerouted so we disconnect.
- case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: {
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ case AAUDIO_CALLBACK_OPERATION_DISCONNECTED:
ALOGD("processCallbackCommon() stream disconnected");
- if (getErrorCallbackProc() != nullptr) {
- (*getErrorCallbackProc())(
- (AAudioStream *) this,
- getErrorCallbackUserData(),
- AAUDIO_ERROR_DISCONNECTED
- );
- }
+ forceDisconnect();
mCallbackEnabled.store(false);
- }
break;
default:
@@ -122,6 +130,30 @@
}
}
+
+
+void AudioStreamLegacy::checkForDisconnectRequest() {
+ if (mRequestDisconnect.isRequested()) {
+ ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
+ forceDisconnect();
+ mRequestDisconnect.acknowledge();
+ mCallbackEnabled.store(false);
+ }
+}
+
+void AudioStreamLegacy::forceDisconnect() {
+ if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
+ setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ if (getErrorCallbackProc() != nullptr) {
+ (*getErrorCallbackProc())(
+ (AAudioStream *) this,
+ getErrorCallbackUserData(),
+ AAUDIO_ERROR_DISCONNECTED
+ );
+ }
+ }
+}
+
aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds,
@@ -139,8 +171,23 @@
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
break;
}
- status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
- return AAudioConvert_androidToAAudioResult(status);
+ ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
+ int64_t localPosition;
+ status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
+ timebase, &location);
+ // use MonotonicCounter to prevent retrograde motion.
+ mTimestampPosition.update32((int32_t)localPosition);
+ *framePosition = mTimestampPosition.get();
+
+// ALOGD("getBestTimestamp() fposition: server = %6lld, kernel = %6lld, location = %d",
+// (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_SERVER],
+// (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_KERNEL],
+// (int)location);
+ if (status == WOULD_BLOCK) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ } else {
+ return AAudioConvert_androidToAAudioResult(status);
+ }
}
void AudioStreamLegacy::onAudioDeviceUpdate(audio_port_handle_t deviceId)
@@ -148,15 +195,18 @@
ALOGD("onAudioDeviceUpdate() deviceId %d", (int)deviceId);
if (getDeviceId() != AAUDIO_UNSPECIFIED && getDeviceId() != deviceId &&
getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
- // if we have a data callback and the stream is active, send the error callback from
- // data callback thread when it sees the DISCONNECTED state
- if (!isDataCallbackActive() && getErrorCallbackProc() != nullptr) {
- (*getErrorCallbackProc())(
- (AAudioStream *) this,
- getErrorCallbackUserData(),
- AAUDIO_ERROR_DISCONNECTED
- );
+ // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
+ // If we have a data callback and the stream is active, then ask the data callback
+ // to DISCONNECT and call the error callback.
+ if (isDataCallbackActive()) {
+ ALOGD("onAudioDeviceUpdate() request DISCONNECT in data callback due to device change");
+ // If the stream is stopped before the data callback has a chance to handle the
+ // request then the requestStop() and requestPause() methods will handle it after
+ // the callback has stopped.
+ mRequestDisconnect.request();
+ } else {
+ ALOGD("onAudioDeviceUpdate() DISCONNECT the stream now");
+ forceDisconnect();
}
}
setDeviceId(deviceId);
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index d2ef3c7..7e28579 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -24,6 +24,7 @@
#include "AudioStream.h"
#include "AAudioLegacy.h"
+#include "utility/AAudioUtilities.h"
#include "utility/FixedBlockAdapter.h"
namespace aaudio {
@@ -63,6 +64,8 @@
aaudio_legacy_callback_t getLegacyCallback();
+ int32_t callDataCallbackFrames(uint8_t *buffer, int32_t numFrames);
+
// This is public so it can be called from the C callback function.
// This is called from the AudioTrack/AudioRecord client.
virtual void processCallback(int event, void *info) = 0;
@@ -109,6 +112,10 @@
void onAudioDeviceUpdate(audio_port_handle_t deviceId);
+ void checkForDisconnectRequest();
+
+ void forceDisconnect();
+
void onStart() { mCallbackEnabled.store(true); }
void onStop() { mCallbackEnabled.store(false); }
@@ -122,11 +129,14 @@
MonotonicCounter mFramesWritten;
MonotonicCounter mFramesRead;
+ MonotonicCounter mTimestampPosition;
FixedBlockAdapter *mBlockAdapter = nullptr;
aaudio_wrapping_frames_t mPositionWhenStarting = 0;
int32_t mCallbackBufferSize = 0;
const android::sp<StreamDeviceCallback> mDeviceCallback;
+
+ AtomicRequestor mRequestDisconnect;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 041280d..bc6e60c 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG "AudioStreamRecord"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -181,11 +181,12 @@
{
// TODO add close() or release() to AudioRecord API then call it from here
if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
+ mAudioRecord->removeAudioDeviceCallback(mDeviceCallback);
mAudioRecord.clear();
setState(AAUDIO_STREAM_STATE_CLOSED);
}
mFixedBlockWriter.close();
- return AAUDIO_OK;
+ return AudioStream::close();
}
void AudioStreamRecord::processCallback(int event, void *info) {
@@ -233,8 +234,11 @@
onStop();
setState(AAUDIO_STREAM_STATE_STOPPING);
incrementFramesWritten(getFramesRead() - getFramesWritten()); // TODO review
+ mTimestampPosition.set(getFramesRead());
mAudioRecord->stop();
mFramesRead.reset32();
+ mTimestampPosition.reset32();
+ checkForDisconnectRequest();
return AAUDIO_OK;
}
@@ -334,7 +338,9 @@
int64_t *timeNanoseconds) {
ExtendedTimestamp extendedTimestamp;
status_t status = mAudioRecord->getTimestamp(&extendedTimestamp);
- if (status != NO_ERROR) {
+ if (status == WOULD_BLOCK) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ } else if (status != NO_ERROR) {
return AAudioConvert_androidToAAudioResult(status);
}
return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 155362c..5e4446f 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG "AudioStreamTrack"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -115,7 +115,7 @@
ALOGD("AudioStreamTrack::open(), request notificationFrames = %d, frameCount = %u",
notificationFrames, (uint)frameCount);
- mAudioTrack = new AudioTrack();
+ mAudioTrack = new AudioTrack(); // TODO review
if (getDeviceId() != AAUDIO_UNSPECIFIED) {
mAudioTrack->setOutputDevice(getDeviceId());
}
@@ -143,8 +143,7 @@
return AAudioConvert_androidToAAudioResult(status);
}
- //TrackPlayerBase init
- init(mAudioTrack.get(), PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+ doSetVolume();
// Get the actual values from the AudioTrack.
setSamplesPerFrame(mAudioTrack->channelCount());
@@ -199,7 +198,7 @@
aaudio_result_t AudioStreamTrack::close()
{
if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
- destroy();
+ mAudioTrack->removeAudioDeviceCallback(mDeviceCallback);
setState(AAUDIO_STREAM_STATE_CLOSED);
}
mFixedBlockReader.close();
@@ -224,8 +223,7 @@
return;
}
-aaudio_result_t AudioStreamTrack::requestStart()
-{
+aaudio_result_t AudioStreamTrack::requestStart() {
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
@@ -238,7 +236,7 @@
return AAudioConvert_androidToAAudioResult(err);
}
- err = startWithStatus();
+ err = mAudioTrack->start();
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
} else {
@@ -248,12 +246,11 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamTrack::requestPause()
-{
+aaudio_result_t AudioStreamTrack::requestPause() {
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
- ALOGE("AudioStreamTrack::requestPause() no AudioTrack");
+ ALOGE("requestPause() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_STARTING
&& getState() != AAUDIO_STREAM_STATE_STARTED) {
@@ -263,7 +260,8 @@
}
onStop();
setState(AAUDIO_STREAM_STATE_PAUSING);
- pause();
+ mAudioTrack->pause();
+ checkForDisconnectRequest();
status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
@@ -285,6 +283,7 @@
incrementFramesRead(getFramesWritten() - getFramesRead());
mAudioTrack->flush();
mFramesWritten.reset32();
+ mTimestampPosition.reset32();
return AAUDIO_OK;
}
@@ -298,8 +297,11 @@
onStop();
setState(AAUDIO_STREAM_STATE_STOPPING);
incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
- stop();
+ mTimestampPosition.set(getFramesWritten());
mFramesWritten.reset32();
+ mTimestampPosition.reset32();
+ mAudioTrack->stop();
+ checkForDisconnectRequest();
return AAUDIO_OK;
}
@@ -444,8 +446,56 @@
int64_t *timeNanoseconds) {
ExtendedTimestamp extendedTimestamp;
status_t status = mAudioTrack->getTimestamp(&extendedTimestamp);
- if (status != NO_ERROR) {
+ if (status == WOULD_BLOCK) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ } if (status != NO_ERROR) {
return AAudioConvert_androidToAAudioResult(status);
}
- return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
+ int64_t position = 0;
+ int64_t nanoseconds = 0;
+ aaudio_result_t result = getBestTimestamp(clockId, &position,
+ &nanoseconds, &extendedTimestamp);
+ if (result == AAUDIO_OK) {
+ if (position < getFramesWritten()) {
+ *framePosition = position;
+ *timeNanoseconds = nanoseconds;
+ return result;
+ } else {
+ return AAUDIO_ERROR_INVALID_STATE; // TODO review, documented but not consistent
+ }
+ }
+ return result;
}
+
+status_t AudioStreamTrack::doSetVolume() {
+ status_t status = NO_INIT;
+ if (mAudioTrack.get() != nullptr) {
+ float volume = getDuckAndMuteVolume();
+ mAudioTrack->setVolume(volume, volume);
+ status = NO_ERROR;
+ }
+ return status;
+}
+
+#if AAUDIO_USE_VOLUME_SHAPER
+binder::Status AudioStreamTrack::applyVolumeShaper(
+ const VolumeShaper::Configuration& configuration,
+ const VolumeShaper::Operation& operation) {
+
+ sp<VolumeShaper::Configuration> spConfiguration = new VolumeShaper::Configuration(configuration);
+ sp<VolumeShaper::Operation> spOperation = new VolumeShaper::Operation(operation);
+
+ if (mAudioTrack.get() != nullptr) {
+ ALOGD("applyVolumeShaper() from IPlayer");
+ VolumeShaper::Status status = mAudioTrack->applyVolumeShaper(spConfiguration, spOperation);
+ if (status < 0) { // a non-negative value is the volume shaper id.
+ ALOGE("applyVolumeShaper() failed with status %d", status);
+ }
+ return binder::Status::fromStatusT(status);
+ } else {
+ ALOGD("applyVolumeShaper()"
+ " no AudioTrack for volume control from IPlayer");
+ return binder::Status::ok();
+ }
+}
+#endif
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 3230ac8..dbcb94e 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -19,6 +19,7 @@
#include <math.h>
#include <media/TrackPlayerBase.h>
+#include <media/AudioTrack.h>
#include <aaudio/AAudio.h>
#include "AudioStreamBuilder.h"
@@ -32,7 +33,7 @@
/**
* Internal stream that uses the legacy AudioTrack path.
*/
-class AudioStreamTrack : public AudioStreamLegacy, public android::TrackPlayerBase {
+class AudioStreamTrack : public AudioStreamLegacy {
public:
AudioStreamTrack();
@@ -76,8 +77,20 @@
return incrementFramesWritten(frames);
}
+ bool needsSystemRegistration() override { return true; }
+
+ android::status_t doSetVolume() override;
+
+#if AAUDIO_USE_VOLUME_SHAPER
+ virtual android::binder::Status applyVolumeShaper(
+ const android::media::VolumeShaper::Configuration& configuration,
+ const android::media::VolumeShaper::Operation& operation) override;
+#endif
+
private:
+ android::sp<android::AudioTrack> mAudioTrack;
+
// adapts between variable sized blocks and fixed size blocks
FixedBlockReader mFixedBlockReader;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index b0c6c94..3afa976 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -272,8 +272,7 @@
class SimpleDoubleBuffer {
public:
SimpleDoubleBuffer()
- : mValues()
- , mCounter(0) {}
+ : mValues() {}
__attribute__((no_sanitize("integer")))
void write(T value) {
@@ -282,6 +281,14 @@
mCounter++; // Increment AFTER updating storage, OK if it wraps.
}
+ /**
+ * This should only be called by the same thread that calls write() or when
+ * no other thread is calling write.
+ */
+ void clear() {
+ mCounter.store(0);
+ }
+
T read() const {
T result;
int before;
@@ -293,7 +300,7 @@
int index = (before & 1) ^ 1;
result = mValues[index];
after = mCounter.load();
- } while ((after != before) && --timeout > 0);
+ } while ((after != before) && (after > 0) && (--timeout > 0));
return result;
}
@@ -306,7 +313,7 @@
private:
T mValues[2];
- std::atomic<int> mCounter;
+ std::atomic<int> mCounter{0};
};
class Timestamp {
@@ -328,4 +335,35 @@
int64_t mNanoseconds;
};
+
+/**
+ * Pass a request to another thread.
+ * This is used when one thread, A, wants another thread, B, to do something.
+ * A naive approach would be for A to set a flag and for B to clear it when done.
+ * But that creates a race condition. This technique avoids the race condition.
+ *
+ * Assumes only one requester and one acknowledger.
+ */
+class AtomicRequestor {
+public:
+
+ __attribute__((no_sanitize("integer")))
+ void request() {
+ mRequested++;
+ }
+
+ __attribute__((no_sanitize("integer")))
+ bool isRequested() {
+ return (mRequested.load() - mAcknowledged.load()) > 0;
+ }
+
+ __attribute__((no_sanitize("integer")))
+ void acknowledge() {
+ mAcknowledged++;
+ }
+
+private:
+ std::atomic<int> mRequested{0};
+ std::atomic<int> mAcknowledged{0};
+};
#endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libaaudio/src/utility/MonotonicCounter.h b/media/libaaudio/src/utility/MonotonicCounter.h
index 81d7f89..13c92a2 100644
--- a/media/libaaudio/src/utility/MonotonicCounter.h
+++ b/media/libaaudio/src/utility/MonotonicCounter.h
@@ -41,6 +41,13 @@
}
/**
+ * set the current value of the counter
+ */
+ void set(int64_t counter) {
+ mCounter64 = counter;
+ }
+
+ /**
* Advance the counter if delta is positive.
* @return current value of the counter
*/
diff --git a/media/libaaudio/tests/test_timestamps.cpp b/media/libaaudio/tests/test_timestamps.cpp
index d9ca391..fb363e7 100644
--- a/media/libaaudio/tests/test_timestamps.cpp
+++ b/media/libaaudio/tests/test_timestamps.cpp
@@ -17,23 +17,99 @@
// Play silence and recover from dead servers or disconnected devices.
#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
-
#include "utils/AAudioExampleUtils.h"
+#include "../examples/utils/AAudioExampleUtils.h"
-#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+// Arbitrary period for glitches, once per second at 48000 Hz.
+#define FORCED_UNDERRUN_PERIOD_FRAMES 48000
+// How long to sleep in a callback to cause an intentional glitch. For testing.
+#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
-int main(int argc, char **argv) {
- (void) argc;
- (void *)argv;
+#define MAX_TIMESTAMPS 1000
+#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+
+#define NUM_SECONDS 1
+#define NUM_LOOPS 4
+
+typedef struct TimestampInfo {
+ int64_t framesTotal;
+ int64_t appPosition; // frames
+ int64_t appNanoseconds;
+ int64_t timestampPosition; // frames
+ int64_t timestampNanos;
+ aaudio_result_t result;
+} TimestampInfo;
+
+typedef struct TimestampCallbackData_s {
+ TimestampInfo timestamps[MAX_TIMESTAMPS];
+ int64_t framesTotal = 0;
+ int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
+ int32_t timestampCount = 0; // in timestamps
+ bool forceUnderruns = false;
+} TimestampCallbackData_t;
+
+// Callback function that fills the audio output buffer.
+aaudio_data_callback_result_t timestampDataCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ void *audioData __unused,
+ int32_t numFrames
+) {
+
+ // should not happen but just in case...
+ if (userData == nullptr) {
+ printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
+ return AAUDIO_CALLBACK_RESULT_STOP;
+ }
+ TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
+
+ aaudio_direction_t direction = AAudioStream_getDirection(stream);
+ if (direction == AAUDIO_DIRECTION_INPUT) {
+ timestampData->framesTotal += numFrames;
+ }
+
+ if (timestampData->forceUnderruns) {
+ if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
+ usleep(FORCED_UNDERRUN_SLEEP_MICROS);
+ printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
+ timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
+ }
+ }
+
+ if (timestampData->timestampCount < MAX_TIMESTAMPS) {
+ TimestampInfo *timestamp = ×tampData->timestamps[timestampData->timestampCount];
+ timestamp->result = AAudioStream_getTimestamp(stream,
+ CLOCK_MONOTONIC,
+ ×tamp->timestampPosition,
+ ×tamp->timestampNanos);
+ timestamp->framesTotal = timestampData->framesTotal;
+ timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
+ ? AAudioStream_getFramesWritten(stream)
+ : AAudioStream_getFramesRead(stream);
+ timestamp->appNanoseconds = getNanoseconds();
+ timestampData->timestampCount++;
+ }
+
+ if (direction == AAUDIO_DIRECTION_OUTPUT) {
+ timestampData->framesTotal += numFrames;
+ }
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+static TimestampCallbackData_t sTimestampData;
+
+static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
+ aaudio_sharing_mode_t sharingMode,
+ aaudio_performance_mode_t performanceMode,
+ aaudio_direction_t direction) {
aaudio_result_t result = AAUDIO_OK;
- int32_t triesLeft = 3;
- int32_t bufferCapacity;
int32_t framesPerBurst = 0;
float *buffer = nullptr;
@@ -44,22 +120,20 @@
int32_t finalBufferSize = 0;
aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
- int32_t framesMax;
- int64_t framesTotal;
- int64_t printAt;
- int samplesPerBurst;
- int64_t previousFramePosition = -1;
+ aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
AAudioStreamBuilder *aaudioBuilder = nullptr;
AAudioStream *aaudioStream = nullptr;
- // Make printf print immediately so that debug info is not stuck
- // in a buffer if we hang or crash.
- setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+ memset(&sTimestampData, 0, sizeof(sTimestampData));
- printf("Test Timestamps V0.1.1\n");
+ printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n",
+ mmapPolicy,
+ getSharingModeText(sharingMode),
+ getPerformanceModeText(performanceMode),
+ getDirectionText(direction));
- AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
+ AAudio_setMMapPolicy(mmapPolicy);
// Use an AAudioStreamBuilder to contain requested parameters.
result = AAudio_createStreamBuilder(&aaudioBuilder);
@@ -70,9 +144,11 @@
}
// Request stream properties.
- AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT);
- //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
- AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
+ AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
+ AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
+ AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
// Create an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
@@ -87,10 +163,25 @@
actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
actualDataFormat = AAudioStream_getFormat(aaudioStream);
- printf("-------- chans = %3d, rate = %6d format = %d\n",
- actualChannelCount, actualSampleRate, actualDataFormat);
+ actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
+ if (actualSharingMode != sharingMode) {
+ printf("did not get expected sharingMode, got %3d, skipping test\n",
+ actualSharingMode);
+ result = AAUDIO_OK;
+ goto finish;
+ }
+ actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
+ if (actualPerformanceMode != performanceMode) {
+ printf("did not get expected performanceMode, got %3d, skipping test\n",
+ actualPerformanceMode);
+ result = AAUDIO_OK;
+ goto finish;
+ }
+
+ printf(" chans = %3d, rate = %6d format = %d\n",
+ actualChannelCount, actualSampleRate, actualDataFormat);
printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
- ? "yes" : "no");
+ ? "yes" : "no");
// This is the number of frames that are read in one chunk by a DMA controller
// or a DSP or a mixer.
@@ -98,91 +189,151 @@
printf(" framesPerBurst = %3d\n", framesPerBurst);
originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
- requestedBufferSize = 2 * framesPerBurst;
+ requestedBufferSize = 4 * framesPerBurst;
finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
printf(" BufferSize: original = %4d, requested = %4d, final = %4d\n",
originalBufferSize, requestedBufferSize, finalBufferSize);
- samplesPerBurst = framesPerBurst * actualChannelCount;
- buffer = new float[samplesPerBurst];
-
- result = AAudioStream_requestStart(aaudioStream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_requestStart returned %s",
+ {
+ int64_t position;
+ int64_t nanoseconds;
+ result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
+ printf("before start, AAudioStream_getTimestamp() returns %s\n",
AAudio_convertResultToText(result));
- goto finish;
}
- // Play silence very briefly.
- framesMax = actualSampleRate * 4;
- framesTotal = 0;
- printAt = actualSampleRate;
- while (result == AAUDIO_OK && framesTotal < framesMax) {
- int32_t framesWritten = AAudioStream_write(aaudioStream,
- buffer, framesPerBurst,
- DEFAULT_TIMEOUT_NANOS);
- if (framesWritten < 0) {
- result = framesWritten;
- printf("write() returned %s, frames = %d\n",
- AAudio_convertResultToText(result), (int)framesTotal);
- printf(" frames = %d\n", (int)framesTotal);
- } else if (framesWritten != framesPerBurst) {
- printf("write() returned %d, frames = %d\n", framesWritten, (int)framesTotal);
- result = AAUDIO_ERROR_TIMEOUT;
- } else {
- framesTotal += framesWritten;
- if (framesTotal >= printAt) {
- printf("frames = %d\n", (int)framesTotal);
- printAt += actualSampleRate;
+ for (int runs = 0; runs < NUM_LOOPS; runs++) {
+ printf("------------------ loop #%d\n", runs);
+
+ int64_t temp = sTimestampData.framesTotal;
+ memset(&sTimestampData, 0, sizeof(sTimestampData));
+ sTimestampData.framesTotal = temp;
+
+ sTimestampData.forceUnderruns = false;
+
+ result = AAudioStream_requestStart(aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_requestStart returned %s",
+ AAudio_convertResultToText(result));
+ goto finish;
+ }
+
+ for (int second = 0; second < NUM_SECONDS; second++) {
+ // Give AAudio callback time to run in the background.
+ sleep(1);
+
+ // Periodically print the progress so we know it hasn't died.
+ printf("framesWritten = %d, XRuns = %d\n",
+ (int) AAudioStream_getFramesWritten(aaudioStream),
+ (int) AAudioStream_getXRunCount(aaudioStream)
+ );
+ }
+
+ result = AAudioStream_requestStop(aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_requestStop returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+
+ printf("timestampCount = %d\n", sTimestampData.timestampCount);
+ int printed = 0;
+ for (int i = 0; i < sTimestampData.timestampCount; i++) {
+ TimestampInfo *timestamp = &sTimestampData.timestamps[i];
+ bool posChanged = (timestamp->timestampPosition != (timestamp - 1)->timestampPosition);
+ bool timeChanged = (timestamp->timestampNanos != (timestamp - 1)->timestampNanos);
+ if ((printed < 20) && ((i < 10) || posChanged || timeChanged)) {
+ printf(" %3d : frames %8lld, xferd %8lld", i,
+ (long long) timestamp->framesTotal,
+ (long long) timestamp->appPosition);
+ if (timestamp->result != AAUDIO_OK) {
+ printf(", result = %s\n", AAudio_convertResultToText(timestamp->result));
+ } else {
+ bool negative = timestamp->timestampPosition < 0;
+ bool retro = (i > 0 && (timestamp->timestampPosition <
+ (timestamp - 1)->timestampPosition));
+ const char *message = negative ? " <=NEGATIVE!"
+ : (retro ? " <= RETROGRADE!" : "");
+
+ double latency = calculateLatencyMillis(timestamp->timestampPosition,
+ timestamp->timestampNanos,
+ timestamp->appPosition,
+ timestamp->appNanoseconds,
+ actualSampleRate);
+ printf(", STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
+ (long long) timestamp->timestampPosition,
+ (long long) timestamp->timestampNanos,
+ latency,
+ message);
+ }
+ printed++;
}
}
- // Print timestamps.
- int64_t framePosition = 0;
- int64_t frameTime = 0;
- aaudio_result_t timeResult;
- timeResult = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC,
- &framePosition, &frameTime);
-
- if (timeResult == AAUDIO_OK) {
- if (framePosition > (previousFramePosition + 5000)) {
- int64_t realTime = getNanoseconds();
- int64_t framesWritten = AAudioStream_getFramesWritten(aaudioStream);
-
- double latencyMillis = calculateLatencyMillis(framePosition, frameTime,
- framesWritten, realTime,
- actualSampleRate);
-
- printf("--- timestamp: result = %4d, position = %lld, at %lld nanos"
- ", latency = %7.2f msec\n",
- timeResult,
- (long long) framePosition,
- (long long) frameTime,
- latencyMillis);
- previousFramePosition = framePosition;
- }
- }
+ // Avoid race conditions in AudioFlinger.
+ // There is normally a delay between a real user stopping and restarting a stream.
+ sleep(1);
}
- result = AAudioStream_requestStop(aaudioStream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_requestStop returned %s\n",
- AAudio_convertResultToText(result));
- }
- result = AAudioStream_close(aaudioStream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_close returned %s\n",
- AAudio_convertResultToText(result));
- }
- aaudioStream = nullptr;
-
-
finish:
if (aaudioStream != nullptr) {
AAudioStream_close(aaudioStream);
}
AAudioStreamBuilder_delete(aaudioBuilder);
- delete[] buffer;
printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
+
+ return result;
+}
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void *) argv;
+
+ aaudio_result_t result = AAUDIO_OK;
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ printf("Test Timestamps V0.1.3\n");
+
+ // Legacy
+ aaudio_policy_t policy = AAUDIO_POLICY_NEVER;
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_DIRECTION_OUTPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_OUTPUT);
+
+ // MMAP
+ policy = AAUDIO_POLICY_ALWAYS;
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_EXCLUSIVE,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_EXCLUSIVE,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_OUTPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_OUTPUT);
+
+ return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 611cde7..398d923 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -274,7 +274,7 @@
mStatus = NO_ERROR;
mUserData = user;
// TODO: add audio hardware input latency here
- mLatency = (1000 * mFrameCount) / mSampleRate;
+ mLatency = (1000LL * mFrameCount) / mSampleRate;
mMarkerPosition = 0;
mMarkerReached = false;
mNewPosition = 0;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 31e1e2b..31df414 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -63,6 +63,14 @@
return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
}
+// TODO move to audio_utils.
+static inline struct timespec convertNsToTimespec(int64_t ns) {
+ struct timespec tv;
+ tv.tv_sec = static_cast<time_t>(ns / NANOS_PER_SECOND);
+ tv.tv_nsec = static_cast<long>(ns % NANOS_PER_SECOND);
+ return tv;
+}
+
// current monotonic time in microseconds.
static int64_t getNowUs()
{
@@ -539,7 +547,8 @@
mUpdatePeriod = 0;
mPosition = 0;
mReleased = 0;
- mStartUs = 0;
+ mStartNs = 0;
+ mStartFromZeroUs = 0;
AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
mSequence = 1;
mObservedSequence = mSequence;
@@ -587,6 +596,7 @@
mStartEts.clear();
}
}
+ mStartNs = systemTime(); // save this for timestamp adjustment after starting.
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mPosition = 0;
@@ -605,7 +615,8 @@
+ mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]),
(long long)mStartEts.mFlushed,
(long long)mFramesWritten);
- mFramesWrittenServerOffset = -mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER];
+ // mStartEts is already adjusted by mFramesWrittenServerOffset, so we delta adjust.
+ mFramesWrittenServerOffset -= mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER];
}
mFramesWritten = 0;
mProxy->clearTimestamp(); // need new server push for valid timestamp
@@ -615,7 +626,7 @@
// since the flush is asynchronous and stop may not fully drain.
// We save the time when the track is started to later verify whether
// the counters are realistic (i.e. start from zero after this time).
- mStartUs = getNowUs();
+ mStartFromZeroUs = mStartNs / 1000;
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
@@ -1268,7 +1279,7 @@
ALOGW("getLatency(%d) failed status %d", mOutput, status);
} else {
// FIXME don't believe this lie
- mLatency = mAfLatency + (1000 * mFrameCount) / mSampleRate;
+ mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate;
}
}
@@ -2086,7 +2097,14 @@
// Convert frame units to time units
nsecs_t ns = NS_WHENEVER;
if (minFrames != (uint32_t) ~0) {
- ns = framesToNanoseconds(minFrames, sampleRate, speed) + kWaitPeriodNs;
+ // AudioFlinger consumption of client data may be irregular when coming out of device
+ // standby since the kernel buffers require filling. This is throttled to no more than 2x
+ // the expected rate in the MixerThread. Hence, we reduce the estimated time to wait by one
+ // half (but no more than half a second) to improve callback accuracy during these temporary
+ // data surges.
+ const nsecs_t estimatedNs = framesToNanoseconds(minFrames, sampleRate, speed);
+ constexpr nsecs_t maxThrottleCompensationNs = 500000000LL;
+ ns = estimatedNs - min(estimatedNs / 2, maxThrottleCompensationNs) + kWaitPeriodNs;
ns -= (timeAfterCallbacks - timeBeforeCallbacks); // account for callback time
// TODO: Should we warn if the callback time is too long?
if (ns < 0) ns = 0;
@@ -2571,8 +2589,7 @@
if (at < limit) {
ALOGV("timestamp pause lag:%lld adjusting from %lld to %lld",
(long long)lag, (long long)at, (long long)limit);
- timestamp.mTime.tv_sec = limit / NANOS_PER_SECOND;
- timestamp.mTime.tv_nsec = limit % NANOS_PER_SECOND; // compiler opt.
+ timestamp.mTime = convertNsToTimespec(limit);
}
}
mPreviousLocation = location;
@@ -2615,18 +2632,18 @@
// the previous song under gapless playback.
// However, we sometimes see zero timestamps, then a glitch of
// the previous song's position, and then correct timestamps afterwards.
- if (mStartUs != 0 && mSampleRate != 0) {
+ if (mStartFromZeroUs != 0 && mSampleRate != 0) {
static const int kTimeJitterUs = 100000; // 100 ms
static const int k1SecUs = 1000000;
const int64_t timeNow = getNowUs();
- if (timeNow < mStartUs + k1SecUs) { // within first second of starting
+ if (timeNow < mStartFromZeroUs + k1SecUs) { // within first second of starting
const int64_t timestampTimeUs = convertTimespecToUs(timestamp.mTime);
- if (timestampTimeUs < mStartUs) {
+ if (timestampTimeUs < mStartFromZeroUs) {
return WOULD_BLOCK; // stale timestamp time, occurs before start.
}
- const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
+ const int64_t deltaTimeUs = timestampTimeUs - mStartFromZeroUs;
const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
/ ((double)mSampleRate * mPlaybackRate.mSpeed);
@@ -2649,10 +2666,10 @@
return WOULD_BLOCK;
}
if (deltaPositionByUs != 0) {
- mStartUs = 0; // don't check again, we got valid nonzero position.
+ mStartFromZeroUs = 0; // don't check again, we got valid nonzero position.
}
} else {
- mStartUs = 0; // don't check again, start time expired.
+ mStartFromZeroUs = 0; // don't check again, start time expired.
}
mTimestampStartupGlitchReported = false;
}
@@ -2690,14 +2707,33 @@
// Prevent retrograde motion in timestamp.
// This is sometimes caused by erratic reports of the available space in the ALSA drivers.
if (status == NO_ERROR) {
+ // previousTimestampValid is set to false when starting after a stop or flush.
if (previousTimestampValid) {
const int64_t previousTimeNanos =
audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime);
- const int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime);
+ int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime);
+
+ // Fix stale time when checking timestamp right after start().
+ //
+ // For offload compatibility, use a default lag value here.
+ // Any time discrepancy between this update and the pause timestamp is handled
+ // by the retrograde check afterwards.
+ const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
+ const int64_t limitNs = mStartNs - lagNs;
+ if (currentTimeNanos < limitNs) {
+ ALOGD("correcting timestamp time for pause, "
+ "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
+ (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
+ timestamp.mTime = convertNsToTimespec(limitNs);
+ currentTimeNanos = limitNs;
+ }
+
+ // retrograde check
if (currentTimeNanos < previousTimeNanos) {
ALOGW("retrograde timestamp time corrected, %lld < %lld",
(long long)currentTimeNanos, (long long)previousTimeNanos);
timestamp.mTime = mPreviousTimestamp.mTime;
+ // currentTimeNanos not used below.
}
// Looking at signed delta will work even when the timestamps
@@ -2907,7 +2943,7 @@
case STATE_STOPPED:
if (isOffloadedOrDirect_l()) {
// check if we have started in the past to return true.
- return mStartUs > 0;
+ return mStartFromZeroUs > 0;
}
// A normal audio track may still be draining, so
// check if stream has ended. This covers fasttrack position
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index b168fc9..7ae8ec5 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1085,8 +1085,10 @@
// reset by stop() but continues monotonically
// after new IAudioTrack to restore mPosition,
// and could be easily widened to uint64_t
- int64_t mStartUs; // the start time after flush or stop.
+ int64_t mStartFromZeroUs; // the start time after flush or stop,
+ // when position should be 0.
// only used for offloaded and direct tracks.
+ int64_t mStartNs; // the time when start() is called.
ExtendedTimestamp mStartEts; // Extended timestamp at start for normal
// AudioTracks.
AudioTimestamp mStartTs; // Timestamp at start for offloaded or direct
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 4f1fed5..10f62f1 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -35,6 +35,15 @@
// effect_handle_t interface implementation for bass boost
extern "C" const struct effect_interface_s gLvmEffectInterface;
+// Turn on VERY_VERY_VERBOSE_LOGGING to log parameter get and set for effects.
+
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while (false)
+#endif
+
#define LVM_ERROR_CHECK(LvmStatus, callingFunc, calledFunc){\
if ((LvmStatus) == LVM_NULLADDRESS){\
ALOGV("\tLVM_ERROR : Parameter error - "\
@@ -140,26 +149,43 @@
void LvmEffect_free (EffectContext *pContext);
int Effect_setConfig (EffectContext *pContext, effect_config_t *pConfig);
void Effect_getConfig (EffectContext *pContext, effect_config_t *pConfig);
-int BassBoost_setParameter (EffectContext *pContext, void *pParam, void *pValue);
+int BassBoost_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
int BassBoost_getParameter (EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue);
-int Virtualizer_setParameter (EffectContext *pContext, void *pParam, void *pValue);
-int Virtualizer_getParameter (EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue);
-int Equalizer_setParameter (EffectContext *pContext,
- void *pParam,
- uint32_t valueSize,
- void *pValue);
-int Equalizer_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
void *pParam,
uint32_t *pValueSize,
void *pValue);
-int Volume_setParameter (EffectContext *pContext, void *pParam, void *pValue);
+int Virtualizer_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
+int Virtualizer_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue);
+int Equalizer_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
+int Equalizer_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue);
+int Volume_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
int Volume_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
void *pParam,
uint32_t *pValueSize,
void *pValue);
@@ -2165,59 +2191,54 @@
//
//----------------------------------------------------------------------------
-int BassBoost_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int BassBoost_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tBassBoost_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
- if (*pValueSize != sizeof(uint32_t)){
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize1 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(uint32_t);
- break;
- case BASSBOOST_PARAM_STRENGTH:
- if (*pValueSize != sizeof(int16_t)){
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize2 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(int16_t);
- break;
-
- default:
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid param %d", param);
- return -EINVAL;
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
}
-
- switch (param){
+ switch (params[0]) {
case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
- *(uint32_t *)pValue = 1;
+ if (*pValueSize != sizeof(uint32_t)) { // legacy: check equality here.
+ ALOGV("%s BASSBOOST_PARAM_STRENGTH_SUPPORTED invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
- //ALOGV("\tBassBoost_getParameter() BASSBOOST_PARAM_STRENGTH_SUPPORTED Value is %d",
- // *(uint32_t *)pValue);
+ *(uint32_t *)pValue = 1;
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH_SUPPORTED %u", __func__, *(uint32_t *)pValue);
break;
case BASSBOOST_PARAM_STRENGTH:
- *(int16_t *)pValue = BassGetStrength(pContext);
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s BASSBOOST_PARAM_STRENGTH invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
- //ALOGV("\tBassBoost_getParameter() BASSBOOST_PARAM_STRENGTH Value is %d",
- // *(int16_t *)pValue);
+ *(int16_t *)pValue = BassGetStrength(pContext);
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH %d", __func__, *(int16_t *)pValue);
break;
default:
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //ALOGV("\tBassBoost_getParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end BassBoost_getParameter */
@@ -2236,27 +2257,42 @@
//
//----------------------------------------------------------------------------
-int BassBoost_setParameter (EffectContext *pContext, void *pParam, void *pValue){
+int BassBoost_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
int status = 0;
- int16_t strength;
- int32_t *pParamTemp = (int32_t *)pParam;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tBassBoost_setParameter start");
+ ALOGVV("%s start", __func__);
- switch (*pParamTemp){
- case BASSBOOST_PARAM_STRENGTH:
- strength = *(int16_t *)pValue;
- //ALOGV("\tBassBoost_setParameter() BASSBOOST_PARAM_STRENGTH value is %d", strength);
- //ALOGV("\tBassBoost_setParameter() Calling pBassBoost->BassSetStrength");
+ if (paramSize != sizeof(int32_t)) { // legacy: check equality here.
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case BASSBOOST_PARAM_STRENGTH: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s BASSBOOST_PARAM_STRENGTH invalid valueSize: %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+
+ const int16_t strength = *(int16_t *)pValue;
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH %d", __func__, strength);
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH Calling BassSetStrength", __func__);
BassSetStrength(pContext, (int32_t)strength);
- //ALOGV("\tBassBoost_setParameter() Called pBassBoost->BassSetStrength");
- break;
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH Called BassSetStrength", __func__);
+ } break;
+
default:
- ALOGV("\tLVM_ERROR : BassBoost_setParameter() invalid param %d", *pParamTemp);
+ ALOGV("%s invalid param %d", __func__, params[0]);
+ status = -EINVAL;
break;
}
- //ALOGV("\tBassBoost_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end BassBoost_setParameter */
@@ -2281,92 +2317,97 @@
//
//----------------------------------------------------------------------------
-int Virtualizer_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int Virtualizer_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVirtualizer_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
- if (*pValueSize != sizeof(uint32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(uint32_t);
- break;
- case VIRTUALIZER_PARAM_STRENGTH:
- if (*pValueSize != sizeof(int16_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize2 %d",*pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(int16_t);
- break;
- case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES:
- // return value size can only be interpreted as relative to input value,
- // deferring validity check to below
- break;
- case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
- if (*pValueSize != sizeof(uint32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(uint32_t);
- break;
- default:
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid param %d", param);
- return -EINVAL;
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
}
-
- switch (param){
+ switch (params[0]) {
case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
- *(uint32_t *)pValue = 1;
+ if (*pValueSize != sizeof(uint32_t)) { // legacy: check equality here.
+ ALOGV("%s VIRTUALIZER_PARAM_STRENGTH_SUPPORTED invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
- //ALOGV("\tVirtualizer_getParameter() VIRTUALIZER_PARAM_STRENGTH_SUPPORTED Value is %d",
- // *(uint32_t *)pValue);
+ *(uint32_t *)pValue = 1;
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH_SUPPORTED %d", __func__, *(uint32_t *)pValue);
break;
case VIRTUALIZER_PARAM_STRENGTH:
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VIRTUALIZER_PARAM_STRENGTH invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
*(int16_t *)pValue = VirtualizerGetStrength(pContext);
- //ALOGV("\tVirtualizer_getParameter() VIRTUALIZER_PARAM_STRENGTH Value is %d",
- // *(int16_t *)pValue);
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH %d", __func__, *(int16_t *)pValue);
break;
case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES: {
- const audio_channel_mask_t channelMask = (audio_channel_mask_t) *pParamTemp++;
- const audio_devices_t deviceType = (audio_devices_t) *pParamTemp;
- uint32_t nbChannels = audio_channel_count_from_out_mask(channelMask);
- if (*pValueSize < 3 * nbChannels * sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
- return -EINVAL;
+ if (paramSize < 3 * sizeof(int32_t)) {
+ ALOGV("%s VIRTUALIZER_PARAM_SPEAKER_ANGLES invalid paramSize: %u",
+ __func__, paramSize);
+ status = -EINVAL;
+ break;
}
+
+ const audio_channel_mask_t channelMask = (audio_channel_mask_t) params[1];
+ const audio_devices_t deviceType = (audio_devices_t) params[2];
+ const uint32_t nbChannels = audio_channel_count_from_out_mask(channelMask);
+ const uint32_t valueSizeRequired = 3 * nbChannels * sizeof(int32_t);
+ if (*pValueSize < valueSizeRequired) {
+ ALOGV("%s VIRTUALIZER_PARAM_SPEAKER_ANGLES invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = valueSizeRequired;
+
// verify the configuration is supported
status = VirtualizerIsConfigurationSupported(channelMask, deviceType);
if (status == 0) {
- ALOGV("VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES supports mask=0x%x device=0x%x",
- channelMask, deviceType);
+ ALOGV("%s VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES mask=0x%x device=0x%x",
+ __func__, channelMask, deviceType);
// configuration is supported, get the angles
VirtualizerGetSpeakerAngles(channelMask, deviceType, (int32_t *)pValue);
}
- }
- break;
+ } break;
case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
- *(uint32_t *)pValue = (uint32_t) VirtualizerGetVirtualizationMode(pContext);
+ if (*pValueSize != sizeof(uint32_t)) { // legacy: check equality here.
+ ALOGV("%s VIRTUALIZER_PARAM_VIRTUALIZATION_MODE invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
+ *(uint32_t *)pValue = (uint32_t) VirtualizerGetVirtualizationMode(pContext);
break;
default:
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- ALOGV("\tVirtualizer_getParameter end returning status=%d", status);
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Virtualizer_getParameter */
@@ -2385,37 +2426,57 @@
//
//----------------------------------------------------------------------------
-int Virtualizer_setParameter (EffectContext *pContext, void *pParam, void *pValue){
+int Virtualizer_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
int status = 0;
- int16_t strength;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVirtualizer_setParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case VIRTUALIZER_PARAM_STRENGTH:
- strength = *(int16_t *)pValue;
- //ALOGV("\tVirtualizer_setParameter() VIRTUALIZER_PARAM_STRENGTH value is %d", strength);
- //ALOGV("\tVirtualizer_setParameter() Calling pVirtualizer->setStrength");
+ if (paramSize != sizeof(int32_t)) { // legacy: check equality here.
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case VIRTUALIZER_PARAM_STRENGTH: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s VIRTUALIZER_PARAM_STRENGTH invalid valueSize: %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+
+ const int16_t strength = *(int16_t *)pValue;
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH %d", __func__, strength);
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH Calling VirtualizerSetStrength", __func__);
VirtualizerSetStrength(pContext, (int32_t)strength);
- //ALOGV("\tVirtualizer_setParameter() Called pVirtualizer->setStrength");
- break;
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH Called VirtualizerSetStrength", __func__);
+ } break;
case VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE: {
- const audio_devices_t deviceType = *(audio_devices_t *) pValue;
- status = VirtualizerForceVirtualizationMode(pContext, deviceType);
- //ALOGV("VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE device=0x%x result=%d",
- // deviceType, status);
+ if (valueSize < sizeof(int32_t)) {
+ ALOGV("%s VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE invalid valueSize: %u",
+ __func__, valueSize);
+ android_errorWriteLog(0x534e4554, "64478003");
+ status = -EINVAL;
+ break;
}
- break;
+
+ const audio_devices_t deviceType = (audio_devices_t)*(int32_t *)pValue;
+ status = VirtualizerForceVirtualizationMode(pContext, deviceType);
+ ALOGVV("%s VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE device=%#x result=%d",
+ __func__, deviceType, status);
+ } break;
default:
- ALOGV("\tLVM_ERROR : Virtualizer_setParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
+ status = -EINVAL;
break;
}
- //ALOGV("\tVirtualizer_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Virtualizer_setParameter */
@@ -2439,174 +2500,215 @@
// Side Effects:
//
//----------------------------------------------------------------------------
-int Equalizer_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int Equalizer_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
- int32_t param2;
- char *name;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tEqualizer_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param) {
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
case EQ_PARAM_NUM_BANDS:
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_NUM_BANDS invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ *(uint16_t *)pValue = (uint16_t)FIVEBAND_NUMBANDS;
+ ALOGVV("%s EQ_PARAM_NUM_BANDS %u", __func__, *(uint16_t *)pValue);
+ break;
+
case EQ_PARAM_CUR_PRESET:
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_CUR_PRESET invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ *(uint16_t *)pValue = (uint16_t)EqualizerGetPreset(pContext);
+ ALOGVV("%s EQ_PARAM_CUR_PRESET %u", __func__, *(uint16_t *)pValue);
+ break;
+
case EQ_PARAM_GET_NUM_OF_PRESETS:
- case EQ_PARAM_BAND_LEVEL:
- case EQ_PARAM_GET_BAND:
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_GET_NUM_OF_PRESETS invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ *(uint16_t *)pValue = (uint16_t)EqualizerGetNumPresets();
+ ALOGVV("%s EQ_PARAM_GET_NUM_OF_PRESETS %u", __func__, *(uint16_t *)pValue);
+ break;
+
+ case EQ_PARAM_GET_BAND: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_GET_BAND invalid paramSize: %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_GET_BAND invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ const int32_t frequency = params[1];
+ *(uint16_t *)pValue = (uint16_t)EqualizerGetBand(pContext, frequency);
+ ALOGVV("%s EQ_PARAM_GET_BAND frequency %d, band %u",
+ __func__, frequency, *(uint16_t *)pValue);
+ } break;
+
+ case EQ_PARAM_BAND_LEVEL: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid paramSize %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
if (*pValueSize < sizeof(int16_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 1 %d", *pValueSize);
- return -EINVAL;
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
*pValueSize = sizeof(int16_t);
- break;
+
+ const int32_t band = params[1];
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ if (band < 0) {
+ android_errorWriteLog(0x534e4554, "32438598");
+ ALOGW("%s EQ_PARAM_BAND_LEVEL invalid band %d", __func__, band);
+ }
+ status = -EINVAL;
+ break;
+ }
+ *(int16_t *)pValue = (int16_t)EqualizerGetBandLevel(pContext, band);
+ ALOGVV("%s EQ_PARAM_BAND_LEVEL band %d, level %d",
+ __func__, band, *(int16_t *)pValue);
+ } break;
case EQ_PARAM_LEVEL_RANGE:
if (*pValueSize < 2 * sizeof(int16_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 2 %d", *pValueSize);
- return -EINVAL;
+ ALOGV("%s EQ_PARAM_LEVEL_RANGE invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
*pValueSize = 2 * sizeof(int16_t);
- break;
- case EQ_PARAM_BAND_FREQ_RANGE:
- if (*pValueSize < 2 * sizeof(int32_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 3 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = 2 * sizeof(int32_t);
- break;
- case EQ_PARAM_CENTER_FREQ:
- if (*pValueSize < sizeof(int32_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 5 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(int32_t);
- break;
-
- case EQ_PARAM_GET_PRESET_NAME:
- break;
-
- case EQ_PARAM_PROPERTIES:
- if (*pValueSize < (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 1 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t);
- break;
-
- default:
- ALOGV("\tLVM_ERROR : Equalizer_getParameter unknown param %d", param);
- return -EINVAL;
- }
-
- switch (param) {
- case EQ_PARAM_NUM_BANDS:
- *(uint16_t *)pValue = (uint16_t)FIVEBAND_NUMBANDS;
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_NUM_BANDS %d", *(int16_t *)pValue);
- break;
-
- case EQ_PARAM_LEVEL_RANGE:
*(int16_t *)pValue = -1500;
*((int16_t *)pValue + 1) = 1500;
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_LEVEL_RANGE min %d, max %d",
- // *(int16_t *)pValue, *((int16_t *)pValue + 1));
+ ALOGVV("%s EQ_PARAM_LEVEL_RANGE min %d, max %d",
+ __func__, *(int16_t *)pValue, *((int16_t *)pValue + 1));
break;
- case EQ_PARAM_BAND_LEVEL:
- param2 = *pParamTemp;
- if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
+ case EQ_PARAM_BAND_FREQ_RANGE: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_FREQ_RANGE invalid paramSize: %u", __func__, paramSize);
status = -EINVAL;
- if (param2 < 0) {
- android_errorWriteLog(0x534e4554, "32438598");
- ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_LEVEL band %d", param2);
- }
break;
}
- *(int16_t *)pValue = (int16_t)EqualizerGetBandLevel(pContext, param2);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_BAND_LEVEL band %d, level %d",
- // param2, *(int32_t *)pValue);
- break;
-
- case EQ_PARAM_CENTER_FREQ:
- param2 = *pParamTemp;
- if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
+ if (*pValueSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_FREQ_RANGE invalid *pValueSize %u", __func__, *pValueSize);
status = -EINVAL;
- if (param2 < 0) {
- android_errorWriteLog(0x534e4554, "32436341");
- ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_CENTER_FREQ band %d", param2);
- }
break;
}
- *(int32_t *)pValue = EqualizerGetCentreFrequency(pContext, param2);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_CENTER_FREQ band %d, frequency %d",
- // param2, *(int32_t *)pValue);
- break;
+ *pValueSize = 2 * sizeof(int32_t);
- case EQ_PARAM_BAND_FREQ_RANGE:
- param2 = *pParamTemp;
- if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
- status = -EINVAL;
- if (param2 < 0) {
+ const int32_t band = params[1];
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ if (band < 0) {
android_errorWriteLog(0x534e4554, "32247948");
- ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d", param2);
+ ALOGW("%s EQ_PARAM_BAND_FREQ_RANGE invalid band %d",
+ __func__, band);
}
- break;
- }
- EqualizerGetBandFreqRange(pContext, param2, (uint32_t *)pValue, ((uint32_t *)pValue + 1));
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d, min %d, max %d",
- // param2, *(int32_t *)pValue, *((int32_t *)pValue + 1));
- break;
-
- case EQ_PARAM_GET_BAND:
- param2 = *pParamTemp;
- *(uint16_t *)pValue = (uint16_t)EqualizerGetBand(pContext, param2);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_GET_BAND frequency %d, band %d",
- // param2, *(uint16_t *)pValue);
- break;
-
- case EQ_PARAM_CUR_PRESET:
- *(uint16_t *)pValue = (uint16_t)EqualizerGetPreset(pContext);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_CUR_PRESET %d", *(int32_t *)pValue);
- break;
-
- case EQ_PARAM_GET_NUM_OF_PRESETS:
- *(uint16_t *)pValue = (uint16_t)EqualizerGetNumPresets();
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_GET_NUM_OF_PRESETS %d", *(int16_t *)pValue);
- break;
-
- case EQ_PARAM_GET_PRESET_NAME:
- param2 = *pParamTemp;
- if ((param2 < 0 && param2 != PRESET_CUSTOM) || param2 >= EqualizerGetNumPresets()) {
status = -EINVAL;
- if (param2 < 0) {
- android_errorWriteLog(0x534e4554, "32448258");
- ALOGE("\tERROR Equalizer_getParameter() EQ_PARAM_GET_PRESET_NAME preset %d",
- param2);
+ break;
+ }
+ EqualizerGetBandFreqRange(pContext, band, (uint32_t *)pValue, ((uint32_t *)pValue + 1));
+ ALOGVV("%s EQ_PARAM_BAND_FREQ_RANGE band %d, min %d, max %d",
+ __func__, band, *(int32_t *)pValue, *((int32_t *)pValue + 1));
+
+ } break;
+
+ case EQ_PARAM_CENTER_FREQ: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_CENTER_FREQ invalid paramSize: %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
+ if (*pValueSize < sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_CENTER_FREQ invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(int32_t);
+
+ const int32_t band = params[1];
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ status = -EINVAL;
+ if (band < 0) {
+ android_errorWriteLog(0x534e4554, "32436341");
+ ALOGW("%s EQ_PARAM_CENTER_FREQ invalid band %d", __func__, band);
}
break;
}
+ *(int32_t *)pValue = EqualizerGetCentreFrequency(pContext, band);
+ ALOGVV("%s EQ_PARAM_CENTER_FREQ band %d, frequency %d",
+ __func__, band, *(int32_t *)pValue);
+ } break;
+ case EQ_PARAM_GET_PRESET_NAME: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_PRESET_NAME invalid paramSize: %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
if (*pValueSize < 1) {
- status = -EINVAL;
android_errorWriteLog(0x534e4554, "37536407");
+ status = -EINVAL;
break;
}
- name = (char *)pValue;
- strncpy(name, EqualizerGetPresetName(param2), *pValueSize - 1);
+ const int32_t preset = params[1];
+ if ((preset < 0 && preset != PRESET_CUSTOM) || preset >= EqualizerGetNumPresets()) {
+ if (preset < 0) {
+ android_errorWriteLog(0x534e4554, "32448258");
+ ALOGE("%s EQ_PARAM_GET_PRESET_NAME preset %d", __func__, preset);
+ }
+ status = -EINVAL;
+ break;
+ }
+
+ char * const name = (char *)pValue;
+ strncpy(name, EqualizerGetPresetName(preset), *pValueSize - 1);
name[*pValueSize - 1] = 0;
*pValueSize = strlen(name) + 1;
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_GET_PRESET_NAME preset %d, name %s len %d",
- // param2, gEqualizerPresets[param2].name, *pValueSize);
- break;
+ ALOGVV("%s EQ_PARAM_GET_PRESET_NAME preset %d, name %s len %d",
+ __func__, preset, gEqualizerPresets[preset].name, *pValueSize);
+
+ } break;
case EQ_PARAM_PROPERTIES: {
+ constexpr uint32_t requiredValueSize = (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t);
+ if (*pValueSize < requiredValueSize) {
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = requiredValueSize;
+
int16_t *p = (int16_t *)pValue;
- ALOGV("\tEqualizer_getParameter() EQ_PARAM_PROPERTIES");
+ ALOGV("%s EQ_PARAM_PROPERTIES", __func__);
p[0] = (int16_t)EqualizerGetPreset(pContext);
p[1] = (int16_t)FIVEBAND_NUMBANDS;
for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
@@ -2615,12 +2717,12 @@
} break;
default:
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //GV("\tEqualizer_getParameter end\n");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Equalizer_getParameter */
@@ -2640,74 +2742,89 @@
// Outputs:
//
//----------------------------------------------------------------------------
-int Equalizer_setParameter (EffectContext *pContext,
- void *pParam,
- uint32_t valueSize,
- void *pValue) {
+int Equalizer_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
int status = 0;
- int32_t preset;
- int32_t band;
- int32_t level;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
+ ALOGVV("%s start", __func__);
- //ALOGV("\tEqualizer_setParameter start");
- switch (param) {
- case EQ_PARAM_CUR_PRESET:
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case EQ_PARAM_CUR_PRESET: {
if (valueSize < sizeof(int16_t)) {
- status = -EINVAL;
- break;
+ ALOGV("%s EQ_PARAM_CUR_PRESET invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
}
- preset = (int32_t)(*(uint16_t *)pValue);
+ const int32_t preset = (int32_t)*(uint16_t *)pValue;
- //ALOGV("\tEqualizer_setParameter() EQ_PARAM_CUR_PRESET %d", preset);
- if ((preset >= EqualizerGetNumPresets())||(preset < 0)) {
+ ALOGVV("%s EQ_PARAM_CUR_PRESET %d", __func__, preset);
+ if (preset >= EqualizerGetNumPresets() || preset < 0) {
+ ALOGV("%s EQ_PARAM_CUR_PRESET invalid preset %d", __func__, preset);
status = -EINVAL;
break;
}
EqualizerSetPreset(pContext, preset);
- break;
- case EQ_PARAM_BAND_LEVEL:
- if (valueSize < sizeof(int16_t)) {
- status = -EINVAL;
- break;
- }
- band = *pParamTemp;
- level = (int32_t)(*(int16_t *)pValue);
- //ALOGV("\tEqualizer_setParameter() EQ_PARAM_BAND_LEVEL band %d, level %d", band, level);
- if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ } break;
+
+ case EQ_PARAM_BAND_LEVEL: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid paramSize: %u", __func__, paramSize);
status = -EINVAL;
+ break;
+ }
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+ const int32_t band = params[1];
+ const int32_t level = (int32_t)*(int16_t *)pValue;
+ ALOGVV("%s EQ_PARAM_BAND_LEVEL band %d, level %d", __func__, band, level);
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
if (band < 0) {
android_errorWriteLog(0x534e4554, "32095626");
- ALOGE("\tERROR Equalizer_setParameter() EQ_PARAM_BAND_LEVEL band %d", band);
+ ALOGE("%s EQ_PARAM_BAND_LEVEL invalid band %d", __func__, band);
}
+ status = -EINVAL;
break;
}
EqualizerSetBandLevel(pContext, band, level);
- break;
+ } break;
+
case EQ_PARAM_PROPERTIES: {
- //ALOGV("\tEqualizer_setParameter() EQ_PARAM_PROPERTIES");
+ ALOGVV("%s EQ_PARAM_PROPERTIES", __func__);
if (valueSize < sizeof(int16_t)) {
- status = -EINVAL;
- break;
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
}
int16_t *p = (int16_t *)pValue;
if ((int)p[0] >= EqualizerGetNumPresets()) {
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid preset %d", __func__, (int)p[0]);
status = -EINVAL;
break;
}
if (p[0] >= 0) {
EqualizerSetPreset(pContext, (int)p[0]);
} else {
- if (valueSize < (2 + FIVEBAND_NUMBANDS) * sizeof(int16_t)) {
+ constexpr uint32_t valueSizeRequired = (2 + FIVEBAND_NUMBANDS) * sizeof(int16_t);
+ if (valueSize < valueSizeRequired) {
android_errorWriteLog(0x534e4554, "37563371");
- ALOGE("\tERROR Equalizer_setParameter() EQ_PARAM_PROPERTIES valueSize %d < %d",
- (int)valueSize, (int)((2 + FIVEBAND_NUMBANDS) * sizeof(int16_t)));
+ ALOGE("%s EQ_PARAM_PROPERTIES invalid valueSize %u < %u",
+ __func__, valueSize, valueSizeRequired);
status = -EINVAL;
break;
}
if ((int)p[1] != FIVEBAND_NUMBANDS) {
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid bands %d", __func__, (int)p[1]);
status = -EINVAL;
break;
}
@@ -2716,13 +2833,14 @@
}
}
} break;
+
default:
- ALOGV("\tLVM_ERROR : Equalizer_setParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //ALOGV("\tEqualizer_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Equalizer_setParameter */
@@ -2747,79 +2865,92 @@
//
//----------------------------------------------------------------------------
-int Volume_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int Volume_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVolume_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
case VOLUME_PARAM_LEVEL:
- case VOLUME_PARAM_MAXLEVEL:
- case VOLUME_PARAM_STEREOPOSITION:
- if (*pValueSize != sizeof(int16_t)){
- ALOGV("\tLVM_ERROR : Volume_getParameter() invalid pValueSize 1 %d", *pValueSize);
- return -EINVAL;
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VOLUME_PARAM_LEVEL invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
- *pValueSize = sizeof(int16_t);
+ // no need to set *pValueSize
+
+ status = VolumeGetVolumeLevel(pContext, (int16_t *)(pValue));
+ ALOGVV("%s VOLUME_PARAM_LEVEL %d", __func__, *(int16_t *)pValue);
+ break;
+
+ case VOLUME_PARAM_MAXLEVEL:
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VOLUME_PARAM_MAXLEVEL invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
+ // in millibel
+ *(int16_t *)pValue = 0;
+ ALOGVV("%s VOLUME_PARAM_MAXLEVEL %d", __func__, *(int16_t *)pValue);
+ break;
+
+ case VOLUME_PARAM_STEREOPOSITION:
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VOLUME_PARAM_STEREOPOSITION invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
+ VolumeGetStereoPosition(pContext, (int16_t *)pValue);
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION %d", __func__, *(int16_t *)pValue);
break;
case VOLUME_PARAM_MUTE:
+ if (*pValueSize < sizeof(uint32_t)) {
+ ALOGV("%s VOLUME_PARAM_MUTE invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint32_t);
+
+ status = VolumeGetMute(pContext, (uint32_t *)pValue);
+ ALOGV("%s VOLUME_PARAM_MUTE %u", __func__, *(uint32_t *)pValue);
+ break;
+
case VOLUME_PARAM_ENABLESTEREOPOSITION:
- if (*pValueSize < sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : Volume_getParameter() invalid pValueSize 2 %d", *pValueSize);
- return -EINVAL;
+ if (*pValueSize < sizeof(int32_t)) {
+ ALOGV("%s VOLUME_PARAM_ENABLESTEREOPOSITION invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
*pValueSize = sizeof(int32_t);
- break;
- default:
- ALOGV("\tLVM_ERROR : Volume_getParameter unknown param %d", param);
- return -EINVAL;
- }
-
- switch (param){
- case VOLUME_PARAM_LEVEL:
- status = VolumeGetVolumeLevel(pContext, (int16_t *)(pValue));
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_LEVEL Value is %d",
- // *(int16_t *)pValue);
- break;
-
- case VOLUME_PARAM_MAXLEVEL:
- *(int16_t *)pValue = 0;
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_MAXLEVEL Value is %d",
- // *(int16_t *)pValue);
- break;
-
- case VOLUME_PARAM_STEREOPOSITION:
- VolumeGetStereoPosition(pContext, (int16_t *)pValue);
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_STEREOPOSITION Value is %d",
- // *(int16_t *)pValue);
- break;
-
- case VOLUME_PARAM_MUTE:
- status = VolumeGetMute(pContext, (uint32_t *)pValue);
- ALOGV("\tVolume_getParameter() VOLUME_PARAM_MUTE Value is %d",
- *(uint32_t *)pValue);
- break;
-
- case VOLUME_PARAM_ENABLESTEREOPOSITION:
*(int32_t *)pValue = pContext->pBundledContext->bStereoPositionEnabled;
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_ENABLESTEREOPOSITION Value is %d",
- // *(uint32_t *)pValue);
+ ALOGVV("%s VOLUME_PARAM_ENABLESTEREOPOSITION %d", __func__, *(int32_t *)pValue);
+
break;
default:
- ALOGV("\tLVM_ERROR : Volume_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //ALOGV("\tVolume_getParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Volume_getParameter */
@@ -2839,55 +2970,87 @@
//
//----------------------------------------------------------------------------
-int Volume_setParameter (EffectContext *pContext, void *pParam, void *pValue){
- int status = 0;
- int16_t level;
- int16_t position;
- uint32_t mute;
- uint32_t positionEnabled;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+int Volume_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
+ int status = 0;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVolume_setParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case VOLUME_PARAM_LEVEL:
- level = *(int16_t *)pValue;
- //ALOGV("\tVolume_setParameter() VOLUME_PARAM_LEVEL value is %d", level);
- //ALOGV("\tVolume_setParameter() Calling pVolume->setVolumeLevel");
- status = VolumeSetVolumeLevel(pContext, (int16_t)level);
- //ALOGV("\tVolume_setParameter() Called pVolume->setVolumeLevel");
- break;
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case VOLUME_PARAM_LEVEL: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s VOLUME_PARAM_LEVEL invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
- case VOLUME_PARAM_MUTE:
- mute = *(uint32_t *)pValue;
- //ALOGV("\tVolume_setParameter() Calling pVolume->setMute, mute is %d", mute);
- //ALOGV("\tVolume_setParameter() Calling pVolume->setMute");
+ const int16_t level = *(int16_t *)pValue;
+ ALOGVV("%s VOLUME_PARAM_LEVEL %d", __func__, level);
+ ALOGVV("%s VOLUME_PARAM_LEVEL Calling VolumeSetVolumeLevel", __func__);
+ status = VolumeSetVolumeLevel(pContext, level);
+ ALOGVV("%s VOLUME_PARAM_LEVEL Called VolumeSetVolumeLevel", __func__);
+ } break;
+
+ case VOLUME_PARAM_MUTE: {
+ if (valueSize < sizeof(uint32_t)) {
+ ALOGV("%s VOLUME_PARAM_MUTE invalid valueSize %u", __func__, valueSize);
+ android_errorWriteLog(0x534e4554, "64477217");
+ status = -EINVAL;
+ break;
+ }
+
+ const uint32_t mute = *(uint32_t *)pValue;
+ ALOGVV("%s VOLUME_PARAM_MUTE %d", __func__, mute);
+ ALOGVV("%s VOLUME_PARAM_MUTE Calling VolumeSetMute", __func__);
status = VolumeSetMute(pContext, mute);
- //ALOGV("\tVolume_setParameter() Called pVolume->setMute");
- break;
+ ALOGVV("%s VOLUME_PARAM_MUTE Called VolumeSetMute", __func__);
+ } break;
- case VOLUME_PARAM_ENABLESTEREOPOSITION:
- positionEnabled = *(uint32_t *)pValue;
- (void) VolumeEnableStereoPosition(pContext, positionEnabled);
- (void) VolumeSetStereoPosition(pContext, pContext->pBundledContext->positionSaved);
- //ALOGV("\tVolume_setParameter() VOLUME_PARAM_ENABLESTEREOPOSITION called");
- break;
+ case VOLUME_PARAM_ENABLESTEREOPOSITION: {
+ if (valueSize < sizeof(uint32_t)) {
+ ALOGV("%s VOLUME_PARAM_ENABLESTEREOPOSITION invalid valueSize %u",
+ __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
- case VOLUME_PARAM_STEREOPOSITION:
- position = *(int16_t *)pValue;
- //ALOGV("\tVolume_setParameter() VOLUME_PARAM_STEREOPOSITION value is %d", position);
- //ALOGV("\tVolume_setParameter() Calling pVolume->VolumeSetStereoPosition");
- status = VolumeSetStereoPosition(pContext, (int16_t)position);
- //ALOGV("\tVolume_setParameter() Called pVolume->VolumeSetStereoPosition");
- break;
+ const uint32_t positionEnabled = *(uint32_t *)pValue;
+ status = VolumeEnableStereoPosition(pContext, positionEnabled)
+ ?: VolumeSetStereoPosition(pContext, pContext->pBundledContext->positionSaved);
+ ALOGVV("%s VOLUME_PARAM_ENABLESTEREOPOSITION called", __func__);
+ } break;
+
+ case VOLUME_PARAM_STEREOPOSITION: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s VOLUME_PARAM_STEREOPOSITION invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+
+ const int16_t position = *(int16_t *)pValue;
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION %d", __func__, position);
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION Calling VolumeSetStereoPosition",
+ __func__);
+ status = VolumeSetStereoPosition(pContext, position);
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION Called VolumeSetStereoPosition",
+ __func__);
+ } break;
default:
- ALOGV("\tLVM_ERROR : Volume_setParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
+ status = -EINVAL;
break;
}
- //ALOGV("\tVolume_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Volume_setParameter */
@@ -3205,6 +3368,13 @@
return status;
} /* end Effect_process */
+// The value offset of an effect parameter is computed by rounding up
+// the parameter size to the next 32 bit alignment.
+static inline uint32_t computeParamVOffset(const effect_param_t *p) {
+ return ((p->psize + sizeof(int32_t) - 1) / sizeof(int32_t)) *
+ sizeof(int32_t);
+}
+
/* Effect Control Interface Implementation: Command */
int Effect_command(effect_handle_t self,
uint32_t cmdCode,
@@ -3315,8 +3485,7 @@
ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: psize too big");
return -EINVAL;
}
- uint32_t paddedParamSize = ((p->psize + sizeof(int32_t) - 1) / sizeof(int32_t)) *
- sizeof(int32_t);
+ const uint32_t paddedParamSize = computeParamVOffset(p);
if ((EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t) < paddedParamSize) ||
(EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t) - paddedParamSize <
p->vsize)) {
@@ -3338,6 +3507,7 @@
uint32_t voffset = paddedParamSize;
if(pContext->EffectType == LVM_BASS_BOOST){
p->status = android::BassBoost_getParameter(pContext,
+ p->psize,
p->data,
&p->vsize,
p->data + voffset);
@@ -3350,6 +3520,7 @@
if(pContext->EffectType == LVM_VIRTUALIZER){
p->status = android::Virtualizer_getParameter(pContext,
+ p->psize,
(void *)p->data,
&p->vsize,
p->data + voffset);
@@ -3364,6 +3535,7 @@
//ALOGV("\tEqualizer_command cmdCode Case: "
// "EFFECT_CMD_GET_PARAM start");
p->status = android::Equalizer_getParameter(pContext,
+ p->psize,
p->data,
&p->vsize,
p->data + voffset);
@@ -3378,6 +3550,7 @@
if(pContext->EffectType == LVM_VOLUME){
//ALOGV("\tVolume_command cmdCode Case: EFFECT_CMD_GET_PARAM start");
p->status = android::Volume_getParameter(pContext,
+ p->psize,
(void *)p->data,
&p->vsize,
p->data + voffset);
@@ -3407,13 +3580,9 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
- if (p->psize != sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : BassBoost_command cmdCode Case: "
- "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)");
- return -EINVAL;
- }
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
//ALOGV("\tnBassBoost_command cmdSize is %d\n"
// "\tsizeof(effect_param_t) is %d\n"
@@ -3423,8 +3592,10 @@
// cmdSize, sizeof(effect_param_t), p->psize, p->vsize );
*(int *)pReplyData = android::BassBoost_setParameter(pContext,
- (void *)p->data,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
if(pContext->EffectType == LVM_VIRTUALIZER){
// Warning this log will fail to properly read an int32_t value, assumes int16_t
@@ -3442,13 +3613,9 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
- if (p->psize != sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_command cmdCode Case: "
- "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)");
- return -EINVAL;
- }
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
//ALOGV("\tnVirtualizer_command cmdSize is %d\n"
// "\tsizeof(effect_param_t) is %d\n"
@@ -3458,8 +3625,10 @@
// cmdSize, sizeof(effect_param_t), p->psize, p->vsize );
*(int *)pReplyData = android::Virtualizer_setParameter(pContext,
- (void *)p->data,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
if(pContext->EffectType == LVM_EQUALIZER){
//ALOGV("\tEqualizer_command cmdCode Case: "
@@ -3475,12 +3644,15 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
+
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
*(int *)pReplyData = android::Equalizer_setParameter(pContext,
- (void *)p->data,
- p->vsize,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
if(pContext->EffectType == LVM_VOLUME){
//ALOGV("\tVolume_command cmdCode Case: EFFECT_CMD_SET_PARAM start");
@@ -3497,11 +3669,15 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
+
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
*(int *)pReplyData = android::Volume_setParameter(pContext,
- (void *)p->data,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
//ALOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_PARAM end");
} break;
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1717b49..ee9406d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -429,9 +429,7 @@
size_t ii;
LVM_INT32 temp;
- src += n-1;
- dst += n-1;
- for (ii = n; ii != 0; ii--) {
+ for (ii = 0; ii < n; ii++) {
temp = (LVM_INT32)((*src) * 32768.0f);
if (temp >= 32767) {
*dst = 32767;
@@ -440,8 +438,8 @@
} else {
*dst = (LVM_INT16)temp;
}
- src--;
- dst--;
+ src++;
+ dst++;
}
return;
}
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 115baff..4b131a7 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -401,6 +401,10 @@
videoFrame->getFlattenedIccData());
}
mFrameDecoded = true;
+
+ // Aggressive clear to avoid holding on to resources
+ mRetriever.clear();
+ mDataSource.clear();
return true;
}
diff --git a/media/libmedia/IMediaCodecService.cpp b/media/libmedia/IMediaCodecService.cpp
index 2d62419..adfa93d 100644
--- a/media/libmedia/IMediaCodecService.cpp
+++ b/media/libmedia/IMediaCodecService.cpp
@@ -27,7 +27,8 @@
namespace android {
enum {
- GET_OMX = IBinder::FIRST_CALL_TRANSACTION
+ GET_OMX = IBinder::FIRST_CALL_TRANSACTION,
+ GET_OMX_STORE
};
class BpMediaCodecService : public BpInterface<IMediaCodecService>
@@ -45,6 +46,13 @@
return interface_cast<IOMX>(reply.readStrongBinder());
}
+ virtual sp<IOMXStore> getOMXStore() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaCodecService::getInterfaceDescriptor());
+ remote()->transact(GET_OMX_STORE, data, &reply);
+ return interface_cast<IOMXStore>(reply.readStrongBinder());
+ }
+
};
IMPLEMENT_META_INTERFACE(MediaCodecService, "android.media.IMediaCodecService");
@@ -62,6 +70,12 @@
reply->writeStrongBinder(IInterface::asBinder(omx));
return NO_ERROR;
}
+ case GET_OMX_STORE: {
+ CHECK_INTERFACE(IMediaCodecService, data, reply);
+ sp<IOMXStore> omxStore = getOMXStore();
+ reply->writeStrongBinder(IInterface::asBinder(omxStore));
+ return NO_ERROR;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index 19b00f3..3c43a72 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -38,7 +38,8 @@
SETMEDIACAS,
SETUID,
NAME,
- GETMETRICS
+ GETMETRICS,
+ RELEASE,
};
class BpMediaExtractor : public BpInterface<IMediaExtractor> {
@@ -138,6 +139,13 @@
ALOGV("name NOT IMPLEMENTED");
return NULL;
}
+
+ virtual void release() {
+ ALOGV("release");
+ Parcel data, reply;
+ data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
+ remote()->transact(RELEASE, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(MediaExtractor, "android.media.IMediaExtractor");
@@ -215,6 +223,12 @@
reply->writeInt32(setMediaCas(casToken));
return OK;
}
+ case RELEASE: {
+ ALOGV("release");
+ CHECK_INTERFACE(IMediaExtractor, data, reply);
+ release();
+ return OK;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 2a74512..a570ffe 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -101,42 +101,46 @@
return OK;
}
-void MediaCodecInfo::CapabilitiesBuilder::addProfileLevel(uint32_t profile, uint32_t level) {
+void MediaCodecInfo::CapabilitiesWriter::addDetail(
+ const char* key, const char* value) {
+ mCap->mDetails->setString(key, value);
+}
+
+void MediaCodecInfo::CapabilitiesWriter::addDetail(
+ const char* key, int32_t value) {
+ mCap->mDetails->setInt32(key, value);
+}
+
+void MediaCodecInfo::CapabilitiesWriter::addProfileLevel(
+ uint32_t profile, uint32_t level) {
ProfileLevel profileLevel;
profileLevel.mProfile = profile;
profileLevel.mLevel = level;
- if (mProfileLevelsSorted.indexOf(profileLevel) < 0) {
- mProfileLevels.push_back(profileLevel);
- mProfileLevelsSorted.add(profileLevel);
+ if (mCap->mProfileLevelsSorted.indexOf(profileLevel) < 0) {
+ mCap->mProfileLevels.push_back(profileLevel);
+ mCap->mProfileLevelsSorted.add(profileLevel);
}
}
-void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
- if (mColorFormatsSorted.indexOf(format) < 0) {
- mColorFormats.push(format);
- mColorFormatsSorted.add(format);
+void MediaCodecInfo::CapabilitiesWriter::addColorFormat(uint32_t format) {
+ if (mCap->mColorFormatsSorted.indexOf(format) < 0) {
+ mCap->mColorFormats.push(format);
+ mCap->mColorFormatsSorted.add(format);
}
}
-void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
- mFlags |= flags;
+void MediaCodecInfo::CapabilitiesWriter::addFlags(uint32_t flags) {
+ mCap->mFlags |= flags;
+}
+
+MediaCodecInfo::CapabilitiesWriter::CapabilitiesWriter(
+ MediaCodecInfo::Capabilities* cap) : mCap(cap) {
}
bool MediaCodecInfo::isEncoder() const {
return mIsEncoder;
}
-bool MediaCodecInfo::hasQuirk(const char *name) const {
- if (name) {
- for (size_t ix = 0; ix < mQuirks.size(); ix++) {
- if (mQuirks.itemAt(ix).equalsIgnoreCase(name)) {
- return true;
- }
- }
- }
- return false;
-}
-
void MediaCodecInfo::getSupportedMimes(Vector<AString> *mimes) const {
mimes->clear();
for (size_t ix = 0; ix < mCaps.size(); ix++) {
@@ -157,20 +161,21 @@
return mName.c_str();
}
+const char *MediaCodecInfo::getOwnerName() const {
+ return mOwner.c_str();
+}
+
// static
sp<MediaCodecInfo> MediaCodecInfo::FromParcel(const Parcel &parcel) {
AString name = AString::FromParcel(parcel);
+ AString owner = AString::FromParcel(parcel);
bool isEncoder = static_cast<bool>(parcel.readInt32());
- sp<MediaCodecInfo> info = new MediaCodecInfo(name, isEncoder, NULL);
+ sp<MediaCodecInfo> info = new MediaCodecInfo;
+ info->mName = name;
+ info->mOwner = owner;
+ info->mIsEncoder = isEncoder;
size_t size = static_cast<size_t>(parcel.readInt32());
for (size_t i = 0; i < size; i++) {
- AString quirk = AString::FromParcel(parcel);
- if (info != NULL) {
- info->mQuirks.push_back(quirk);
- }
- }
- size = static_cast<size_t>(parcel.readInt32());
- for (size_t i = 0; i < size; i++) {
AString mime = AString::FromParcel(parcel);
sp<Capabilities> caps = Capabilities::FromParcel(parcel);
if (caps == NULL)
@@ -184,11 +189,8 @@
status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const {
mName.writeToParcel(parcel);
+ mOwner.writeToParcel(parcel);
parcel->writeInt32(mIsEncoder);
- parcel->writeInt32(mQuirks.size());
- for (size_t i = 0; i < mQuirks.size(); i++) {
- mQuirks.itemAt(i).writeToParcel(parcel);
- }
parcel->writeInt32(mCaps.size());
for (size_t i = 0; i < mCaps.size(); i++) {
mCaps.keyAt(i).writeToParcel(parcel);
@@ -208,86 +210,46 @@
return -1;
}
-MediaCodecInfo::MediaCodecInfo(AString name, bool encoder, const char *mime)
- : mName(name),
- mIsEncoder(encoder),
- mHasSoleMime(false) {
- if (mime != NULL) {
- addMime(mime);
- mHasSoleMime = true;
- }
+MediaCodecInfo::MediaCodecInfo() {
}
-status_t MediaCodecInfo::addMime(const char *mime) {
- if (mHasSoleMime) {
- ALOGE("Codec '%s' already had its type specified", mName.c_str());
- return -EINVAL;
- }
- ssize_t ix = getCapabilityIndex(mime);
+void MediaCodecInfoWriter::setName(const char* name) {
+ mInfo->mName = name;
+}
+
+void MediaCodecInfoWriter::setOwner(const char* owner) {
+ mInfo->mOwner = owner;
+}
+
+void MediaCodecInfoWriter::setEncoder(bool isEncoder) {
+ mInfo->mIsEncoder = isEncoder;
+}
+
+std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>
+ MediaCodecInfoWriter::addMime(const char *mime) {
+ ssize_t ix = mInfo->getCapabilityIndex(mime);
if (ix >= 0) {
- mCurrentCaps = mCaps.valueAt(ix);
- } else {
- mCurrentCaps = new Capabilities();
- mCaps.add(AString(mime), mCurrentCaps);
+ return std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>(
+ new MediaCodecInfo::CapabilitiesWriter(
+ mInfo->mCaps.valueAt(ix).get()));
}
- return OK;
+ sp<MediaCodecInfo::Capabilities> caps = new MediaCodecInfo::Capabilities();
+ mInfo->mCaps.add(AString(mime), caps);
+ return std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>(
+ new MediaCodecInfo::CapabilitiesWriter(caps.get()));
}
-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);
+bool MediaCodecInfoWriter::removeMime(const char *mime) {
+ ssize_t ix = mInfo->getCapabilityIndex(mime);
if (ix >= 0) {
- mCaps.removeItemsAt(ix);
- // mCurrentCaps will be removed when completed
+ mInfo->mCaps.removeItemsAt(ix);
+ return true;
}
+ return false;
}
-status_t MediaCodecInfo::initializeCapabilities(const sp<Capabilities> &caps) {
- // TRICKY: copy data to mCurrentCaps as it is a reference to
- // an element of the capabilites map.
- mCurrentCaps->mColorFormats.clear();
- mCurrentCaps->mColorFormats.appendVector(caps->mColorFormats);
- mCurrentCaps->mProfileLevels.clear();
- mCurrentCaps->mProfileLevels.appendVector(caps->mProfileLevels);
- mCurrentCaps->mFlags = caps->mFlags;
- mCurrentCaps->mDetails = caps->mDetails;
- return OK;
-}
-
-void MediaCodecInfo::addQuirk(const char *name) {
- if (!hasQuirk(name)) {
- mQuirks.push(name);
- }
-}
-
-void MediaCodecInfo::complete() {
- mCurrentCaps = NULL;
-}
-
-void MediaCodecInfo::addDetail(const AString &key, const AString &value) {
- mCurrentCaps->mDetails->setString(key.c_str(), value.c_str());
-}
-
-void MediaCodecInfo::addFeature(const AString &key, int32_t value) {
- AString tag = "feature-";
- tag.append(key);
- mCurrentCaps->mDetails->setInt32(tag.c_str(), value);
-}
-
-void MediaCodecInfo::addFeature(const AString &key, const char *value) {
- AString tag = "feature-";
- tag.append(key);
- mCurrentCaps->mDetails->setString(tag.c_str(), value);
+MediaCodecInfoWriter::MediaCodecInfoWriter(MediaCodecInfo* info) :
+ mInfo(info) {
}
} // namespace android
diff --git a/media/libmedia/include/media/IMediaCodecService.h b/media/libmedia/include/media/IMediaCodecService.h
index da3c5a03..59fb1c0 100644
--- a/media/libmedia/include/media/IMediaCodecService.h
+++ b/media/libmedia/include/media/IMediaCodecService.h
@@ -22,6 +22,7 @@
#include <binder/Parcel.h>
#include <media/IDataSource.h>
#include <media/IOMX.h>
+#include <media/IOMXStore.h>
namespace android {
@@ -31,6 +32,7 @@
DECLARE_META_INTERFACE(MediaCodecService);
virtual sp<IOMX> getOMX() = 0;
+ virtual sp<IOMXStore> getOMXStore() = 0;
};
class BnMediaCodecService: public BnInterface<IMediaCodecService>
diff --git a/media/libmedia/include/media/IMediaExtractor.h b/media/libmedia/include/media/IMediaExtractor.h
index 1e13b65..0ac7673 100644
--- a/media/libmedia/include/media/IMediaExtractor.h
+++ b/media/libmedia/include/media/IMediaExtractor.h
@@ -68,6 +68,8 @@
virtual void setUID(uid_t uid) = 0;
virtual const char * name() = 0;
+
+ virtual void release() = 0;
};
diff --git a/media/libmedia/include/media/IOMXStore.h b/media/libmedia/include/media/IOMXStore.h
index f739c3b..628db70 100644
--- a/media/libmedia/include/media/IOMXStore.h
+++ b/media/libmedia/include/media/IOMXStore.h
@@ -27,11 +27,6 @@
#include <vector>
#include <string>
-/*
-#include <OMX_Core.h>
-#include <OMX_Video.h>
-*/
-
namespace android {
using hardware::media::omx::V1_0::IOmxStore;
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index ef641d2..ab2cd24 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -18,6 +18,7 @@
#define MEDIA_CODEC_INFO_H_
+#include <android-base/macros.h>
#include <binder/Parcel.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
@@ -36,6 +37,9 @@
typedef KeyedVector<AString, AString> CodecSettings;
+struct MediaCodecInfoWriter;
+struct MediaCodecListWriter;
+
struct MediaCodecInfo : public RefBase {
struct ProfileLevel {
uint32_t mProfile;
@@ -45,6 +49,8 @@
}
};
+ struct CapabilitiesWriter;
+
struct Capabilities : public RefBase {
enum {
// decoder flags
@@ -77,72 +83,191 @@
static sp<Capabilities> FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
- DISALLOW_EVIL_CONSTRUCTORS(Capabilities);
+ DISALLOW_COPY_AND_ASSIGN(Capabilities);
friend struct MediaCodecInfo;
+ friend struct MediaCodecInfoWriter;
+ friend struct CapabilitiesWriter;
};
- // Use a subclass to allow setting fields on construction without allowing
- // to do the same throughout the framework.
- struct CapabilitiesBuilder : public Capabilities {
+ /**
+ * This class is used for modifying information inside a `Capabilities`
+ * object. An object of type `CapabilitiesWriter` can be obtained by calling
+ * `MediaCodecInfoWriter::addMime()` or
+ * `MediaCodecInfoWriter::updateMime()`.
+ */
+ struct CapabilitiesWriter {
+ /**
+ * Add a key-value pair to the list of details. If the key already
+ * exists, the old value will be replaced.
+ *
+ * A pair added by this function will be accessible by
+ * `Capabilities::getDetails()`. Call `AMessage::getString()` with the
+ * same key to retrieve the value.
+ *
+ * @param key The key.
+ * @param value The string value.
+ */
+ void addDetail(const char* key, const char* value);
+ /**
+ * Add a key-value pair to the list of details. If the key already
+ * exists, the old value will be replaced.
+ *
+ * A pair added by this function will be accessible by
+ * `Capabilities::getDetails()`. Call `AMessage::getInt32()` with the
+ * same key to retrieve the value.
+ *
+ * @param key The key.
+ * @param value The `int32_t` value.
+ */
+ void addDetail(const char* key, int32_t value);
+ /**
+ * Add a profile-level pair. If this profile-level pair already exists,
+ * it will be ignored.
+ *
+ * @param profile The "profile" component.
+ * @param level The "level" component.
+ */
void addProfileLevel(uint32_t profile, uint32_t level);
+ /**
+ * Add a color format. If this color format already exists, it will be
+ * ignored.
+ *
+ * @param format The color format.
+ */
void addColorFormat(uint32_t format);
+ /**
+ * Add flags. The underlying operation is bitwise-or. In other words,
+ * bits that have already been set will be ignored.
+ *
+ * @param flags The additional flags.
+ */
void addFlags(uint32_t flags);
+ private:
+ /**
+ * The associated `Capabilities` object.
+ */
+ Capabilities* mCap;
+ /**
+ * Construct a writer for the given `Capabilities` object.
+ *
+ * @param cap The `Capabilities` object to be written to.
+ */
+ CapabilitiesWriter(Capabilities* cap);
+
+ friend MediaCodecInfoWriter;
};
bool isEncoder() const;
- bool hasQuirk(const char *name) const;
void getSupportedMimes(Vector<AString> *mimes) const;
const sp<Capabilities> getCapabilitiesFor(const char *mime) const;
const char *getCodecName() const;
/**
+ * Return the name of the service that hosts the codec. This value is not
+ * visible at the Java level.
+ *
+ * Currently, this is the "instance name" of the IOmx service.
+ */
+ const char *getOwnerName() const;
+
+ /**
* Serialization over Binder
*/
static sp<MediaCodecInfo> FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
private:
- // variable set only in constructor - these are accessed by MediaCodecList
- // to avoid duplication of same variables
AString mName;
+ AString mOwner;
bool mIsEncoder;
- bool mHasSoleMime; // was initialized with mime
-
- Vector<AString> mQuirks;
KeyedVector<AString, sp<Capabilities> > mCaps;
- sp<Capabilities> mCurrentCaps; // currently initalized capabilities
-
ssize_t getCapabilityIndex(const char *mime) const;
- /* Methods used by MediaCodecList to construct the info
- * object from XML.
- *
- * After info object is created:
- * - additional quirks can be added
- * - additional mimes can be added
- * - OMX codec capabilities can be set for the current mime-type
- * - a capability detail can be set for the current mime-type
- * - a feature can be set for the current mime-type
- * - info object can be completed when parsing of a mime-type is done
+ /**
+ * Construct an `MediaCodecInfo` object. After the construction, its
+ * information can be set via an `MediaCodecInfoWriter` object obtained from
+ * `MediaCodecListWriter::addMediaCodecInfo()`.
*/
- 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);
+ MediaCodecInfo();
- status_t initializeCapabilities(const sp<Capabilities> &caps);
- void addDetail(const AString &key, const AString &value);
- void addFeature(const AString &key, int32_t value);
- void addFeature(const AString &key, const char *value);
- void removeMime(const char *mime);
- void complete();
+ DISALLOW_COPY_AND_ASSIGN(MediaCodecInfo);
- DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
-
- friend struct MediaCodecList;
friend class MediaCodecListOverridesTest;
+ friend struct MediaCodecInfoWriter;
+ friend struct MediaCodecListWriter;
+};
+
+/**
+ * This class is to be used by a `MediaCodecListBuilderBase` instance to
+ * populate information inside the associated `MediaCodecInfo` object.
+ *
+ * The only place where an instance of `MediaCodecInfoWriter` can be constructed
+ * is `MediaCodecListWriter::addMediaCodecInfo()`. A `MediaCodecListBuilderBase`
+ * instance should call `MediaCodecListWriter::addMediaCodecInfo()` on the given
+ * `MediaCodecListWriter` object given as an input to
+ * `MediaCodecListBuilderBase::buildMediaCodecList()`.
+ */
+struct MediaCodecInfoWriter {
+ /**
+ * Set the name of the codec.
+ *
+ * @param name The new name.
+ */
+ void setName(const char* name);
+ /**
+ * Set the owner name of the codec.
+ *
+ * This "owner name" is the name of the `IOmx` instance that supports this
+ * codec.
+ *
+ * @param owner The new owner name.
+ */
+ void setOwner(const char* owner);
+ /**
+ * Set whether this codec is an encoder or a decoder.
+ *
+ * @param isEncoder Whether this codec is an encoder or a decoder.
+ */
+ void setEncoder(bool isEncoder = true);
+ /**
+ * Add a mime to an indexed list and return a `CapabilitiesWriter` object
+ * that can be used for modifying the associated `Capabilities`.
+ *
+ * If the mime already exists, this function will return the
+ * `CapabilitiesWriter` associated with the mime.
+ *
+ * @param[in] mime The name of a new mime to add.
+ * @return writer The `CapabilitiesWriter` object for modifying the
+ * `Capabilities` associated with the mime. `writer` will be valid
+ * regardless of whether `mime` already exists or not.
+ */
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> addMime(
+ const char* mime);
+ /**
+ * Remove a mime.
+ *
+ * @param mime The name of the mime to remove.
+ * @return `true` if `mime` is removed; `false` if `mime` is not found.
+ */
+ bool removeMime(const char* mime);
+private:
+ /**
+ * The associated `MediaCodecInfo`.
+ */
+ MediaCodecInfo* mInfo;
+ /**
+ * Construct the `MediaCodecInfoWriter` object associated with the given
+ * `MediaCodecInfo` object.
+ *
+ * @param info The underlying `MediaCodecInfo` object.
+ */
+ MediaCodecInfoWriter(MediaCodecInfo* info);
+
+ DISALLOW_COPY_AND_ASSIGN(MediaCodecInfoWriter);
+
+ friend struct MediaCodecListWriter;
};
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 483a9ff..bd866cb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1152,7 +1152,18 @@
return writtenAudioDurationUs - (mediaUs - mAudioFirstAnchorTimeMediaUs);
}
}
- return writtenAudioDurationUs - mAudioSink->getPlayedOutDurationUs(nowUs);
+
+ const int64_t audioSinkPlayedUs = mAudioSink->getPlayedOutDurationUs(nowUs);
+ int64_t pendingUs = writtenAudioDurationUs - audioSinkPlayedUs;
+ if (pendingUs < 0) {
+ // This shouldn't happen unless the timestamp is stale.
+ ALOGW("%s: pendingUs %lld < 0, clamping to zero, potential resume after pause "
+ "writtenAudioDurationUs: %lld, audioSinkPlayedUs: %lld",
+ __func__, (long long)pendingUs,
+ (long long)writtenAudioDurationUs, (long long)audioSinkPlayedUs);
+ pendingUs = 0;
+ }
+ return pendingUs;
}
int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ec48561..c9394bb 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -62,9 +62,6 @@
#include "include/SharedMemoryBuffer.h"
#include <media/stagefright/omx/OMXUtils.h>
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <android/hidl/memory/1.0/IMemory.h>
-
namespace android {
using binder::Status;
@@ -6344,19 +6341,10 @@
CHECK(mCodec->mOMXNode == NULL);
- OMXClient client;
- bool trebleFlag;
- if (client.connect(&trebleFlag) != OK) {
- mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
- return false;
- }
- mCodec->setTrebleFlag(trebleFlag);
-
- sp<IOMX> omx = client.interface();
-
sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec);
Vector<AString> matchingCodecs;
+ Vector<AString> owners;
AString mime;
@@ -6364,9 +6352,28 @@
int32_t encoder = false;
if (msg->findString("componentName", &componentName)) {
sp<IMediaCodecList> list = MediaCodecList::getInstance();
- if (list != NULL && list->findCodecByName(componentName.c_str()) >= 0) {
- matchingCodecs.add(componentName);
+ if (list == nullptr) {
+ ALOGE("Unable to obtain MediaCodecList while "
+ "attempting to create codec \"%s\"",
+ componentName.c_str());
+ return false;
}
+ ssize_t index = list->findCodecByName(componentName.c_str());
+ if (index < 0) {
+ ALOGE("Unable to find codec \"%s\"",
+ componentName.c_str());
+ return false;
+ }
+ sp<MediaCodecInfo> info = list->getCodecInfo(index);
+ if (info == nullptr) {
+ ALOGE("Unexpected error (index out-of-bound) while "
+ "retrieving information for codec \"%s\"",
+ componentName.c_str());
+ return false;
+ }
+ matchingCodecs.add(info->getCodecName());
+ owners.add(info->getOwnerName() == nullptr ?
+ "default" : info->getOwnerName());
} else {
CHECK(msg->findString("mime", &mime));
@@ -6378,10 +6385,12 @@
mime.c_str(),
encoder, // createEncoder
0, // flags
- &matchingCodecs);
+ &matchingCodecs,
+ &owners);
}
sp<CodecObserver> observer = new CodecObserver;
+ sp<IOMX> omx;
sp<IOMXNode> omxNode;
status_t err = NAME_NOT_FOUND;
@@ -6389,6 +6398,14 @@
++matchIndex) {
componentName = matchingCodecs[matchIndex];
+ OMXClient client;
+ bool trebleFlag;
+ if (client.connect(owners[matchIndex].c_str(), &trebleFlag) != OK) {
+ mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
+ return false;
+ }
+ omx = client.interface();
+
pid_t tid = gettid();
int prevPriority = androidGetThreadPriority(tid);
androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
@@ -6396,6 +6413,7 @@
androidSetThreadPriority(tid, prevPriority);
if (err == OK) {
+ mCodec->setTrebleFlag(trebleFlag);
break;
} else {
ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
@@ -8219,16 +8237,15 @@
}
status_t ACodec::queryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps) {
- (*caps).clear();
- const char *role = GetComponentRole(isEncoder, mime.c_str());
+ const char* owner, const char* name, const char* mime, bool isEncoder,
+ MediaCodecInfo::CapabilitiesWriter* caps) {
+ const char *role = GetComponentRole(isEncoder, mime);
if (role == NULL) {
return BAD_VALUE;
}
OMXClient client;
- status_t err = client.connect();
+ status_t err = client.connect(owner);
if (err != OK) {
return err;
}
@@ -8237,7 +8254,7 @@
sp<CodecObserver> observer = new CodecObserver;
sp<IOMXNode> omxNode;
- err = omx->allocateNode(name.c_str(), observer, &omxNode);
+ err = omx->allocateNode(name, observer, &omxNode);
if (err != OK) {
client.disconnect();
return err;
@@ -8250,8 +8267,7 @@
return err;
}
- sp<MediaCodecInfo::CapabilitiesBuilder> builder = new MediaCodecInfo::CapabilitiesBuilder();
- bool isVideo = mime.startsWithIgnoreCase("video/");
+ bool isVideo = strncasecmp(mime, "video/", 6) == 0;
if (isVideo) {
OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
@@ -8266,22 +8282,22 @@
if (err != OK) {
break;
}
- builder->addProfileLevel(param.eProfile, param.eLevel);
+ caps->addProfileLevel(param.eProfile, param.eLevel);
// AVC components may not list the constrained profiles explicitly, but
// decoders that support a profile also support its constrained version.
// Encoders must explicitly support constrained profiles.
- if (!isEncoder && mime.equalsIgnoreCase(MEDIA_MIMETYPE_VIDEO_AVC)) {
+ if (!isEncoder && strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
- builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
+ caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
} else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
- builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
+ caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
}
}
if (index == kMaxIndicesToCheck) {
ALOGW("[%s] stopping checking profiles after %u: %x/%x",
- name.c_str(), index,
+ name, index,
param.eProfile, param.eLevel);
}
}
@@ -8305,17 +8321,17 @@
if (IsFlexibleColorFormat(
omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
&flexibleEquivalent)) {
- builder->addColorFormat(flexibleEquivalent);
+ caps->addColorFormat(flexibleEquivalent);
}
- builder->addColorFormat(portFormat.eColorFormat);
+ caps->addColorFormat(portFormat.eColorFormat);
if (index == kMaxIndicesToCheck) {
ALOGW("[%s] stopping checking formats after %u: %s(%x)",
- name.c_str(), index,
+ name, index,
asString(portFormat.eColorFormat), portFormat.eColorFormat);
}
}
- } else if (mime.equalsIgnoreCase(MEDIA_MIMETYPE_AUDIO_AAC)) {
+ } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
// More audio codecs if they have profiles.
OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
InitOMXParams(¶m);
@@ -8329,11 +8345,11 @@
break;
}
// For audio, level is ignored.
- builder->addProfileLevel(param.eProfile, 0 /* level */);
+ caps->addProfileLevel(param.eProfile, 0 /* level */);
if (index == kMaxIndicesToCheck) {
ALOGW("[%s] stopping checking profiles after %u: %x",
- name.c_str(), index,
+ name, index,
param.eProfile);
}
}
@@ -8341,7 +8357,7 @@
// NOTE: Without Android extensions, OMX does not provide a way to query
// AAC profile support
if (param.nProfileIndex == 0) {
- ALOGW("component %s doesn't support profile query.", name.c_str());
+ ALOGW("component %s doesn't support profile query.", name);
}
}
@@ -8350,14 +8366,14 @@
if (omxNode->configureVideoTunnelMode(
kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
// tunneled playback includes adaptive playback
- builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
| MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
} else if (omxNode->setPortMode(
kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK ||
omxNode->prepareForAdaptivePlayback(
kPortIndexOutput, OMX_TRUE,
1280 /* width */, 720 /* height */) == OK) {
- builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
}
}
@@ -8369,11 +8385,10 @@
if (omxNode->getConfig(
(OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
¶ms, sizeof(params)) == OK) {
- builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
}
}
- *caps = builder;
omxNode->freeNode();
client.disconnect();
return OK;
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 348abf8..c8d4e4a 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -51,6 +51,7 @@
"NuCachedSource2.cpp",
"NuMediaExtractor.cpp",
"OMXClient.cpp",
+ "OmxInfoBuilder.cpp",
"OggExtractor.cpp",
"SampleIterator.cpp",
"SampleTable.cpp",
@@ -93,6 +94,7 @@
"libnativewindow",
"libmedia_helper",
+ "libstagefright_omx_utils",
"libstagefright_flacdec",
"libstagefright_foundation",
"libstagefright_xmlparser",
@@ -114,7 +116,6 @@
"libstagefright_aacenc",
"libstagefright_matroska",
"libstagefright_mediafilter",
- "libstagefright_omx_utils",
"libstagefright_webm",
"libstagefright_timedtext",
"libvpx",
@@ -124,9 +125,12 @@
"libFLAC",
],
- export_shared_lib_headers: ["libmedia"],
+ export_shared_lib_headers: [
+ "libmedia",
+ "android.hidl.allocator@1.0",
+ ],
+
export_include_dirs: [
- ".",
"include",
],
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6f59fac..a1d48c2 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -354,6 +354,10 @@
}
MPEG4Extractor::~MPEG4Extractor() {
+ release();
+}
+
+void MPEG4Extractor::release() {
Track *track = mFirstTrack;
while (track) {
Track *next = track->next;
@@ -375,6 +379,12 @@
for (size_t i = 0; i < mPssh.size(); i++) {
delete [] mPssh[i].data;
}
+ mPssh.clear();
+
+ if (mDataSource != NULL) {
+ mDataSource->close();
+ mDataSource.clear();
+ }
}
uint32_t MPEG4Extractor::flags() const {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index d7383cd..abacf13 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -434,22 +434,6 @@
}
// static
-status_t MediaCodec::QueryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) {
- // TRICKY: this method is used by MediaCodecList/Info during its
- // initialization. As such, we cannot create a MediaCodec instance
- // because that requires an initialized MediaCodecList.
-
- sp<CodecBase> codec = GetCodecBase(name);
- if (codec == NULL) {
- return NAME_NOT_FOUND;
- }
-
- return codec->queryCapabilities(name, mime, isEncoder, caps);
-}
-
-// static
sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
OMXClient client;
if (client.connect() != OK) {
@@ -1616,6 +1600,19 @@
}
setState(CONFIGURED);
(new AMessage)->postReply(mReplyID);
+
+ // augment our media metrics info, now that we know more things
+ if (mAnalyticsItem != NULL) {
+ sp<AMessage> format;
+ if (mConfigureMsg != NULL &&
+ mConfigureMsg->findMessage("format", &format)) {
+ // format includes: mime
+ AString mime;
+ if (format->findString("mime", &mime)) {
+ mAnalyticsItem->setCString(kCodecMime, mime.c_str());
+ }
+ }
+ }
break;
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 1dcba29..4652594 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -24,17 +24,17 @@
#include <media/IMediaCodecList.h>
#include <media/IMediaPlayerService.h>
-#include <media/IResourceManagerService.h>
+#include <media/IMediaCodecService.h>
#include <media/MediaCodecInfo.h>
-#include <media/MediaResourcePolicy.h>
+#include <media/MediaDefs.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/ACodec.h>
-#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OmxInfoBuilder.h>
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
#include <sys/stat.h>
#include <utils/threads.h>
@@ -42,20 +42,20 @@
#include <cutils/properties.h>
#include <expat.h>
+#include <algorithm>
+
namespace android {
-static Mutex sInitMutex;
+namespace {
-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;
-}
+Mutex sInitMutex;
-static bool isProfilingNeeded() {
+Mutex sRemoteInitMutex;
+
+constexpr const char* kProfilingResults =
+ MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
+
+bool isProfilingNeeded() {
int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
if (value == 0) {
return false;
@@ -78,6 +78,10 @@
return profilingNeeded;
}
+OmxInfoBuilder sOmxInfoBuilder;
+
+} // unnamed namespace
+
// static
sp<IMediaCodecList> MediaCodecList::sCodecList;
@@ -86,42 +90,42 @@
ALOGV("Enter profilerThreadWrapper.");
remove(kProfilingResults); // remove previous result so that it won't be loaded to
// the new MediaCodecList
- MediaCodecList *codecList = new MediaCodecList();
+ sp<MediaCodecList> codecList(new MediaCodecList(&sOmxInfoBuilder));
if (codecList->initCheck() != OK) {
ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
- delete codecList;
- return NULL;
+ return nullptr;
}
- Vector<sp<MediaCodecInfo>> infos;
- for (size_t i = 0; i < codecList->countCodecs(); ++i) {
- infos.push_back(codecList->getCodecInfo(i));
- }
+ const auto& infos = codecList->mCodecInfos;
ALOGV("Codec profiling started.");
- profileCodecs(infos);
+ profileCodecs(infos, kProfilingResults);
ALOGV("Codec profiling completed.");
- codecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
+ codecList = new MediaCodecList(&sOmxInfoBuilder);
+ if (codecList->initCheck() != OK) {
+ ALOGW("Failed to parse profiling results.");
+ return nullptr;
+ }
{
Mutex::Autolock autoLock(sInitMutex);
sCodecList = codecList;
}
- return NULL;
+ return nullptr;
}
// static
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
Mutex::Autolock autoLock(sInitMutex);
- if (sCodecList == NULL) {
- MediaCodecList *codecList = new MediaCodecList;
+ if (sCodecList == nullptr) {
+ MediaCodecList *codecList = new MediaCodecList(&sOmxInfoBuilder);
if (codecList->initCheck() == OK) {
sCodecList = codecList;
if (isProfilingNeeded()) {
ALOGV("Codec profiling needed, will be run in separated thread.");
pthread_t profiler;
- if (pthread_create(&profiler, NULL, profilerThreadWrapper, NULL) != 0) {
+ if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
ALOGW("Failed to create thread for codec profiling.");
}
}
@@ -134,8 +138,6 @@
return sCodecList;
}
-static Mutex sRemoteInitMutex;
-
sp<IMediaCodecList> MediaCodecList::sRemoteList;
sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
@@ -149,19 +151,19 @@
// static
sp<IMediaCodecList> MediaCodecList::getInstance() {
Mutex::Autolock _l(sRemoteInitMutex);
- if (sRemoteList == NULL) {
+ if (sRemoteList == nullptr) {
sp<IBinder> binder =
defaultServiceManager()->getService(String16("media.player"));
sp<IMediaPlayerService> service =
interface_cast<IMediaPlayerService>(binder);
- if (service.get() != NULL) {
+ if (service.get() != nullptr) {
sRemoteList = service->getCodecList();
- if (sRemoteList != NULL) {
+ if (sRemoteList != nullptr) {
sBinderDeathObserver = new BinderDeathObserver();
binder->linkToDeath(sBinderDeathObserver.get());
}
}
- if (sRemoteList == NULL) {
+ if (sRemoteList == nullptr) {
// if failed to get remote list, create local list
sRemoteList = getLocalInstance();
}
@@ -169,175 +171,11 @@
return sRemoteList;
}
-// Treblized media codec list will be located in /odm/etc or /vendor/etc.
-static const char *kConfigLocationList[] =
- {"/odm/etc", "/vendor/etc", "/etc"};
-static const int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
-#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 128
-
-static bool findMediaCodecListFileFullPath(const char *file_name, char *out_path) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
- snprintf(out_path,
- MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH,
- "%s/%s",
- kConfigLocationList[i],
- file_name);
- struct stat file_stat;
- if (stat(out_path, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
- return true;
- }
- }
- return false;
-}
-
-MediaCodecList::MediaCodecList()
- : mInitCheck(NO_INIT),
- mUpdate(false),
- mGlobalSettings(new AMessage()) {
- char config_file_path[MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH];
- if (findMediaCodecListFileFullPath("media_codecs.xml", config_file_path)) {
- parseTopLevelXMLFile(config_file_path);
- }
- if (findMediaCodecListFileFullPath("media_codecs_performance.xml",
- config_file_path)) {
- parseTopLevelXMLFile(config_file_path, true/* ignore_errors */);
- }
- parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
-}
-
-void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
- // get href_base
- const char *href_base_end = strrchr(codecs_xml, '/');
- if (href_base_end != NULL) {
- mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
- }
-
- mInitCheck = OK; // keeping this here for safety
- mCurrentSection = SECTION_TOPLEVEL;
- mDepth = 0;
-
- OMXClient client;
- mInitCheck = client.connect();
- if (mInitCheck != OK) {
- return; // this may fail if IMediaPlayerService is not available.
- }
- parseXMLFile(codecs_xml);
-
- 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 (mGlobalSettings->findString(kPolicySupportsSecureWithNonSecureCodec, &value)) {
- policies.push_back(
- MediaResourcePolicy(
- String8(kPolicySupportsSecureWithNonSecureCodec),
- 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;) {
- i--;
- 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?",
- info.mName.c_str());
-
- mCodecInfos.removeAt(i);
-#if LOG_NDEBUG == 0
- } else {
- for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) {
- AString mime = info.mCaps.keyAt(type_ix);
- const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix);
-
- ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(),
- caps->getDetails()->debugString().c_str());
- ALOGV(" flags=%d", caps->getFlags());
- {
- Vector<uint32_t> colorFormats;
- caps->getSupportedColorFormats(&colorFormats);
- AString nice;
- for (size_t ix = 0; ix < colorFormats.size(); ix++) {
- if (ix > 0) {
- nice.append(", ");
- }
- nice.append(colorFormats.itemAt(ix));
- }
- ALOGV(" colors=[%s]", nice.c_str());
- }
- {
- Vector<MediaCodecInfo::ProfileLevel> profileLevels;
- caps->getSupportedProfileLevels(&profileLevels);
- AString nice;
- for (size_t ix = 0; ix < profileLevels.size(); ix++) {
- if (ix > 0) {
- nice.append(", ");
- }
- const MediaCodecInfo::ProfileLevel &pl =
- profileLevels.itemAt(ix);
- nice.append(pl.mProfile);
- nice.append("/");
- nice.append(pl.mLevel);
- }
- 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
- }
- }
-
-#if 0
- for (size_t i = 0; i < mCodecInfos.size(); ++i) {
- const CodecInfo &info = mCodecInfos.itemAt(i);
-
- AString line = info.mName;
- line.append(" supports ");
- for (size_t j = 0; j < mTypes.size(); ++j) {
- uint32_t value = mTypes.valueAt(j);
-
- if (info.mTypes & (1ul << value)) {
- line.append(mTypes.keyAt(j));
- line.append(" ");
- }
- }
-
- ALOGI("%s", line.c_str());
- }
-#endif
+MediaCodecList::MediaCodecList(MediaCodecListBuilderBase* builder) {
+ mGlobalSettings = new AMessage();
+ mCodecInfos.clear();
+ MediaCodecListWriter writer(this);
+ mInitCheck = builder->buildMediaCodecList(&writer);
}
MediaCodecList::~MediaCodecList() {
@@ -347,531 +185,21 @@
return mInitCheck;
}
-void MediaCodecList::parseXMLFile(const char *path) {
- FILE *file = fopen(path, "r");
-
- if (file == NULL) {
- ALOGW("unable to open media codecs configuration xml file: %s", path);
- mInitCheck = NAME_NOT_FOUND;
- return;
- }
-
- XML_Parser parser = ::XML_ParserCreate(NULL);
- CHECK(parser != NULL);
-
- ::XML_SetUserData(parser, this);
- ::XML_SetElementHandler(
- parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
-
- const int BUFF_SIZE = 512;
- while (mInitCheck == OK) {
- void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
- if (buff == NULL) {
- ALOGE("failed in call to XML_GetBuffer()");
- mInitCheck = UNKNOWN_ERROR;
- break;
- }
-
- int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
- if (bytes_read < 0) {
- ALOGE("failed in call to read");
- mInitCheck = ERROR_IO;
- break;
- }
-
- XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
- if (status != XML_STATUS_OK) {
- ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
- mInitCheck = ERROR_MALFORMED;
- break;
- }
-
- if (bytes_read == 0) {
- break;
- }
- }
-
- ::XML_ParserFree(parser);
-
- fclose(file);
- file = NULL;
+MediaCodecListWriter::MediaCodecListWriter(MediaCodecList* list) :
+ mList(list) {
}
-// static
-void MediaCodecList::StartElementHandlerWrapper(
- void *me, const char *name, const char **attrs) {
- static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
+void MediaCodecListWriter::addGlobalSetting(
+ const char* key, const char* value) {
+ mList->mGlobalSettings->setString(key, value);
}
-// static
-void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
- static_cast<MediaCodecList *>(me)->endElementHandler(name);
-}
-
-status_t MediaCodecList::includeXMLFile(const char **attrs) {
- const char *href = NULL;
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "href")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- href = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
- ++i;
- }
-
- // For security reasons and for simplicity, file names can only contain
- // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml
- for (i = 0; href[i] != '\0'; i++) {
- if (href[i] == '.' || href[i] == '_' ||
- (href[i] >= '0' && href[i] <= '9') ||
- (href[i] >= 'A' && href[i] <= 'Z') ||
- (href[i] >= 'a' && href[i] <= 'z')) {
- continue;
- }
- ALOGE("invalid include file name: %s", href);
- return -EINVAL;
- }
-
- AString filename = href;
- if (!filename.startsWith("media_codecs_") ||
- !filename.endsWith(".xml")) {
- ALOGE("invalid include file name: %s", href);
- return -EINVAL;
- }
- filename.insert(mHrefBase, 0);
-
- parseXMLFile(filename.c_str());
- return mInitCheck;
-}
-
-void MediaCodecList::startElementHandler(
- const char *name, const char **attrs) {
- if (mInitCheck != OK) {
- return;
- }
-
- bool inType = true;
-
- if (!strcmp(name, "Include")) {
- mInitCheck = includeXMLFile(attrs);
- if (mInitCheck == OK) {
- mPastSections.push(mCurrentSection);
- mCurrentSection = SECTION_INCLUDE;
- }
- ++mDepth;
- return;
- }
-
- switch (mCurrentSection) {
- case SECTION_TOPLEVEL:
- {
- if (!strcmp(name, "Decoders")) {
- 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;
- }
-
- case SECTION_DECODERS:
- {
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
- addMediaCodecFromAttributes(false /* encoder */, attrs);
-
- mCurrentSection = SECTION_DECODER;
- }
- break;
- }
-
- case SECTION_ENCODERS:
- {
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
- addMediaCodecFromAttributes(true /* encoder */, attrs);
-
- mCurrentSection = SECTION_ENCODER;
- }
- break;
- }
-
- case SECTION_DECODER:
- case SECTION_ENCODER:
- {
- if (!strcmp(name, "Quirk")) {
- mInitCheck = addQuirk(attrs);
- } else if (!strcmp(name, "Type")) {
- mInitCheck = addTypeFromAttributes(attrs);
- mCurrentSection =
- (mCurrentSection == SECTION_DECODER
- ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
- }
- }
- inType = false;
- // fall through
-
- case SECTION_DECODER_TYPE:
- case SECTION_ENCODER_TYPE:
- {
- // ignore limits and features specified outside of type
- bool outside = !inType && !mCurrentInfo->mHasSoleMime;
- if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
- ALOGW("ignoring %s specified outside of a Type", name);
- } else if (!strcmp(name, "Limit")) {
- mInitCheck = addLimit(attrs);
- } else if (!strcmp(name, "Feature")) {
- mInitCheck = addFeature(attrs);
- }
- break;
- }
-
- default:
- break;
- }
-
- ++mDepth;
-}
-
-void MediaCodecList::endElementHandler(const char *name) {
- if (mInitCheck != OK) {
- return;
- }
-
- switch (mCurrentSection) {
- case SECTION_SETTINGS:
- {
- if (!strcmp(name, "Settings")) {
- mCurrentSection = SECTION_TOPLEVEL;
- }
- break;
- }
-
- case SECTION_DECODERS:
- {
- if (!strcmp(name, "Decoders")) {
- mCurrentSection = SECTION_TOPLEVEL;
- }
- break;
- }
-
- case SECTION_ENCODERS:
- {
- if (!strcmp(name, "Encoders")) {
- mCurrentSection = SECTION_TOPLEVEL;
- }
- break;
- }
-
- case SECTION_DECODER_TYPE:
- case SECTION_ENCODER_TYPE:
- {
- if (!strcmp(name, "Type")) {
- mCurrentSection =
- (mCurrentSection == SECTION_DECODER_TYPE
- ? SECTION_DECODER : SECTION_ENCODER);
-
- mCurrentInfo->complete();
- }
- break;
- }
-
- case SECTION_DECODER:
- {
- if (!strcmp(name, "MediaCodec")) {
- mCurrentSection = SECTION_DECODERS;
- mCurrentInfo->complete();
- mCurrentInfo = NULL;
- }
- break;
- }
-
- case SECTION_ENCODER:
- {
- if (!strcmp(name, "MediaCodec")) {
- mCurrentSection = SECTION_ENCODERS;
- mCurrentInfo->complete();;
- mCurrentInfo = NULL;
- }
- break;
- }
-
- case SECTION_INCLUDE:
- {
- if (!strcmp(name, "Include") && mPastSections.size() > 0) {
- mCurrentSection = mPastSections.top();
- mPastSections.pop();
- }
- break;
- }
-
- default:
- break;
- }
-
- --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) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "type")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- type = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- update = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
-
- ++i;
- }
-
- if (name == NULL) {
- return -EINVAL;
- }
-
- mUpdate = (update != NULL) && parseBoolean(update);
- ssize_t index = -1;
- for (size_t i = 0; i < mCodecInfos.size(); ++i) {
- if (AString(name) == mCodecInfos[i]->getCodecName()) {
- index = i;
- }
- }
- if (mUpdate != (index >= 0)) {
- return -EINVAL;
- }
-
- if (index >= 0) {
- // existing codec
- mCurrentInfo = mCodecInfos.editItemAt(index);
- if (type != NULL) {
- // existing type
- if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
- return -EINVAL;
- }
- mCurrentInfo->updateMime(type);
- }
- } else {
- // new codec
- mCurrentInfo = new MediaCodecInfo(name, encoder, type);
- // The next step involves trying to load the codec, which may
- // fail. Only list the codec if this succeeds.
- // However, keep mCurrentInfo object around until parsing
- // of full codec info is completed.
- if (initializeCapabilities(type) == OK) {
- mCodecInfos.push_back(mCurrentInfo);
- }
- }
-
- return OK;
-}
-
-status_t MediaCodecList::initializeCapabilities(const char *type) {
- if (type == NULL) {
- return OK;
- }
-
- ALOGV("initializeCapabilities %s:%s",
- mCurrentInfo->mName.c_str(), type);
-
- sp<MediaCodecInfo::Capabilities> caps;
- status_t err = MediaCodec::QueryCapabilities(
- mCurrentInfo->mName,
- type,
- mCurrentInfo->mIsEncoder,
- &caps);
- if (err != OK) {
- return err;
- } else if (caps == NULL) {
- ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
- mCurrentInfo->mName.c_str(), type,
- mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
- return UNKNOWN_ERROR;
- }
-
- return mCurrentInfo->initializeCapabilities(caps);
-}
-
-status_t MediaCodecList::addQuirk(const char **attrs) {
- const char *name = 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 {
- return -EINVAL;
- }
-
- ++i;
- }
-
- if (name == NULL) {
- return -EINVAL;
- }
-
- mCurrentInfo->addQuirk(name);
- return OK;
-}
-
-status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
- const char *name = 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], "update")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- update = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
-
- ++i;
- }
-
- if (name == NULL) {
- return -EINVAL;
- }
-
- 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 (!mUpdate && initializeCapabilities(name) != OK) {
- mCurrentInfo->removeMime(name);
- }
- return OK;
+std::unique_ptr<MediaCodecInfoWriter>
+ MediaCodecListWriter::addMediaCodecInfo() {
+ sp<MediaCodecInfo> info = new MediaCodecInfo();
+ mList->mCodecInfos.push_back(info);
+ return std::unique_ptr<MediaCodecInfoWriter>(
+ new MediaCodecInfoWriter(info.get()));
}
// legacy method for non-advanced codecs
@@ -882,15 +210,15 @@
"feature-tunneled-playback",
};
- size_t numCodecs = mCodecInfos.size();
- for (; startIndex < numCodecs; ++startIndex) {
- const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get();
+ size_t numCodecInfos = mCodecInfos.size();
+ for (; startIndex < numCodecInfos; ++startIndex) {
+ const MediaCodecInfo &info = *mCodecInfos[startIndex];
if (info.isEncoder() != encoder) {
continue;
}
sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
- if (capabilities == NULL) {
+ if (capabilities == nullptr) {
continue;
}
const sp<AMessage> &details = capabilities->getDetails();
@@ -913,223 +241,9 @@
return -ENOENT;
}
-static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
- ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
- (found ? "" : "no "), attr);
- return -EINVAL;
-}
-
-static status_t limitError(const AString &name, const char *msg) {
- ALOGE("limit '%s' %s", name.c_str(), msg);
- return -EINVAL;
-}
-
-static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
- ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
- attr, value.c_str());
- return -EINVAL;
-}
-
-status_t MediaCodecList::addLimit(const char **attrs) {
- sp<AMessage> msg = new AMessage();
-
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")
- || !strcmp(attrs[i], "default")
- || !strcmp(attrs[i], "in")
- || !strcmp(attrs[i], "max")
- || !strcmp(attrs[i], "min")
- || !strcmp(attrs[i], "range")
- || !strcmp(attrs[i], "ranges")
- || !strcmp(attrs[i], "scale")
- || !strcmp(attrs[i], "value")) {
- msg->setString(attrs[i], attrs[i + 1]);
- ++i;
- } else {
- return -EINVAL;
- }
- ++i;
- }
-
- AString name;
- if (!msg->findString("name", &name)) {
- ALOGE("limit with no 'name' attribute");
- return -EINVAL;
- }
-
- // 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 == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
- AString min, max;
- if (msg->findString("min", &min) && msg->findString("max", &max)) {
- min.append("-");
- min.append(max);
- if (msg->contains("range") || msg->contains("value")) {
- return limitError(name, "has 'min' and 'max' as well as 'range' or "
- "'value' attributes");
- }
- msg->setString("range", min);
- } else if (msg->contains("min") || msg->contains("max")) {
- return limitError(name, "has only 'min' or 'max' attribute");
- } else if (msg->findString("value", &max)) {
- min = max;
- min.append("-");
- min.append(max);
- if (msg->contains("range")) {
- return limitError(name, "has both 'range' and 'value' attributes");
- }
- msg->setString("range", min);
- }
-
- AString range, scale = "linear", def, in_;
- if (!msg->findString("range", &range)) {
- return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
- }
-
- if ((name == "quality" || name == "complexity") ^
- (found = msg->findString("default", &def))) {
- return limitFoundMissingAttr(name, "default", found);
- }
- if (name != "quality" && msg->findString("scale", &scale)) {
- return limitFoundMissingAttr(name, "scale");
- }
- if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
- return limitFoundMissingAttr(name, "in", found);
- }
-
- if (name == "aspect-ratio") {
- if (!(in_ == "pixels") && !(in_ == "blocks")) {
- return limitInvalidAttr(name, "in", in_);
- }
- in_.erase(5, 1); // (pixel|block)-aspect-ratio
- in_.append("-");
- in_.append(name);
- name = in_;
- }
- if (name == "quality") {
- mCurrentInfo->addDetail("quality-scale", scale);
- }
- if (name == "quality" || name == "complexity") {
- AString tag = name;
- tag.append("-default");
- mCurrentInfo->addDetail(tag, def);
- }
- AString tag = name;
- tag.append("-range");
- mCurrentInfo->addDetail(tag, range);
- } else {
- AString max, value, ranges;
- if (msg->contains("default")) {
- return limitFoundMissingAttr(name, "default");
- } else if (msg->contains("in")) {
- return limitFoundMissingAttr(name, "in");
- } else if ((name == "channel-count" || name == "concurrent-instances") ^
- (found = msg->findString("max", &max))) {
- return limitFoundMissingAttr(name, "max", found);
- } else if (msg->contains("min")) {
- return limitFoundMissingAttr(name, "min");
- } else if (msg->contains("range")) {
- return limitFoundMissingAttr(name, "range");
- } else if ((name == "sample-rate") ^
- (found = msg->findString("ranges", &ranges))) {
- return limitFoundMissingAttr(name, "ranges", found);
- } else if (msg->contains("scale")) {
- return limitFoundMissingAttr(name, "scale");
- } else if ((name == "alignment" || name == "block-size") ^
- (found = msg->findString("value", &value))) {
- return limitFoundMissingAttr(name, "value", found);
- }
-
- if (max.size()) {
- AString tag = "max-";
- tag.append(name);
- mCurrentInfo->addDetail(tag, max);
- } else if (value.size()) {
- mCurrentInfo->addDetail(name, value);
- } else if (ranges.size()) {
- AString tag = name;
- tag.append("-ranges");
- mCurrentInfo->addDetail(tag, ranges);
- } else {
- ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
- }
- }
- return OK;
-}
-
-status_t MediaCodecList::addFeature(const char **attrs) {
- size_t i = 0;
- const char *name = NULL;
- int32_t optional = -1;
- int32_t required = -1;
- const char *value = NULL;
-
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")) {
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
- int value = (int)parseBoolean(attrs[i + 1]);
- if (!strcmp(attrs[i], "optional")) {
- optional = value;
- } else {
- required = value;
- }
- ++i;
- } else if (!strcmp(attrs[i], "value")) {
- value = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
- ++i;
- }
- if (name == NULL) {
- ALOGE("feature with no 'name' attribute");
- return -EINVAL;
- }
-
- if (optional == required && optional != -1) {
- ALOGE("feature '%s' is both/neither optional and required", name);
- return -EINVAL;
- }
-
- if ((optional != -1 || required != -1) && (value != NULL)) {
- ALOGE("feature '%s' has both a value and optional/required attribute", name);
- return -EINVAL;
- }
-
- if (value != NULL) {
- mCurrentInfo->addFeature(name, value);
- } else {
- mCurrentInfo->addFeature(name, (required == 1) || (optional == 0));
- }
- return OK;
-}
-
ssize_t MediaCodecList::findCodecByName(const char *name) const {
for (size_t i = 0; i < mCodecInfos.size(); ++i) {
- const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-
- if (info.mName == name) {
+ if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
return i;
}
}
@@ -1167,11 +281,15 @@
//static
void MediaCodecList::findMatchingCodecs(
- const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) {
+ const char *mime, bool encoder, uint32_t flags,
+ Vector<AString> *matches, Vector<AString> *owners) {
matches->clear();
+ if (owners != nullptr) {
+ owners->clear();
+ }
const sp<IMediaCodecList> list = getInstance();
- if (list == NULL) {
+ if (list == nullptr) {
return;
}
@@ -1187,45 +305,27 @@
index = matchIndex + 1;
const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
- CHECK(info != NULL);
+ CHECK(info != nullptr);
AString componentName = info->getCodecName();
if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
ALOGV("skipping SW codec '%s'", componentName.c_str());
} else {
matches->push(componentName);
+ if (owners != nullptr) {
+ owners->push(AString(info->getOwnerName()));
+ }
ALOGV("matching '%s'", componentName.c_str());
}
}
- if (flags & kPreferSoftwareCodecs || property_get_bool("debug.stagefright.swcodec", false)) {
+ if (flags & kPreferSoftwareCodecs ||
+ property_get_bool("debug.stagefright.swcodec", false)) {
matches->sort(compareSoftwareCodecsFirst);
}
}
-// static
-uint32_t MediaCodecList::getQuirksFor(const char *componentName) {
- const sp<IMediaCodecList> list = getInstance();
- if (list == NULL) {
- return 0;
- }
-
- ssize_t ix = list->findCodecByName(componentName);
- if (ix < 0) {
- return 0;
- }
-
- const sp<MediaCodecInfo> info = list->getCodecInfo(ix);
-
- uint32_t quirks = 0;
- if (info->hasQuirk("requires-allocate-on-input-ports")) {
- quirks |= ACodec::kRequiresAllocateBufferOnInputPorts;
- }
- if (info->hasQuirk("requires-allocate-on-output-ports")) {
- quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts;
- }
-
- return quirks;
+MediaCodecListBuilderBase::~MediaCodecListBuilderBase() {
}
} // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
index 095fc6a..6920e51 100644
--- a/media/libstagefright/MediaCodecListOverrides.cpp
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -33,8 +33,6 @@
namespace android {
-const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml";
-
AString getProfilingVersionString() {
char val[PROPERTY_VALUE_MAX];
if (property_get("ro.build.display.id", val, NULL) && (strlen(val) > 0)) {
@@ -205,24 +203,24 @@
return true;
}
-void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos) {
+void profileCodecs(const std::vector<sp<MediaCodecInfo>> &infos,
+ const char* profilingResults) {
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);
+ exportResultsToXML(profilingResults, global_results, encoder_results, decoder_results);
}
void profileCodecs(
- const Vector<sp<MediaCodecInfo>> &infos,
+ const std::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];
+ for (const auto& info : infos) {
AString name = info->getCodecName();
if (name.startsWith("OMX.google.") ||
// TODO: reenable below codecs once fixed
diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h
index d4bb225..4f8c2f5 100644
--- a/media/libstagefright/MediaCodecListOverrides.h
+++ b/media/libstagefright/MediaCodecListOverrides.h
@@ -23,12 +23,10 @@
#include <utils/StrongPointer.h>
#include <utils/KeyedVector.h>
+#include <vector>
namespace android {
-extern const char *kProfilingVersionString;
-extern const char *kProfilingResults;
-
struct MediaCodecInfo;
AString getProfilingVersionString();
@@ -36,11 +34,12 @@
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);
+void profileCodecs(const std::vector<sp<MediaCodecInfo>> &infos,
+ const char* profilingResults);
// profile codecs and save the result to global_results, encoder_results and decoder_results.
void profileCodecs(
- const Vector<sp<MediaCodecInfo>> &infos,
+ const std::vector<sp<MediaCodecInfo>> &infos,
CodecSettings *global_results,
KeyedVector<AString, CodecSettings> *encoder_results,
KeyedVector<AString, CodecSettings> *decoder_results,
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 5f9aa01..5f50e46 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -38,7 +38,7 @@
}
status_t OMXClient::connect() {
- return connect(nullptr);
+ return connect("default", nullptr);
}
status_t OMXClient::connect(bool* trebleFlag) {
@@ -54,6 +54,19 @@
return connectLegacy();
}
+status_t OMXClient::connect(const char* name, bool* trebleFlag) {
+ if (property_get_bool("persist.media.treble_omx", true)) {
+ if (trebleFlag != nullptr) {
+ *trebleFlag = true;
+ }
+ return connectTreble(name);
+ }
+ if (trebleFlag != nullptr) {
+ *trebleFlag = false;
+ }
+ return connectLegacy();
+}
+
status_t OMXClient::connectLegacy() {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> codecbinder = sm->getService(String16("media.codec"));
@@ -73,9 +86,12 @@
return OK;
}
-status_t OMXClient::connectTreble() {
+status_t OMXClient::connectTreble(const char* name) {
using namespace ::android::hardware::media::omx::V1_0;
- sp<IOmx> tOmx = IOmx::getService("default");
+ if (name == nullptr) {
+ name = "default";
+ }
+ sp<IOmx> tOmx = IOmx::getService(name);
if (tOmx.get() == nullptr) {
ALOGE("Cannot obtain Treble IOmx.");
return NO_INIT;
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
new file mode 100644
index 0000000..686bcc8
--- /dev/null
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2017 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 "OmxInfoBuilder"
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+#include <utils/Log.h>
+#include <cutils/properties.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaCodecService.h>
+#include <media/stagefright/OmxInfoBuilder.h>
+#include <media/stagefright/ACodec.h>
+
+#include <android/hardware/media/omx/1.0/IOmxStore.h>
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <media/stagefright/omx/OMXUtils.h>
+
+#include <media/IOMXStore.h>
+#include <media/IOMX.h>
+#include <media/MediaDefs.h>
+#include <media/omx/1.0/WOmx.h>
+
+#include <media/openmax/OMX_Index.h>
+#include <media/openmax/OMX_IndexExt.h>
+#include <media/openmax/OMX_Audio.h>
+#include <media/openmax/OMX_AudioExt.h>
+#include <media/openmax/OMX_Video.h>
+#include <media/openmax/OMX_VideoExt.h>
+
+namespace android {
+
+namespace /* unnamed */ {
+
+status_t queryCapabilities(
+ const IOMXStore::NodeInfo& node, const char* mime, bool isEncoder,
+ MediaCodecInfo::CapabilitiesWriter* caps) {
+ sp<ACodec> codec = new ACodec();
+ status_t err = codec->queryCapabilities(
+ node.owner.c_str(), node.name.c_str(), mime, isEncoder, caps);
+ if (err != OK) {
+ return err;
+ }
+ for (const auto& attribute : node.attributes) {
+ // All features have an int32 value except
+ // "feature-bitrate-control", which has a string value.
+ if ((attribute.key.compare(0, 8, "feature-") == 0) &&
+ (attribute.key.compare(8, 15, "bitrate-control")
+ != 0)) {
+ // If this attribute.key is a feature that is not a bitrate
+ // control, add an int32 value.
+ caps->addDetail(
+ attribute.key.c_str(),
+ attribute.value == "1" ? 1 : 0);
+ } else {
+ // Non-feature attributes
+ caps->addDetail(
+ attribute.key.c_str(), attribute.value.c_str());
+ }
+ }
+ return OK;
+}
+
+} // unnamed namespace
+
+OmxInfoBuilder::OmxInfoBuilder() {
+}
+
+status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
+ bool treble;
+ sp<IOMX> omx;
+ std::vector<IOMXStore::RoleInfo> roles;
+
+ treble = property_get_bool("persist.media.treble_omx", true);
+ if (treble) {
+ using namespace ::android::hardware::media::omx::V1_0;
+ using ::android::hardware::hidl_vec;
+ using ::android::hardware::hidl_string;
+
+ // Obtain IOmxStore
+ sp<IOmxStore> omxStore = IOmxStore::getService();
+ if (omxStore == nullptr) {
+ ALOGE("Cannot connect to an IOmxStore instance.");
+ return NO_INIT;
+ }
+
+ // List service attributes (global settings)
+ Status status;
+ hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;
+ auto transStatus = omxStore->listServiceAttributes(
+ [&status, &serviceAttributes]
+ (Status inStatus, const hidl_vec<IOmxStore::ServiceAttribute>&
+ inAttributes) {
+ status = inStatus;
+ serviceAttributes = inAttributes;
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("Fail to obtain global settings from IOmxStore.");
+ return NO_INIT;
+ }
+ if (status != Status::OK) {
+ ALOGE("IOmxStore reports parsing error.");
+ return NO_INIT;
+ }
+ for (const auto& p : serviceAttributes) {
+ writer->addGlobalSetting(
+ p.key.c_str(), p.value.c_str());
+ }
+
+ // List roles and convert to IOMXStore's format
+ transStatus = omxStore->listRoles(
+ [&roles]
+ (const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {
+ roles.reserve(inRoleList.size());
+ for (const auto& inRole : inRoleList) {
+ IOMXStore::RoleInfo role;
+ role.role = inRole.role;
+ role.type = inRole.type;
+ role.isEncoder = inRole.isEncoder;
+ role.preferPlatformNodes = inRole.preferPlatformNodes;
+ std::vector<IOMXStore::NodeInfo>& nodes =
+ role.nodes;
+ nodes.reserve(inRole.nodes.size());
+ for (const auto& inNode : inRole.nodes) {
+ IOMXStore::NodeInfo node;
+ node.name = inNode.name;
+ node.owner = inNode.owner;
+ std::vector<IOMXStore::Attribute>& attributes =
+ node.attributes;
+ attributes.reserve(inNode.attributes.size());
+ for (const auto& inAttr : inNode.attributes) {
+ IOMXStore::Attribute attr;
+ attr.key = inAttr.key;
+ attr.value = inAttr.value;
+ attributes.push_back(std::move(attr));
+ }
+ nodes.push_back(std::move(node));
+ }
+ roles.push_back(std::move(role));
+ }
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("Fail to obtain codec roles from IOmxStore.");
+ return NO_INIT;
+ }
+ } else {
+ // Obtain IOMXStore
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == nullptr) {
+ ALOGE("Cannot obtain the default service manager.");
+ return NO_INIT;
+ }
+ sp<IBinder> codecBinder = sm->getService(String16("media.codec"));
+ if (codecBinder == nullptr) {
+ ALOGE("Cannot obtain the media codec service.");
+ return NO_INIT;
+ }
+ sp<IMediaCodecService> codecService =
+ interface_cast<IMediaCodecService>(codecBinder);
+ if (codecService == nullptr) {
+ ALOGE("Wrong type of media codec service obtained.");
+ return NO_INIT;
+ }
+ omx = codecService->getOMX();
+ if (omx == nullptr) {
+ ALOGE("Cannot connect to an IOMX instance.");
+ }
+ sp<IOMXStore> omxStore = codecService->getOMXStore();
+ if (omxStore == nullptr) {
+ ALOGE("Cannot connect to an IOMXStore instance.");
+ return NO_INIT;
+ }
+
+ // List service attributes (global settings)
+ std::vector<IOMXStore::Attribute> serviceAttributes;
+ status_t status = omxStore->listServiceAttributes(&serviceAttributes);
+ if (status != OK) {
+ ALOGE("Fail to obtain global settings from IOMXStore.");
+ return NO_INIT;
+ }
+ for (const auto& p : serviceAttributes) {
+ writer->addGlobalSetting(
+ p.key.c_str(), p.value.c_str());
+ }
+
+ // List roles
+ status = omxStore->listRoles(&roles);
+ if (status != OK) {
+ ALOGE("Fail to obtain codec roles from IOMXStore.");
+ return NO_INIT;
+ }
+ }
+
+ // Convert roles to lists of codecs
+
+ // codec name -> index into swCodecs
+ std::map<std::string, std::unique_ptr<MediaCodecInfoWriter> >
+ swCodecName2Info;
+ // codec name -> index into hwCodecs
+ std::map<std::string, std::unique_ptr<MediaCodecInfoWriter> >
+ hwCodecName2Info;
+ // owner name -> MediaCodecInfo
+ // This map will be used to obtain the correct IOmx service(s) needed for
+ // creating IOmxNode instances and querying capabilities.
+ std::map<std::string, std::vector<sp<MediaCodecInfo> > >
+ owner2CodecInfo;
+
+ for (const auto& role : roles) {
+ const auto& typeName = role.type;
+ bool isEncoder = role.isEncoder;
+ bool preferPlatformNodes = role.preferPlatformNodes;
+ // If preferPlatformNodes is true, hardware nodes must be added after
+ // platform (software) nodes. hwCodecs is used to hold hardware nodes
+ // that need to be added after software nodes for the same role.
+ std::vector<const IOMXStore::NodeInfo*> hwCodecs;
+ for (const auto& node : role.nodes) {
+ const auto& nodeName = node.name;
+ bool isSoftware = nodeName.compare(0, 10, "OMX.google") == 0;
+ MediaCodecInfoWriter* info;
+ if (isSoftware) {
+ auto c2i = swCodecName2Info.find(nodeName);
+ if (c2i == swCodecName2Info.end()) {
+ // Create a new MediaCodecInfo for a new node.
+ c2i = swCodecName2Info.insert(std::make_pair(
+ nodeName, writer->addMediaCodecInfo())).first;
+ info = c2i->second.get();
+ info->setName(nodeName.c_str());
+ info->setOwner(node.owner.c_str());
+ info->setEncoder(isEncoder);
+ } else {
+ // The node has been seen before. Simply retrieve the
+ // existing MediaCodecInfoWriter.
+ info = c2i->second.get();
+ }
+ } else {
+ auto c2i = hwCodecName2Info.find(nodeName);
+ if (c2i == hwCodecName2Info.end()) {
+ // Create a new MediaCodecInfo for a new node.
+ if (!preferPlatformNodes) {
+ c2i = hwCodecName2Info.insert(std::make_pair(
+ nodeName, writer->addMediaCodecInfo())).first;
+ info = c2i->second.get();
+ info->setName(nodeName.c_str());
+ info->setOwner(node.owner.c_str());
+ info->setEncoder(isEncoder);
+ } else {
+ // If preferPlatformNodes is true, this node must be
+ // added after all software nodes.
+ hwCodecs.push_back(&node);
+ continue;
+ }
+ } else {
+ // The node has been seen before. Simply retrieve the
+ // existing MediaCodecInfoWriter.
+ info = c2i->second.get();
+ }
+ }
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+ info->addMime(typeName.c_str());
+ if (queryCapabilities(
+ node, typeName.c_str(), isEncoder, caps.get()) != OK) {
+ ALOGW("Fail to add mime %s to codec %s",
+ typeName.c_str(), nodeName.c_str());
+ info->removeMime(typeName.c_str());
+ }
+ }
+
+ // If preferPlatformNodes is true, hardware nodes will not have been
+ // added in the loop above, but rather saved in hwCodecs. They are
+ // going to be added here.
+ if (preferPlatformNodes) {
+ for (const auto& node : hwCodecs) {
+ MediaCodecInfoWriter* info;
+ const auto& nodeName = node->name;
+ auto c2i = hwCodecName2Info.find(nodeName);
+ if (c2i == hwCodecName2Info.end()) {
+ // Create a new MediaCodecInfo for a new node.
+ c2i = hwCodecName2Info.insert(std::make_pair(
+ nodeName, writer->addMediaCodecInfo())).first;
+ info = c2i->second.get();
+ info->setName(nodeName.c_str());
+ info->setOwner(node->owner.c_str());
+ info->setEncoder(isEncoder);
+ } else {
+ // The node has been seen before. Simply retrieve the
+ // existing MediaCodecInfoWriter.
+ info = c2i->second.get();
+ }
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+ info->addMime(typeName.c_str());
+ if (queryCapabilities(
+ *node, typeName.c_str(), isEncoder, caps.get()) != OK) {
+ ALOGW("Fail to add mime %s to codec %s "
+ "after software codecs",
+ typeName.c_str(), nodeName.c_str());
+ info->removeMime(typeName.c_str());
+ }
+ }
+ }
+ }
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index a53897f..103da95 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -60,6 +60,12 @@
StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
ALOGV("~StagefrightMetadataRetriever()");
clearMetadata();
+ // Explicitly release extractor before continuing with the destructor,
+ // some extractors might need to callback to close off the DataSource
+ // and we need to make sure it's still there.
+ if (mExtractor != NULL) {
+ mExtractor->release();
+ }
if (mSource != NULL) {
mSource->close();
}
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
index cff4a33..c3d24c7 100644
--- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
@@ -126,6 +126,29 @@
OMX_INDEXTYPE index, OMX_PTR params) {
ALOGV("internalGetParameter: index(%x)", index);
switch ((OMX_U32)index) {
+ case OMX_IndexParamAudioPortFormat:
+ {
+ OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex > 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ formatParams->eEncoding =
+ (formatParams->nPortIndex == 0)
+ ? OMX_AUDIO_CodingFLAC : OMX_AUDIO_CodingPCM;
+
+ return OMX_ErrorNone;
+ }
case OMX_IndexParamAudioFlac:
{
OMX_AUDIO_PARAM_FLACTYPE *flacParams =
@@ -219,6 +242,29 @@
return OMX_ErrorNone;
}
+ case OMX_IndexParamAudioPortFormat:
+ {
+ const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if ((formatParams->nPortIndex == 0
+ && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)
+ || (formatParams->nPortIndex == 1
+ && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
case OMX_IndexParamAudioPcm:
{
const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
index caceda9..56d2d69 100644
--- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
+++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
@@ -154,6 +154,30 @@
ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index);
switch (index) {
+ case OMX_IndexParamAudioPortFormat:
+ {
+ OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex > 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ formatParams->eEncoding =
+ (formatParams->nPortIndex == 0)
+ ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingFLAC;
+
+ return OMX_ErrorNone;
+ }
+
case OMX_IndexParamAudioPcm:
{
OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
@@ -163,7 +187,7 @@
return OMX_ErrorBadParameter;
}
- if (pcmParams->nPortIndex > 1) {
+ if (pcmParams->nPortIndex != 0) {
return OMX_ErrorUndefined;
}
@@ -189,6 +213,10 @@
return OMX_ErrorBadParameter;
}
+ if (flacParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
flacParams->nCompressionLevel = mCompressionLevel;
flacParams->nChannels = mNumChannels;
flacParams->nSampleRate = mSampleRate;
@@ -203,6 +231,29 @@
OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter(
OMX_INDEXTYPE index, const OMX_PTR params) {
switch (index) {
+ case OMX_IndexParamAudioPortFormat:
+ {
+ const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if ((formatParams->nPortIndex == 0
+ && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
+ || (formatParams->nPortIndex == 1
+ && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
case OMX_IndexParamAudioPcm:
{
ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)");
@@ -212,7 +263,7 @@
return OMX_ErrorBadParameter;
}
- if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) {
+ if (pcmParams->nPortIndex != 0) {
ALOGE("SoftFlacEncoder::internalSetParameter() Error #1");
return OMX_ErrorUndefined;
}
@@ -258,6 +309,10 @@
return OMX_ErrorBadParameter;
}
+ if (flacParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder
return OMX_ErrorNone;
}
diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml
index a127843..829f403 100644
--- a/media/libstagefright/data/media_codecs_google_video.xml
+++ b/media/libstagefright/data/media_codecs_google_video.xml
@@ -35,10 +35,10 @@
</MediaCodec>
<MediaCodec name="OMX.google.h264.decoder" type="video/avc">
<!-- profiles and levels: ProfileHigh : Level52 -->
- <Limit name="size" min="2x2" max="4096x4096" />
+ <Limit name="size" min="2x2" max="4080x4080" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 -->
+ <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
<Limit name="blocks-per-second" range="1-1966080" />
<Limit name="bitrate" range="1-48000000" />
<Feature name="adaptive-playback" />
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 4a4c538..c9d7dde 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -62,6 +62,7 @@
virtual sp<MetaData> getMetaData();
virtual uint32_t flags() const;
virtual const char * name() { return "MPEG4Extractor"; }
+ virtual void release();
// for DRM
virtual char* getDrmTrackInfo(size_t trackID, int *len);
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index b9f48c4..424246d 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <android/native_window.h>
#include <media/hardware/MetadataBufferType.h>
+#include <media/MediaCodecInfo.h>
#include <media/IOMX.h>
#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
#include <media/stagefright/CodecBase.h>
@@ -30,6 +31,8 @@
#include <OMX_Audio.h>
#include <hardware/gralloc.h>
#include <nativebase/nativebase.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
#define TRACK_BUFFER_TIMING 0
@@ -42,20 +45,6 @@
struct DescribeColorFormat2Params;
struct DataConverter;
-// Treble shared memory
-namespace hidl {
-namespace allocator {
-namespace V1_0 {
-struct IAllocator;
-} // V1_0
-} // allocator
-namespace memory {
-namespace V1_0 {
-struct IMemory;
-} // V1_0
-} // memory
-} // hidl
-
typedef hidl::allocator::V1_0::IAllocator TAllocator;
typedef hidl::memory::V1_0::IMemory TMemory;
@@ -72,9 +61,10 @@
virtual void initiateStart();
virtual void initiateShutdown(bool keepComponentAllocated = false);
- virtual status_t queryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps);
+ status_t queryCapabilities(
+ const char* owner, const char* name,
+ const char* mime, bool isEncoder,
+ MediaCodecInfo::CapabilitiesWriter* caps);
virtual status_t setSurface(const sp<Surface> &surface);
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 6245ccb..9197f7b 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -213,10 +213,6 @@
// require an explicit message handler
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
- virtual status_t queryCapabilities(
- const AString& /*name*/, const AString& /*mime*/, bool /*isEncoder*/,
- sp<MediaCodecInfo::Capabilities>* /*caps*/ /* nonnull */) { return INVALID_OPERATION; }
-
virtual status_t setSurface(const sp<Surface>& /*surface*/) { return INVALID_OPERATION; }
virtual void signalFlush() = 0;
diff --git a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
index 044699c..c14755a 100644
--- a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
+++ b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
@@ -23,6 +23,8 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
#include <list>
@@ -30,9 +32,6 @@
namespace android {
-class Fence;
-class GraphicBuffer;
-
// Tracks the render information about a frame. Frames go through several states while
// the render information is tracked:
//
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 209fe12..836534d 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -87,11 +87,6 @@
static sp<PersistentSurface> CreatePersistentInputSurface();
- // utility method to query capabilities
- static status_t QueryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps /* nonnull */);
-
status_t configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index 430bc16..f2bd496 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -21,7 +21,6 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
#include <media/IMediaCodecList.h>
-#include <media/IOMX.h>
#include <media/MediaCodecInfo.h>
#include <sys/types.h>
@@ -36,6 +35,8 @@
struct AMessage;
+struct MediaCodecListBuilderBase;
+
struct MediaCodecList : public BnMediaCodecList {
static sp<IMediaCodecList> getInstance();
@@ -51,7 +52,7 @@
ALOGE("b/24445127");
return NULL;
}
- return mCodecInfos.itemAt(index);
+ return mCodecInfos[index];
}
virtual const sp<AMessage> getGlobalSettings() const;
@@ -62,9 +63,6 @@
// only to be used by getLocalInstance
static void *profilerThreadWrapper(void * /*arg*/);
- // only to be used by MediaPlayerService
- void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
-
enum Flags {
kPreferSoftwareCodecs = 1,
kHardwareCodecsOnly = 2,
@@ -74,13 +72,11 @@
const char *mime,
bool createEncoder,
uint32_t flags,
- Vector<AString> *matching);
-
- static uint32_t getQuirksFor(const char *mComponentName);
+ Vector<AString> *matchingCodecs,
+ Vector<AString> *owners = nullptr);
static bool isSoftwareCodec(const AString &componentName);
-
private:
class BinderDeathObserver : public IBinder::DeathRecipient {
void binderDied(const wp<IBinder> &the_late_who __unused);
@@ -88,64 +84,86 @@
static sp<BinderDeathObserver> sBinderDeathObserver;
- enum Section {
- SECTION_TOPLEVEL,
- SECTION_SETTINGS,
- SECTION_DECODERS,
- SECTION_DECODER,
- SECTION_DECODER_TYPE,
- SECTION_ENCODERS,
- SECTION_ENCODER,
- SECTION_ENCODER_TYPE,
- SECTION_INCLUDE,
- };
-
static sp<IMediaCodecList> sCodecList;
static sp<IMediaCodecList> sRemoteList;
status_t mInitCheck;
- Section mCurrentSection;
- bool mUpdate;
- Vector<Section> mPastSections;
- int32_t mDepth;
- AString mHrefBase;
sp<AMessage> mGlobalSettings;
- KeyedVector<AString, CodecSettings> mOverrides;
+ std::vector<sp<MediaCodecInfo> > mCodecInfos;
- Vector<sp<MediaCodecInfo> > mCodecInfos;
- sp<MediaCodecInfo> mCurrentInfo;
+ /**
+ * This constructor will call `buildMediaCodecList()` from the given
+ * `MediaCodecListBuilderBase` object.
+ */
+ MediaCodecList(MediaCodecListBuilderBase* builder);
- MediaCodecList();
~MediaCodecList();
status_t initCheck() const;
- void parseXMLFile(const char *path);
- static void StartElementHandlerWrapper(
- void *me, const char *name, const char **attrs);
+ MediaCodecList(const MediaCodecList&) = delete;
+ MediaCodecList& operator=(const MediaCodecList&) = delete;
- static void EndElementHandlerWrapper(void *me, const char *name);
+ friend MediaCodecListWriter;
+};
- void startElementHandler(const char *name, const char **attrs);
- void endElementHandler(const char *name);
+/**
+ * This class is to be used by a `MediaCodecListBuilderBase` instance to add
+ * information to the associated `MediaCodecList` object.
+ */
+struct MediaCodecListWriter {
+ /**
+ * Add a key-value pair to a `MediaCodecList`'s global settings.
+ *
+ * @param key Key.
+ * @param value Value.
+ */
+ void addGlobalSetting(const char* key, const char* value);
+ /**
+ * Create an add a new `MediaCodecInfo` object to a `MediaCodecList`, and
+ * return a `MediaCodecInfoWriter` object associated with the newly added
+ * `MediaCodecInfo`.
+ *
+ * @return The `MediaCodecInfoWriter` object associated with the newly
+ * added `MediaCodecInfo` object.
+ */
+ std::unique_ptr<MediaCodecInfoWriter> addMediaCodecInfo();
+private:
+ /**
+ * The associated `MediaCodecList` object.
+ */
+ MediaCodecList* mList;
- 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);
+ /**
+ * Construct this writer object associated with the given `MediaCodecList`
+ * object.
+ *
+ * @param list The "base" `MediaCodecList` object.
+ */
+ MediaCodecListWriter(MediaCodecList* list);
- void setCurrentCodecInfo(bool encoder, const char *name, const char *type);
+ friend MediaCodecList;
+};
- status_t addQuirk(const char **attrs);
- status_t addTypeFromAttributes(const char **attrs);
- status_t addLimit(const char **attrs);
- status_t addFeature(const char **attrs);
- void addType(const char *name);
+/**
+ * This interface is to be used by `MediaCodecList` to fill its members with
+ * appropriate information. `buildMediaCodecList()` will be called from a
+ * `MediaCodecList` object during its construction.
+ */
+struct MediaCodecListBuilderBase {
+ /**
+ * Build the `MediaCodecList` via the given `MediaCodecListWriter` interface.
+ *
+ * @param writer The writer interface.
+ * @return The status of the construction. `NO_ERROR` means success.
+ */
+ virtual status_t buildMediaCodecList(MediaCodecListWriter* writer) = 0;
- status_t initializeCapabilities(const char *type);
-
- DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList);
+ /**
+ * The default destructor does nothing.
+ */
+ virtual ~MediaCodecListBuilderBase();
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractor.h b/media/libstagefright/include/media/stagefright/MediaExtractor.h
index f12160b..6ec7eaf 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractor.h
@@ -72,6 +72,8 @@
virtual const char * name() { return "<unspecified>"; }
+ virtual void release() {}
+
protected:
MediaExtractor();
virtual ~MediaExtractor();
diff --git a/media/libstagefright/include/media/stagefright/OMXClient.h b/media/libstagefright/include/media/stagefright/OMXClient.h
index 203a181..2f159b0 100644
--- a/media/libstagefright/include/media/stagefright/OMXClient.h
+++ b/media/libstagefright/include/media/stagefright/OMXClient.h
@@ -28,9 +28,10 @@
status_t connect();
status_t connect(bool* trebleFlag);
+ status_t connect(const char* name, bool* trebleFlag = nullptr);
status_t connectLegacy();
- status_t connectTreble();
+ status_t connectTreble(const char* name = "default");
void disconnect();
sp<IOMX> interface() {
@@ -40,8 +41,8 @@
private:
sp<IOMX> mOMX;
- OMXClient(const OMXClient &);
- OMXClient &operator=(const OMXClient &);
+ OMXClient(const OMXClient &) = delete;
+ OMXClient &operator=(const OMXClient &) = delete;
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
new file mode 100644
index 0000000..1b4d873
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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 OMX_INFO_BUILDER_H_
+#define OMX_INFO_BUILDER_H_
+
+#include <media/stagefright/MediaCodecList.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class OmxInfoBuilder : public MediaCodecListBuilderBase {
+public:
+ OmxInfoBuilder();
+ status_t buildMediaCodecList(MediaCodecListWriter* writer) override;
+};
+
+} // namespace android
+
+#endif // OMX_INFO_BUILDER_H_
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index dfab3b0..fe50656 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -114,15 +114,23 @@
return Void();
}
instance->setHandle(handle);
- std::vector<AString> quirkVector;
- if (mParser.getQuirks(name.c_str(), &quirkVector) == OK) {
+
+ // Find quirks from mParser
+ const auto& codec = mParser.getCodecMap().find(name.c_str());
+ if (codec == mParser.getCodecMap().cend()) {
+ LOG(WARNING) << "Failed to obtain quirks for omx component "
+ "'" << name.c_str() << "' "
+ "from XML files";
+ } else {
uint32_t quirks = 0;
- for (const AString quirk : quirkVector) {
+ for (const auto& quirk : codec->second.quirkSet) {
if (quirk == "requires-allocate-on-input-ports") {
- quirks |= kRequiresAllocateBufferOnInputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnInputPorts;
}
if (quirk == "requires-allocate-on-output-ports") {
- quirks |= kRequiresAllocateBufferOnOutputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnOutputPorts;
}
}
instance->setQuirks(quirks);
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
index a82625a..447af6f 100644
--- a/media/libstagefright/omx/1.0/OmxStore.cpp
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -21,6 +21,7 @@
#include <media/stagefright/omx/1.0/Conversion.h>
#include <media/stagefright/omx/1.0/OmxStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
namespace android {
namespace hardware {
@@ -29,24 +30,87 @@
namespace V1_0 {
namespace implementation {
-OmxStore::OmxStore() {
+OmxStore::OmxStore(
+ const char* owner,
+ const char* const* searchDirs,
+ const char* mainXmlName,
+ const char* performanceXmlName,
+ const char* profilingResultsXmlPath) {
+ MediaCodecsXmlParser parser(searchDirs,
+ mainXmlName,
+ performanceXmlName,
+ profilingResultsXmlPath);
+ mParsingStatus = toStatus(parser.getParsingStatus());
+
+ const auto& serviceAttributeMap = parser.getServiceAttributeMap();
+ mServiceAttributeList.resize(serviceAttributeMap.size());
+ size_t i = 0;
+ for (const auto& attributePair : serviceAttributeMap) {
+ ServiceAttribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ mServiceAttributeList[i] = std::move(attribute);
+ ++i;
+ }
+
+ const auto& roleMap = parser.getRoleMap();
+ mRoleList.resize(roleMap.size());
+ i = 0;
+ for (const auto& rolePair : roleMap) {
+ RoleInfo role;
+ role.role = rolePair.first;
+ role.type = rolePair.second.type;
+ role.isEncoder = rolePair.second.isEncoder;
+ // TODO: Currently, preferPlatformNodes information is not available in
+ // the xml file. Once we have a way to provide this information, it
+ // should be parsed properly.
+ role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+ hidl_vec<NodeInfo>& nodeList = role.nodes;
+ nodeList.resize(rolePair.second.nodeList.size());
+ size_t j = 0;
+ for (const auto& nodePair : rolePair.second.nodeList) {
+ NodeInfo node;
+ node.name = nodePair.second.name;
+ node.owner = owner;
+ hidl_vec<NodeAttribute>& attributeList = node.attributes;
+ attributeList.resize(nodePair.second.attributeList.size());
+ size_t k = 0;
+ for (const auto& attributePair : nodePair.second.attributeList) {
+ NodeAttribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ attributeList[k] = std::move(attribute);
+ ++k;
+ }
+ nodeList[j] = std::move(node);
+ ++j;
+ }
+ mRoleList[i] = std::move(role);
+ ++i;
+ }
+
+ mPrefix = parser.getCommonPrefix();
}
OmxStore::~OmxStore() {
}
Return<void> OmxStore::listServiceAttributes(listServiceAttributes_cb _hidl_cb) {
- _hidl_cb(toStatus(NO_ERROR), hidl_vec<ServiceAttribute>());
+ if (mParsingStatus == Status::NO_ERROR) {
+ _hidl_cb(Status::NO_ERROR, mServiceAttributeList);
+ } else {
+ _hidl_cb(mParsingStatus, hidl_vec<ServiceAttribute>());
+ }
return Void();
}
Return<void> OmxStore::getNodePrefix(getNodePrefix_cb _hidl_cb) {
- _hidl_cb(hidl_string());
+ _hidl_cb(mPrefix);
return Void();
}
Return<void> OmxStore::listRoles(listRoles_cb _hidl_cb) {
- _hidl_cb(hidl_vec<RoleInfo>());
+ _hidl_cb(mRoleList);
return Void();
}
@@ -54,12 +118,6 @@
return IOmx::tryGetService(omxName);
}
-// Methods from ::android::hidl::base::V1_0::IBase follow.
-
-IOmxStore* HIDL_FETCH_IOmxStore(const char* /* name */) {
- return new OmxStore();
-}
-
} // namespace implementation
} // namespace V1_0
} // namespace omx
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index fcf1092..c4499dc 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -41,7 +41,9 @@
sp<GraphicBuffer> buf;
status_t status = mBase->requestBuffer(slot, &buf);
AnwBuffer anwBuffer;
- wrapAs(&anwBuffer, *buf);
+ if (buf != nullptr) {
+ wrapAs(&anwBuffer, *buf);
+ }
_hidl_cb(static_cast<int32_t>(status), anwBuffer);
return Void();
}
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index bb05740..d4cdf69 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -10,6 +10,7 @@
"GraphicBufferSource.cpp",
"BWGraphicBufferSource.cpp",
"OMX.cpp",
+ "OMXStore.cpp",
"OMXMaster.cpp",
"OMXNodeInstance.cpp",
"OMXUtils.cpp",
@@ -32,15 +33,6 @@
"include",
],
- include_dirs: [
- "frameworks/av/include/media/",
- "frameworks/av/media/libstagefright",
- "frameworks/av/media/libstagefright/include",
- "frameworks/native/include", // for media/hardware/MetadataBufferType.h
- "frameworks/native/include/media/hardware",
- "frameworks/native/include/media/openmax",
- ],
-
header_libs: [
"media_plugin_headers",
],
@@ -99,8 +91,12 @@
},
}
-cc_library_static {
+cc_library_shared {
name: "libstagefright_omx_utils",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["OMXUtils.cpp"],
export_include_dirs: [
"include",
@@ -112,10 +108,11 @@
"media_plugin_headers",
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
+ "liblog",
],
export_shared_lib_headers: [
- "libmedia",
+ "libmedia_omx",
],
sanitize: {
misc_undefined: [
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 93b4dbe..09c4019 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -115,15 +115,22 @@
return StatusFromOMXError(err);
}
instance->setHandle(handle);
- std::vector<AString> quirkVector;
- if (mParser.getQuirks(name, &quirkVector) == OK) {
+
+ // Find quirks from mParser
+ const auto& codec = mParser.getCodecMap().find(name);
+ if (codec == mParser.getCodecMap().cend()) {
+ ALOGW("Failed to obtain quirks for omx component '%s' from XML files",
+ name);
+ } else {
uint32_t quirks = 0;
- for (const AString quirk : quirkVector) {
+ for (const auto& quirk : codec->second.quirkSet) {
if (quirk == "requires-allocate-on-input-ports") {
- quirks |= kRequiresAllocateBufferOnInputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnInputPorts;
}
if (quirk == "requires-allocate-on-output-ports") {
- quirks |= kRequiresAllocateBufferOnOutputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnOutputPorts;
}
}
instance->setQuirks(quirks);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index c749454..015a148 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -372,6 +372,8 @@
mPortMode[1] = IOMX::kPortModePresetByteBuffer;
mSecureBufferType[0] = kSecureBufferTypeUnknown;
mSecureBufferType[1] = kSecureBufferTypeUnknown;
+ mGraphicBufferEnabled[0] = false;
+ mGraphicBufferEnabled[1] = false;
mIsSecure = AString(name).endsWith(".secure");
mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled("legacy-adaptive");
}
@@ -495,6 +497,9 @@
LOG_ALWAYS_FATAL("unknown state %s(%#x).", asString(state), state);
break;
}
+
+ Mutex::Autolock _l(mLock);
+
status_t err = mOwner->freeNode(this);
mDispatcher.clear();
@@ -674,6 +679,11 @@
return BAD_VALUE;
}
+ if (mSailed || mNumPortBuffers[portIndex] > 0) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return INVALID_OPERATION;
+ }
+
CLOG_CONFIG(setPortMode, "%s(%d), port %d", asString(mode), mode, portIndex);
switch (mode) {
@@ -805,6 +815,12 @@
} else if (mSecureBufferType[portIndex] == kSecureBufferTypeUnknown) {
mSecureBufferType[portIndex] = kSecureBufferTypeOpaque;
}
+ } else {
+ if (err == OMX_ErrorNone) {
+ mGraphicBufferEnabled[portIndex] = enable;
+ } else if (enable) {
+ mGraphicBufferEnabled[portIndex] = false;
+ }
}
} else {
CLOG_ERROR_IF(enable, getExtensionIndex, err, "%s", name);
@@ -1073,6 +1089,12 @@
OMX_ERRORTYPE err = OMX_ErrorNone;
bool isMetadata = mMetadataType[portIndex] != kMetadataBufferTypeInvalid;
+ if (!isMetadata && mGraphicBufferEnabled[portIndex]) {
+ ALOGE("b/62948670");
+ android_errorWriteLog(0x534e4554, "62948670");
+ return INVALID_OPERATION;
+ }
+
size_t paramsSize;
void* paramsPointer;
if (params != NULL && hParams != NULL) {
@@ -1258,6 +1280,13 @@
portIndex, graphicBuffer, buffer);
}
+ if (!mGraphicBufferEnabled[portIndex]) {
+ // Report error if this is not in graphic buffer mode.
+ ALOGE("b/62948670");
+ android_errorWriteLog(0x534e4554, "62948670");
+ return INVALID_OPERATION;
+ }
+
// See if the newer version of the extension is present.
OMX_INDEXTYPE index;
if (OMX_GetExtensionIndex(
diff --git a/media/libstagefright/omx/OMXStore.cpp b/media/libstagefright/omx/OMXStore.cpp
new file mode 100644
index 0000000..345336d
--- /dev/null
+++ b/media/libstagefright/omx/OMXStore.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXStore"
+#include <utils/Log.h>
+
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/omx/OMX.h>
+#include <media/stagefright/omx/OMXStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#include <map>
+#include <string>
+
+namespace android {
+
+namespace {
+ struct RoleProperties {
+ std::string type;
+ bool isEncoder;
+ bool preferPlatformNodes;
+ std::multimap<size_t, IOMXStore::NodeInfo> nodeList;
+ };
+} // Unnamed namespace
+
+OMXStore::OMXStore(
+ const char* owner,
+ const char* const* searchDirs,
+ const char* mainXmlName,
+ const char* performanceXmlName,
+ const char* profilingResultsXmlPath) {
+ MediaCodecsXmlParser parser(
+ searchDirs,
+ mainXmlName,
+ performanceXmlName,
+ profilingResultsXmlPath);
+ mParsingStatus = parser.getParsingStatus();
+
+ const auto& serviceAttributeMap = parser.getServiceAttributeMap();
+ mServiceAttributeList.reserve(serviceAttributeMap.size());
+ for (const auto& attributePair : serviceAttributeMap) {
+ Attribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ mServiceAttributeList.push_back(std::move(attribute));
+ }
+
+ const auto& roleMap = parser.getRoleMap();
+ mRoleList.reserve(roleMap.size());
+ for (const auto& rolePair : roleMap) {
+ RoleInfo role;
+ role.role = rolePair.first;
+ role.type = rolePair.second.type;
+ role.isEncoder = rolePair.second.isEncoder;
+ // TODO: Currently, preferPlatformNodes information is not available in
+ // the xml file. Once we have a way to provide this information, it
+ // should be parsed properly.
+ role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+ std::vector<NodeInfo>& nodeList = role.nodes;
+ nodeList.reserve(rolePair.second.nodeList.size());
+ for (const auto& nodePair : rolePair.second.nodeList) {
+ NodeInfo node;
+ node.name = nodePair.second.name;
+ node.owner = owner;
+ std::vector<Attribute>& attributeList = node.attributes;
+ attributeList.reserve(nodePair.second.attributeList.size());
+ for (const auto& attributePair : nodePair.second.attributeList) {
+ Attribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ attributeList.push_back(std::move(attribute));
+ }
+ nodeList.push_back(std::move(node));
+ }
+ mRoleList.push_back(std::move(role));
+ }
+
+ mPrefix = parser.getCommonPrefix();
+}
+
+status_t OMXStore::listServiceAttributes(std::vector<Attribute>* attributes) {
+ *attributes = mServiceAttributeList;
+ return mParsingStatus;
+}
+
+status_t OMXStore::getNodePrefix(std::string* prefix) {
+ *prefix = mPrefix;
+ return mParsingStatus;
+}
+
+status_t OMXStore::listRoles(std::vector<RoleInfo>* roleList) {
+ *roleList = mRoleList;
+ return mParsingStatus;
+}
+
+status_t OMXStore::getOmx(const std::string& name, sp<IOMX>* omx) {
+ *omx = new OMX();
+ return NO_ERROR;
+}
+
+OMXStore::~OMXStore() {
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index f319bdc..8d8a2d9 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -2064,8 +2064,10 @@
int const* constFds = static_cast<int const*>(baseFds.get());
numFds = baseNumFds;
if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
- native_handle_close(nh);
- native_handle_delete(nh);
+ if (nh != nullptr) {
+ native_handle_close(nh);
+ native_handle_delete(nh);
+ }
return false;
}
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
index f377f5a..006d2d9 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
@@ -21,6 +21,7 @@
#include <hidl/Status.h>
#include <android/hardware/media/omx/1.0/IOmxStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
namespace android {
namespace hardware {
@@ -41,17 +42,31 @@
using ::android::wp;
struct OmxStore : public IOmxStore {
- OmxStore();
+ OmxStore(
+ const char* owner = "default",
+ const char* const* searchDirs
+ = MediaCodecsXmlParser::defaultSearchDirs,
+ const char* mainXmlName
+ = MediaCodecsXmlParser::defaultMainXmlName,
+ const char* performanceXmlName
+ = MediaCodecsXmlParser::defaultPerformanceXmlName,
+ const char* profilingResultsXmlPath
+ = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
+
virtual ~OmxStore();
- // Methods from IOmx
+ // Methods from IOmxStore
Return<void> listServiceAttributes(listServiceAttributes_cb) override;
Return<void> getNodePrefix(getNodePrefix_cb) override;
Return<void> listRoles(listRoles_cb) override;
Return<sp<IOmx>> getOmx(hidl_string const&) override;
-};
-extern "C" IOmxStore* HIDL_FETCH_IOmxStore(const char* name);
+protected:
+ Status mParsingStatus;
+ hidl_string mPrefix;
+ hidl_vec<ServiceAttribute> mServiceAttributeList;
+ hidl_vec<RoleInfo> mRoleList;
+};
} // namespace implementation
} // namespace V1_0
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
index 29b51a8..0f2d9a6 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
@@ -22,7 +22,7 @@
#include <gui/BufferQueue.h>
#include <utils/RefBase.h>
-#include <VideoAPI.h>
+#include <media/hardware/VideoAPI.h>
#include <media/IOMX.h>
#include <media/OMXFenceParcelable.h>
#include <media/stagefright/foundation/ABase.h>
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
index 3f9c0ca..897f287 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
@@ -18,7 +18,7 @@
#define OMX_MASTER_H_
-#include <OMXPluginBase.h>
+#include <media/hardware/OMXPluginBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
index 8e08d15..1065ca5 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
@@ -92,6 +92,15 @@
status_t getExtensionIndex(
const char *parameterName, OMX_INDEXTYPE *index);
+ // Quirk still supported, even though deprecated
+ enum Quirks {
+ kRequiresAllocateBufferOnInputPorts = 1,
+ kRequiresAllocateBufferOnOutputPorts = 2,
+
+ kQuirksMask = kRequiresAllocateBufferOnInputPorts
+ | kRequiresAllocateBufferOnOutputPorts,
+ };
+
status_t setQuirks(OMX_U32 quirks);
bool isSecure() const {
@@ -143,7 +152,7 @@
bool mLegacyAdaptiveExperiment;
IOMX::PortMode mPortMode[2];
- // metadata and secure buffer type tracking
+ // metadata and secure buffer types and graphic buffer mode tracking
MetadataBufferType mMetadataType[2];
enum SecureBufferType {
kSecureBufferTypeUnknown,
@@ -151,6 +160,7 @@
kSecureBufferTypeNativeHandle,
};
SecureBufferType mSecureBufferType[2];
+ bool mGraphicBufferEnabled[2];
// Following are OMX parameters managed by us (instead of the component)
// OMX_IndexParamMaxFrameDurationForBitrateControl
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
new file mode 100644
index 0000000..e00d713
--- /dev/null
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OMXSTORE_H_
+#define ANDROID_OMXSTORE_H_
+
+#include <media/IOMXStore.h>
+#include <media/IOMX.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#include <vector>
+#include <string>
+
+namespace android {
+
+class OMXStore : public BnOMXStore {
+public:
+ OMXStore(
+ const char* owner = "default",
+ const char* const* searchDirs
+ = MediaCodecsXmlParser::defaultSearchDirs,
+ const char* mainXmlName
+ = MediaCodecsXmlParser::defaultMainXmlName,
+ const char* performanceXmlName
+ = MediaCodecsXmlParser::defaultPerformanceXmlName,
+ const char* profilingResultsXmlPath
+ = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
+
+ status_t listServiceAttributes(
+ std::vector<Attribute>* attributes) override;
+
+ status_t getNodePrefix(std::string* prefix) override;
+
+ status_t listRoles(std::vector<RoleInfo>* roleList) override;
+
+ status_t getOmx(const std::string& name, sp<IOMX>* omx) override;
+
+ ~OMXStore() override;
+
+protected:
+ status_t mParsingStatus;
+ std::string mPrefix;
+ std::vector<Attribute> mServiceAttributeList;
+ std::vector<RoleInfo> mRoleList;
+};
+
+} // namespace android
+
+#endif // ANDROID_OMXSTORE_H_
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
index c89cd87..8ec717e 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
@@ -19,7 +19,7 @@
#define SOFT_OMX_PLUGIN_H_
#include <media/stagefright/foundation/ABase.h>
-#include <OMXPluginBase.h>
+#include <media/hardware/OMXPluginBase.h>
namespace android {
diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
index 2599608..0c22a42 100644
--- a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
+++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
@@ -26,6 +26,8 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodecList.h>
+#include <vector>
+
namespace android {
static const char kTestOverridesStr[] =
@@ -117,7 +119,7 @@
// 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;
+ std::vector<sp<MediaCodecInfo>> infos;
for (size_t i = 0; i < list->countCodecs(); ++i) {
infos.push_back(list->getCodecInfo(i));
}
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index ab893de..3507284 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -9,11 +9,6 @@
"MediaCodecsXmlParser.cpp",
],
- include_dirs: [
- "frameworks/av/media/libstagefright",
- "frameworks/av/include",
- ],
-
export_include_dirs: [
"include",
],
@@ -24,6 +19,7 @@
"liblog",
"libcutils",
"libstagefright_foundation",
+ "libstagefright_omx_utils",
],
cflags: [
@@ -38,6 +34,10 @@
"unsigned-integer-overflow",
"signed-integer-overflow",
],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
},
}
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 4fdd107..ffd30ea 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -16,157 +16,190 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaCodecsXmlParser"
-#include <utils/Log.h>
#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
-#include <media/MediaCodecInfo.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/AUtils.h>
+#include <utils/Log.h>
#include <media/stagefright/MediaErrors.h>
-
+#include <media/stagefright/omx/OMXUtils.h>
#include <sys/stat.h>
-
#include <expat.h>
-#include <string>
-#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 256
+#include <cctype>
+#include <algorithm>
namespace android {
-namespace { // Local variables and functions
+namespace {
-const char *kProfilingResults =
- "/data/misc/media/media_codecs_profiling_results.xml";
-
-// Treblized media codec list will be located in /odm/etc or /vendor/etc.
-const char *kConfigLocationList[] =
- {"/odm/etc", "/vendor/etc", "/etc"};
-constexpr int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
-bool findMediaCodecListFileFullPath(
- const char *file_name, std::string *out_path) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
- *out_path = std::string(kConfigLocationList[i]) + "/" + file_name;
- struct stat file_stat;
- if (stat(out_path->c_str(), &file_stat) == 0 &&
- S_ISREG(file_stat.st_mode)) {
+/**
+ * Search for a file in a list of search directories.
+ *
+ * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
+ * tested whether it is a valid file name or not. If it is a valid file name,
+ * the concatenated name (`searchDir/fileName`) will be stored in the output
+ * variable `outPath`, and the function will return `true`. Otherwise, the
+ * search continues until the `nullptr` element in `searchDirs` is reached, at
+ * which point the function returns `false`.
+ *
+ * \param[in] searchDirs Null-terminated array of search paths.
+ * \param[in] fileName Name of the file to search.
+ * \param[out] outPath Full path of the file. `outPath` will hold a valid file
+ * name if the return value of this function is `true`.
+ * \return `true` if some element in `searchDirs` combined with `fileName` is a
+ * valid file name; `false` otherwise.
+ */
+bool findFileInDirs(
+ const char* const* searchDirs,
+ const char *fileName,
+ std::string *outPath) {
+ for (; *searchDirs != nullptr; ++searchDirs) {
+ *outPath = std::string(*searchDirs) + "/" + fileName;
+ struct stat fileStat;
+ if (stat(outPath->c_str(), &fileStat) == 0 &&
+ S_ISREG(fileStat.st_mode)) {
return true;
}
}
return false;
}
-// Find TypeInfo by name.
-std::vector<TypeInfo>::iterator findTypeInfo(
- CodecInfo &codecInfo, const AString &typeName) {
- return std::find_if(
- codecInfo.mTypes.begin(), codecInfo.mTypes.end(),
- [typeName](const auto &typeInfo) {
- return typeInfo.mName == typeName;
- });
+bool strnEq(const char* s1, const char* s2, size_t count) {
+ return strncmp(s1, s2, count) == 0;
}
-// Convert a string into a boolean value.
-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;
+bool strEq(const char* s1, const char* s2) {
+ return strcmp(s1, s2) == 0;
}
-} // unnamed namespace
+bool striEq(const char* s1, const char* s2) {
+ return strcasecmp(s1, s2) == 0;
+}
-MediaCodecsXmlParser::MediaCodecsXmlParser() :
- mInitCheck(NO_INIT),
- mUpdate(false) {
- std::string config_file_path;
- if (findMediaCodecListFileFullPath(
- "media_codecs.xml", &config_file_path)) {
- parseTopLevelXMLFile(config_file_path.c_str(), false);
+bool strHasPrefix(const char* test, const char* prefix) {
+ return strnEq(test, prefix, strlen(prefix));
+}
+
+bool parseBoolean(const char* s) {
+ return striEq(s, "y") ||
+ striEq(s, "yes") ||
+ striEq(s, "t") ||
+ striEq(s, "true") ||
+ striEq(s, "1");
+}
+
+status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) {
+ ALOGE("limit '%s' with %s'%s' attribute", name,
+ (found ? "" : "no "), attr);
+ return -EINVAL;
+}
+
+status_t limitError(const char* name, const char *msg) {
+ ALOGE("limit '%s' %s", name, msg);
+ return -EINVAL;
+}
+
+status_t limitInvalidAttr(const char* name, const char* attr, const char* value) {
+ ALOGE("limit '%s' with invalid '%s' attribute (%s)", name,
+ attr, value);
+ return -EINVAL;
+}
+
+}; // unnamed namespace
+
+constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[];
+constexpr char const* MediaCodecsXmlParser::defaultMainXmlName;
+constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName;
+constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
+
+MediaCodecsXmlParser::MediaCodecsXmlParser(
+ const char* const* searchDirs,
+ const char* mainXmlName,
+ const char* performanceXmlName,
+ const char* profilingResultsXmlPath) :
+ mParsingStatus(NO_INIT),
+ mUpdate(false),
+ mCodecCounter(0) {
+ std::string path;
+ if (findFileInDirs(searchDirs, mainXmlName, &path)) {
+ parseTopLevelXMLFile(path.c_str(), false);
} else {
- mInitCheck = NAME_NOT_FOUND;
+ ALOGE("Cannot find %s", mainXmlName);
+ mParsingStatus = NAME_NOT_FOUND;
}
- if (findMediaCodecListFileFullPath(
- "media_codecs_performance.xml", &config_file_path)) {
- parseTopLevelXMLFile(config_file_path.c_str(), true);
+ if (findFileInDirs(searchDirs, performanceXmlName, &path)) {
+ parseTopLevelXMLFile(path.c_str(), true);
}
- parseTopLevelXMLFile(kProfilingResults, true);
+ if (profilingResultsXmlPath != nullptr) {
+ parseTopLevelXMLFile(profilingResultsXmlPath, true);
+ }
}
-void MediaCodecsXmlParser::parseTopLevelXMLFile(
- const char *codecs_xml, bool ignore_errors) {
+bool MediaCodecsXmlParser::parseTopLevelXMLFile(
+ const char *codecs_xml,
+ bool ignore_errors) {
// get href_base
const char *href_base_end = strrchr(codecs_xml, '/');
- if (href_base_end != NULL) {
- mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
+ if (href_base_end != nullptr) {
+ mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1);
}
- mInitCheck = OK; // keeping this here for safety
+ mParsingStatus = OK; // keeping this here for safety
mCurrentSection = SECTION_TOPLEVEL;
- mDepth = 0;
parseXMLFile(codecs_xml);
- if (mInitCheck != OK) {
+ if (mParsingStatus != OK) {
+ ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml);
if (ignore_errors) {
- mInitCheck = OK;
- return;
+ mParsingStatus = OK;
+ return false;
}
- mCodecInfos.clear();
- return;
+ mCodecMap.clear();
+ return false;
}
+ return true;
}
MediaCodecsXmlParser::~MediaCodecsXmlParser() {
}
-status_t MediaCodecsXmlParser::initCheck() const {
- return mInitCheck;
-}
-
void MediaCodecsXmlParser::parseXMLFile(const char *path) {
FILE *file = fopen(path, "r");
- if (file == NULL) {
+ if (file == nullptr) {
ALOGW("unable to open media codecs configuration xml file: %s", path);
- mInitCheck = NAME_NOT_FOUND;
+ mParsingStatus = NAME_NOT_FOUND;
return;
}
- ALOGV("Start parsing %s", path);
- XML_Parser parser = ::XML_ParserCreate(NULL);
- CHECK(parser != NULL);
+ XML_Parser parser = ::XML_ParserCreate(nullptr);
+ LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed.");
::XML_SetUserData(parser, this);
::XML_SetElementHandler(
parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
- const int BUFF_SIZE = 512;
- while (mInitCheck == OK) {
+ static constexpr int BUFF_SIZE = 512;
+ while (mParsingStatus == OK) {
void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
- if (buff == NULL) {
+ if (buff == nullptr) {
ALOGE("failed in call to XML_GetBuffer()");
- mInitCheck = UNKNOWN_ERROR;
+ mParsingStatus = UNKNOWN_ERROR;
break;
}
int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
if (bytes_read < 0) {
ALOGE("failed in call to read");
- mInitCheck = ERROR_IO;
+ mParsingStatus = ERROR_IO;
break;
}
XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
if (status != XML_STATUS_OK) {
ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
- mInitCheck = ERROR_MALFORMED;
+ mParsingStatus = ERROR_MALFORMED;
break;
}
@@ -178,30 +211,29 @@
::XML_ParserFree(parser);
fclose(file);
- file = NULL;
+ file = nullptr;
}
// static
void MediaCodecsXmlParser::StartElementHandlerWrapper(
void *me, const char *name, const char **attrs) {
- static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs);
+ static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs);
}
// static
void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
- static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name);
+ static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name);
}
status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
- const char *href = NULL;
+ const char *href = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "href")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "href")) {
+ if (attrs[++i] == nullptr) {
return -EINVAL;
}
- href = attrs[i + 1];
- ++i;
+ href = attrs[i];
} else {
ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
@@ -222,44 +254,43 @@
return -EINVAL;
}
- AString filename = href;
- if (!filename.startsWith("media_codecs_") ||
- !filename.endsWith(".xml")) {
+ std::string filename = href;
+ if (filename.compare(0, 13, "media_codecs_") != 0 ||
+ filename.compare(filename.size() - 4, 4, ".xml") != 0) {
ALOGE("invalid include file name: %s", href);
return -EINVAL;
}
- filename.insert(mHrefBase, 0);
+ filename.insert(0, mHrefBase);
parseXMLFile(filename.c_str());
- return mInitCheck;
+ return mParsingStatus;
}
void MediaCodecsXmlParser::startElementHandler(
const char *name, const char **attrs) {
- if (mInitCheck != OK) {
+ if (mParsingStatus != OK) {
return;
}
bool inType = true;
- if (!strcmp(name, "Include")) {
- mInitCheck = includeXMLFile(attrs);
- if (mInitCheck == OK) {
- mPastSections.push(mCurrentSection);
+ if (strEq(name, "Include")) {
+ mParsingStatus = includeXMLFile(attrs);
+ if (mParsingStatus == OK) {
+ mSectionStack.push_back(mCurrentSection);
mCurrentSection = SECTION_INCLUDE;
}
- ++mDepth;
return;
}
switch (mCurrentSection) {
case SECTION_TOPLEVEL:
{
- if (!strcmp(name, "Decoders")) {
+ if (strEq(name, "Decoders")) {
mCurrentSection = SECTION_DECODERS;
- } else if (!strcmp(name, "Encoders")) {
+ } else if (strEq(name, "Encoders")) {
mCurrentSection = SECTION_ENCODERS;
- } else if (!strcmp(name, "Settings")) {
+ } else if (strEq(name, "Settings")) {
mCurrentSection = SECTION_SETTINGS;
}
break;
@@ -267,16 +298,16 @@
case SECTION_SETTINGS:
{
- if (!strcmp(name, "Setting")) {
- mInitCheck = addSettingFromAttributes(attrs);
+ if (strEq(name, "Setting")) {
+ mParsingStatus = addSettingFromAttributes(attrs);
}
break;
}
case SECTION_DECODERS:
{
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
+ if (strEq(name, "MediaCodec")) {
+ mParsingStatus =
addMediaCodecFromAttributes(false /* encoder */, attrs);
mCurrentSection = SECTION_DECODER;
@@ -286,8 +317,8 @@
case SECTION_ENCODERS:
{
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
+ if (strEq(name, "MediaCodec")) {
+ mParsingStatus =
addMediaCodecFromAttributes(true /* encoder */, attrs);
mCurrentSection = SECTION_ENCODER;
@@ -298,13 +329,14 @@
case SECTION_DECODER:
case SECTION_ENCODER:
{
- if (!strcmp(name, "Quirk")) {
- mInitCheck = addQuirk(attrs);
- } else if (!strcmp(name, "Type")) {
- mInitCheck = addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER));
+ if (strEq(name, "Quirk")) {
+ mParsingStatus = addQuirk(attrs);
+ } else if (strEq(name, "Type")) {
+ mParsingStatus = addTypeFromAttributes(attrs,
+ (mCurrentSection == SECTION_ENCODER));
mCurrentSection =
- (mCurrentSection == SECTION_DECODER
- ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
+ (mCurrentSection == SECTION_DECODER ?
+ SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
}
}
inType = false;
@@ -314,13 +346,15 @@
case SECTION_ENCODER_TYPE:
{
// ignore limits and features specified outside of type
- bool outside = !inType && mCurrentType == mCodecInfos[mCurrentName].mTypes.end();
- if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
+ bool outside = !inType &&
+ mCurrentType == mCurrentCodec->second.typeMap.end();
+ if (outside &&
+ (strEq(name, "Limit") || strEq(name, "Feature"))) {
ALOGW("ignoring %s specified outside of a Type", name);
- } else if (!strcmp(name, "Limit")) {
- mInitCheck = addLimit(attrs);
- } else if (!strcmp(name, "Feature")) {
- mInitCheck = addFeature(attrs);
+ } else if (strEq(name, "Limit")) {
+ mParsingStatus = addLimit(attrs);
+ } else if (strEq(name, "Feature")) {
+ mParsingStatus = addFeature(attrs);
}
break;
}
@@ -329,18 +363,17 @@
break;
}
- ++mDepth;
}
void MediaCodecsXmlParser::endElementHandler(const char *name) {
- if (mInitCheck != OK) {
+ if (mParsingStatus != OK) {
return;
}
switch (mCurrentSection) {
case SECTION_SETTINGS:
{
- if (!strcmp(name, "Settings")) {
+ if (strEq(name, "Settings")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
@@ -348,7 +381,7 @@
case SECTION_DECODERS:
{
- if (!strcmp(name, "Decoders")) {
+ if (strEq(name, "Decoders")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
@@ -356,7 +389,7 @@
case SECTION_ENCODERS:
{
- if (!strcmp(name, "Encoders")) {
+ if (strEq(name, "Encoders")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
@@ -365,19 +398,19 @@
case SECTION_DECODER_TYPE:
case SECTION_ENCODER_TYPE:
{
- if (!strcmp(name, "Type")) {
+ if (strEq(name, "Type")) {
mCurrentSection =
- (mCurrentSection == SECTION_DECODER_TYPE
- ? SECTION_DECODER : SECTION_ENCODER);
+ (mCurrentSection == SECTION_DECODER_TYPE ?
+ SECTION_DECODER : SECTION_ENCODER);
- mCurrentType = mCodecInfos[mCurrentName].mTypes.end();
+ mCurrentType = mCurrentCodec->second.typeMap.end();
}
break;
}
case SECTION_DECODER:
{
- if (!strcmp(name, "MediaCodec")) {
+ if (strEq(name, "MediaCodec")) {
mCurrentSection = SECTION_DECODERS;
mCurrentName.clear();
}
@@ -386,7 +419,7 @@
case SECTION_ENCODER:
{
- if (!strcmp(name, "MediaCodec")) {
+ if (strEq(name, "MediaCodec")) {
mCurrentSection = SECTION_ENCODERS;
mCurrentName.clear();
}
@@ -395,9 +428,9 @@
case SECTION_INCLUDE:
{
- if (!strcmp(name, "Include") && mPastSections.size() > 0) {
- mCurrentSection = mPastSections.top();
- mPastSections.pop();
+ if (strEq(name, "Include") && (mSectionStack.size() > 0)) {
+ mCurrentSection = mSectionStack.back();
+ mSectionStack.pop_back();
}
break;
}
@@ -406,253 +439,282 @@
break;
}
- --mDepth;
}
status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
- const char *name = NULL;
- const char *value = NULL;
- const char *update = NULL;
+ const char *name = nullptr;
+ const char *value = nullptr;
+ const char *update = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addSettingFromAttributes: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "value")) {
- if (attrs[i + 1] == NULL) {
+ name = attrs[i];
+ } else if (strEq(attrs[i], "value")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addSettingFromAttributes: value is null");
return -EINVAL;
}
- value = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
+ value = attrs[i];
+ } else if (strEq(attrs[i], "update")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addSettingFromAttributes: update is null");
return -EINVAL;
}
- update = attrs[i + 1];
- ++i;
+ update = attrs[i];
} else {
ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL || value == NULL) {
+ if (name == nullptr || value == nullptr) {
ALOGE("addSettingFromAttributes: name or value unspecified");
return -EINVAL;
}
- mUpdate = (update != NULL) && ParseBoolean(update);
- if (mUpdate != (mGlobalSettings.count(name) > 0)) {
- ALOGE("addSettingFromAttributes: updating non-existing setting");
- return -EINVAL;
+ // Boolean values are converted to "0" or "1".
+ if (strHasPrefix(name, "supports-")) {
+ value = parseBoolean(value) ? "1" : "0";
}
- mGlobalSettings[name] = value;
+
+ mUpdate = (update != nullptr) && parseBoolean(update);
+ auto attribute = mServiceAttributeMap.find(name);
+ if (attribute == mServiceAttributeMap.end()) { // New attribute name
+ if (mUpdate) {
+ ALOGE("addSettingFromAttributes: updating non-existing setting");
+ return -EINVAL;
+ }
+ mServiceAttributeMap.insert(Attribute(name, value));
+ } else { // Existing attribute name
+ if (!mUpdate) {
+ ALOGE("addSettingFromAttributes: adding existing setting");
+ }
+ attribute->second = value;
+ }
return OK;
}
status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
bool encoder, const char **attrs) {
- const char *name = NULL;
- const char *type = NULL;
- const char *update = NULL;
+ const char *name = nullptr;
+ const char *type = nullptr;
+ const char *update = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addMediaCodecFromAttributes: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "type")) {
- if (attrs[i + 1] == NULL) {
+ name = attrs[i];
+ } else if (strEq(attrs[i], "type")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addMediaCodecFromAttributes: type is null");
return -EINVAL;
}
- type = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
+ type = attrs[i];
+ } else if (strEq(attrs[i], "update")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addMediaCodecFromAttributes: update is null");
return -EINVAL;
}
- update = attrs[i + 1];
- ++i;
+ update = attrs[i];
} else {
ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL) {
+ if (name == nullptr) {
ALOGE("addMediaCodecFromAttributes: name not found");
return -EINVAL;
}
- mUpdate = (update != NULL) && ParseBoolean(update);
- if (mUpdate != (mCodecInfos.count(name) > 0)) {
- ALOGE("addMediaCodecFromAttributes: updating non-existing codec or vice versa");
- return -EINVAL;
- }
-
- CodecInfo *info = &mCodecInfos[name];
- if (mUpdate) {
- // existing codec
- mCurrentName = name;
- mCurrentType = info->mTypes.begin();
- if (type != NULL) {
- // existing type
- mCurrentType = findTypeInfo(*info, type);
- if (mCurrentType == info->mTypes.end()) {
+ mUpdate = (update != nullptr) && parseBoolean(update);
+ mCurrentCodec = mCodecMap.find(name);
+ if (mCurrentCodec == mCodecMap.end()) { // New codec name
+ if (mUpdate) {
+ ALOGE("addMediaCodecFromAttributes: updating non-existing codec");
+ return -EINVAL;
+ }
+ // Create a new codec in mCodecMap
+ mCurrentCodec = mCodecMap.insert(
+ Codec(name, CodecProperties())).first;
+ if (type != nullptr) {
+ mCurrentType = mCurrentCodec->second.typeMap.insert(
+ Type(type, AttributeMap())).first;
+ } else {
+ mCurrentType = mCurrentCodec->second.typeMap.end();
+ }
+ mCurrentCodec->second.isEncoder = encoder;
+ mCurrentCodec->second.order = mCodecCounter++;
+ } else { // Existing codec name
+ if (!mUpdate) {
+ ALOGE("addMediaCodecFromAttributes: adding existing codec");
+ return -EINVAL;
+ }
+ if (type != nullptr) {
+ mCurrentType = mCurrentCodec->second.typeMap.find(type);
+ if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGE("addMediaCodecFromAttributes: updating non-existing type");
return -EINVAL;
}
+ } else {
+ // This should happen only when the codec has at most one type.
+ mCurrentType = mCurrentCodec->second.typeMap.begin();
}
- } else {
- // new codec
- mCurrentName = name;
- mQuirks[name].clear();
- info->mTypes.clear();
- info->mTypes.emplace_back();
- mCurrentType = --info->mTypes.end();
- mCurrentType->mName = type;
- info->mIsEncoder = encoder;
}
return OK;
}
status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
- const char *name = NULL;
+ const char *name = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addQuirk: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
+ name = attrs[i];
} else {
ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL) {
+ if (name == nullptr) {
ALOGE("addQuirk: name not found");
return -EINVAL;
}
- mQuirks[mCurrentName].emplace_back(name);
+ mCurrentCodec->second.quirkSet.emplace(name);
return OK;
}
status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
- const char *name = NULL;
- const char *update = NULL;
+ const char *name = nullptr;
+ const char *update = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addTypeFromAttributes: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
+ name = attrs[i];
+ } else if (strEq(attrs[i], "update")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addTypeFromAttributes: update is null");
return -EINVAL;
}
- update = attrs[i + 1];
- ++i;
+ update = attrs[i];
} else {
ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL) {
+ if (name == nullptr) {
return -EINVAL;
}
- CodecInfo *info = &mCodecInfos[mCurrentName];
- info->mIsEncoder = encoder;
- mCurrentType = findTypeInfo(*info, name);
+ mCurrentCodec->second.isEncoder = encoder;
+ mCurrentType = mCurrentCodec->second.typeMap.find(name);
if (!mUpdate) {
- if (mCurrentType != info->mTypes.end()) {
+ if (mCurrentType != mCurrentCodec->second.typeMap.end()) {
ALOGE("addTypeFromAttributes: re-defining existing type without update");
return -EINVAL;
}
- info->mTypes.emplace_back();
- mCurrentType = --info->mTypes.end();
- } else if (mCurrentType == info->mTypes.end()) {
+ mCurrentType = mCurrentCodec->second.typeMap.insert(
+ Type(name, AttributeMap())).first;
+ } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGE("addTypeFromAttributes: updating non-existing type");
- return -EINVAL;
}
-
return OK;
}
-static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
- ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
- (found ? "" : "no "), attr);
- return -EINVAL;
-}
-
-static status_t limitError(const AString &name, const char *msg) {
- ALOGE("limit '%s' %s", name.c_str(), msg);
- return -EINVAL;
-}
-
-static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
- ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
- attr, value.c_str());
- return -EINVAL;
-}
-
status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
- sp<AMessage> msg = new AMessage();
+ const char* a_name = nullptr;
+ const char* a_default = nullptr;
+ const char* a_in = nullptr;
+ const char* a_max = nullptr;
+ const char* a_min = nullptr;
+ const char* a_range = nullptr;
+ const char* a_ranges = nullptr;
+ const char* a_scale = nullptr;
+ const char* a_value = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- ALOGE("addLimit: limit is not given");
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")
- || !strcmp(attrs[i], "default")
- || !strcmp(attrs[i], "in")
- || !strcmp(attrs[i], "max")
- || !strcmp(attrs[i], "min")
- || !strcmp(attrs[i], "range")
- || !strcmp(attrs[i], "ranges")
- || !strcmp(attrs[i], "scale")
- || !strcmp(attrs[i], "value")) {
- msg->setString(attrs[i], attrs[i + 1]);
- ++i;
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: name is null");
+ return -EINVAL;
+ }
+ a_name = attrs[i];
+ } else if (strEq(attrs[i], "default")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: default is null");
+ return -EINVAL;
+ }
+ a_default = attrs[i];
+ } else if (strEq(attrs[i], "in")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: in is null");
+ return -EINVAL;
+ }
+ a_in = attrs[i];
+ } else if (strEq(attrs[i], "max")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: max is null");
+ return -EINVAL;
+ }
+ a_max = attrs[i];
+ } else if (strEq(attrs[i], "min")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: min is null");
+ return -EINVAL;
+ }
+ a_min = attrs[i];
+ } else if (strEq(attrs[i], "range")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: range is null");
+ return -EINVAL;
+ }
+ a_range = attrs[i];
+ } else if (strEq(attrs[i], "ranges")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: ranges is null");
+ return -EINVAL;
+ }
+ a_ranges = attrs[i];
+ } else if (strEq(attrs[i], "scale")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: scale is null");
+ return -EINVAL;
+ }
+ a_scale = attrs[i];
+ } else if (strEq(attrs[i], "value")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: value is null");
+ return -EINVAL;
+ }
+ a_value = attrs[i];
} else {
ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
return -EINVAL;
@@ -660,8 +722,7 @@
++i;
}
- AString name;
- if (!msg->findString("name", &name)) {
+ if (a_name == nullptr) {
ALOGE("limit with no 'name' attribute");
return -EINVAL;
}
@@ -670,109 +731,148 @@
// measured-frame-rate, measured-blocks-per-second: range
// quality: range + default + [scale]
// complexity: range + default
- bool found;
- if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+ if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGW("ignoring null type");
return OK;
}
- if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
- || name == "blocks-per-second" || name == "complexity"
- || name == "frame-rate" || name == "quality" || name == "size"
- || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
- AString min, max;
- if (msg->findString("min", &min) && msg->findString("max", &max)) {
- min.append("-");
- min.append(max);
- if (msg->contains("range") || msg->contains("value")) {
- return limitError(name, "has 'min' and 'max' as well as 'range' or "
+ std::string range;
+ if (strEq(a_name, "aspect-ratio") ||
+ strEq(a_name, "bitrate") ||
+ strEq(a_name, "block-count") ||
+ strEq(a_name, "blocks-per-second") ||
+ strEq(a_name, "complexity") ||
+ strEq(a_name, "frame-rate") ||
+ strEq(a_name, "quality") ||
+ strEq(a_name, "size") ||
+ strEq(a_name, "measured-blocks-per-second") ||
+ strHasPrefix(a_name, "measured-frame-rate-")) {
+ // "range" is specified in exactly one of the following forms:
+ // 1) min-max
+ // 2) value-value
+ // 3) range
+ if (a_min != nullptr && a_max != nullptr) {
+ // min-max
+ if (a_range != nullptr || a_value != nullptr) {
+ return limitError(a_name, "has 'min' and 'max' as well as 'range' or "
"'value' attributes");
}
- msg->setString("range", min);
- } else if (msg->contains("min") || msg->contains("max")) {
- return limitError(name, "has only 'min' or 'max' attribute");
- } else if (msg->findString("value", &max)) {
- min = max;
- min.append("-");
- min.append(max);
- if (msg->contains("range")) {
- return limitError(name, "has both 'range' and 'value' attributes");
+ range = a_min;
+ range += '-';
+ range += a_max;
+ } else if (a_min != nullptr || a_max != nullptr) {
+ return limitError(a_name, "has only 'min' or 'max' attribute");
+ } else if (a_value != nullptr) {
+ // value-value
+ if (a_range != nullptr) {
+ return limitError(a_name, "has both 'range' and 'value' attributes");
}
- msg->setString("range", min);
- }
-
- AString range, scale = "linear", def, in_;
- if (!msg->findString("range", &range)) {
- return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
- }
-
- if ((name == "quality" || name == "complexity") ^
- (found = msg->findString("default", &def))) {
- return limitFoundMissingAttr(name, "default", found);
- }
- if (name != "quality" && msg->findString("scale", &scale)) {
- return limitFoundMissingAttr(name, "scale");
- }
- if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
- return limitFoundMissingAttr(name, "in", found);
- }
-
- if (name == "aspect-ratio") {
- if (!(in_ == "pixels") && !(in_ == "blocks")) {
- return limitInvalidAttr(name, "in", in_);
- }
- in_.erase(5, 1); // (pixel|block)-aspect-ratio
- in_.append("-");
- in_.append(name);
- name = in_;
- }
- if (name == "quality") {
- mCurrentType->mDetails["quality-scale"] = scale;
- }
- if (name == "quality" || name == "complexity") {
- AString tag = name;
- tag.append("-default");
- mCurrentType->mDetails[tag] = def;
- }
- AString tag = name;
- tag.append("-range");
- mCurrentType->mDetails[tag] = range;
- } else {
- AString max, value, ranges;
- if (msg->contains("default")) {
- return limitFoundMissingAttr(name, "default");
- } else if (msg->contains("in")) {
- return limitFoundMissingAttr(name, "in");
- } else if ((name == "channel-count" || name == "concurrent-instances") ^
- (found = msg->findString("max", &max))) {
- return limitFoundMissingAttr(name, "max", found);
- } else if (msg->contains("min")) {
- return limitFoundMissingAttr(name, "min");
- } else if (msg->contains("range")) {
- return limitFoundMissingAttr(name, "range");
- } else if ((name == "sample-rate") ^
- (found = msg->findString("ranges", &ranges))) {
- return limitFoundMissingAttr(name, "ranges", found);
- } else if (msg->contains("scale")) {
- return limitFoundMissingAttr(name, "scale");
- } else if ((name == "alignment" || name == "block-size") ^
- (found = msg->findString("value", &value))) {
- return limitFoundMissingAttr(name, "value", found);
- }
-
- if (max.size()) {
- AString tag = "max-";
- tag.append(name);
- mCurrentType->mDetails[tag] = max;
- } else if (value.size()) {
- mCurrentType->mDetails[name] = value;
- } else if (ranges.size()) {
- AString tag = name;
- tag.append("-ranges");
- mCurrentType->mDetails[tag] = ranges;
+ range = a_value;
+ range += '-';
+ range += a_value;
+ } else if (a_range == nullptr) {
+ return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes");
} else {
- ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
+ // range
+ range = a_range;
}
+
+ // "aspect-ratio" requires some special treatment.
+ if (strEq(a_name, "aspect-ratio")) {
+ // "aspect-ratio" must have "in".
+ if (a_in == nullptr) {
+ return limitFoundMissingAttr(a_name, "in", false);
+ }
+ // "in" must be either "pixels" or "blocks".
+ if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) {
+ return limitInvalidAttr(a_name, "in", a_in);
+ }
+ // name will be "pixel-aspect-ratio-range" or
+ // "block-aspect-ratio-range".
+ mCurrentType->second[
+ std::string(a_in).substr(0, strlen(a_in) - 1) +
+ "-aspect-ratio-range"] = range;
+ } else {
+ // For everything else (apart from "aspect-ratio"), simply append
+ // "-range" to the name for the range-type property.
+ mCurrentType->second[std::string(a_name) + "-range"] = range;
+
+ // Only "quality" may have "scale".
+ if (!strEq(a_name, "quality") && a_scale != nullptr) {
+ return limitFoundMissingAttr(a_name, "scale");
+ } else if (strEq(a_name, "quality")) {
+ // The default value of "quality-scale" is "linear".
+ mCurrentType->second["quality-scale"] = a_scale == nullptr ?
+ "linear" : a_scale;
+ }
+
+ // "quality" and "complexity" must have "default".
+ // Other limits must not have "default".
+ if (strEq(a_name, "quality") || strEq(a_name, "complexity")) {
+ if (a_default == nullptr) {
+ return limitFoundMissingAttr(a_name, "default", false);
+ }
+ // name will be "quality-default" or "complexity-default".
+ mCurrentType->second[std::string(a_name) + "-default"] = a_default;
+ } else if (a_default != nullptr) {
+ return limitFoundMissingAttr(a_name, "default", true);
+ }
+ }
+ } else {
+ if (a_default != nullptr) {
+ return limitFoundMissingAttr(a_name, "default");
+ }
+ if (a_in != nullptr) {
+ return limitFoundMissingAttr(a_name, "in");
+ }
+ if (a_scale != nullptr) {
+ return limitFoundMissingAttr(a_name, "scale");
+ }
+ if (a_range != nullptr) {
+ return limitFoundMissingAttr(a_name, "range");
+ }
+ if (a_min != nullptr) {
+ return limitFoundMissingAttr(a_name, "min");
+ }
+
+ if (a_max != nullptr) {
+ // "max" must exist if and only if name is "channel-count" or
+ // "concurrent-instances".
+ // "min" is not ncessary.
+ if (strEq(a_name, "channel-count") ||
+ strEq(a_name, "concurrent-instances")) {
+ mCurrentType->second[std::string("max-") + a_name] = a_max;
+ } else {
+ return limitFoundMissingAttr(a_name, "max", false);
+ }
+ } else if (strEq(a_name, "channel-count") ||
+ strEq(a_name, "concurrent-instances")) {
+ return limitFoundMissingAttr(a_name, "max");
+ }
+
+ if (a_ranges != nullptr) {
+ // "ranges" must exist if and only if name is "sample-rate".
+ if (strEq(a_name, "sample-rate")) {
+ mCurrentType->second["sample-rate-ranges"] = a_ranges;
+ } else {
+ return limitFoundMissingAttr(a_name, "ranges", false);
+ }
+ } else if (strEq(a_name, "sample-rate")) {
+ return limitFoundMissingAttr(a_name, "ranges");
+ }
+
+ if (a_value != nullptr) {
+ // "value" must exist if and only if name is "alignment" or
+ // "block-size".
+ if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
+ mCurrentType->second[a_name] = a_value;
+ } else {
+ return limitFoundMissingAttr(a_name, "value", false);
+ }
+ } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
+ return limitFoundMissingAttr(a_name, "value", false);
+ }
+
}
return OK;
@@ -780,83 +880,175 @@
status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
size_t i = 0;
- const char *name = NULL;
+ const char *name = nullptr;
int32_t optional = -1;
int32_t required = -1;
- const char *value = NULL;
+ const char *value = nullptr;
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- ALOGE("addFeature: feature is not given");
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")) {
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
- int value = (int)ParseBoolean(attrs[i + 1]);
- if (!strcmp(attrs[i], "optional")) {
- optional = value;
- } else {
- required = value;
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: name is null");
+ return -EINVAL;
}
- ++i;
- } else if (!strcmp(attrs[i], "value")) {
- value = attrs[i + 1];
- ++i;
+ name = attrs[i];
+ } else if (strEq(attrs[i], "optional")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: optional is null");
+ return -EINVAL;
+ }
+ optional = parseBoolean(attrs[i]) ? 1 : 0;
+ } else if (strEq(attrs[i], "required")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: required is null");
+ return -EINVAL;
+ }
+ required = parseBoolean(attrs[i]) ? 1 : 0;
+ } else if (strEq(attrs[i], "value")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: value is null");
+ return -EINVAL;
+ }
+ value = attrs[i];
} else {
ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
++i;
}
- if (name == NULL) {
+
+ // Every feature must have a name.
+ if (name == nullptr) {
ALOGE("feature with no 'name' attribute");
return -EINVAL;
}
- if (optional == required && optional != -1) {
- ALOGE("feature '%s' is both/neither optional and required", name);
- return -EINVAL;
- }
-
- if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+ if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGW("ignoring null type");
return OK;
}
- if (value != NULL) {
- mCurrentType->mStringFeatures[name] = value;
- } else {
- mCurrentType->mBoolFeatures[name] = (required == 1) || (optional == 0);
+
+ if ((optional != -1) || (required != -1)) {
+ if (optional == required) {
+ ALOGE("feature '%s' is both/neither optional and required", name);
+ return -EINVAL;
+ }
+ if ((optional == 1) || (required == 1)) {
+ if (value != nullptr) {
+ ALOGE("feature '%s' cannot have extra 'value'", name);
+ return -EINVAL;
+ }
+ mCurrentType->second[std::string("feature-") + name] =
+ optional == 1 ? "0" : "1";
+ return OK;
+ }
}
+ mCurrentType->second[std::string("feature-") + name] = value == nullptr ?
+ "0" : value;
return OK;
}
-void MediaCodecsXmlParser::getGlobalSettings(
- std::map<AString, AString> *settings) const {
- settings->clear();
- settings->insert(mGlobalSettings.begin(), mGlobalSettings.end());
+const MediaCodecsXmlParser::AttributeMap&
+ MediaCodecsXmlParser::getServiceAttributeMap() const {
+ return mServiceAttributeMap;
}
-status_t MediaCodecsXmlParser::getCodecInfo(const char *name, CodecInfo *info) const {
- if (mCodecInfos.count(name) == 0) {
- ALOGE("Codec not found with name '%s'", name);
- return NAME_NOT_FOUND;
+const MediaCodecsXmlParser::CodecMap&
+ MediaCodecsXmlParser::getCodecMap() const {
+ return mCodecMap;
+}
+
+const MediaCodecsXmlParser::RoleMap&
+ MediaCodecsXmlParser::getRoleMap() const {
+ if (mRoleMap.empty()) {
+ generateRoleMap();
}
- *info = mCodecInfos.at(name);
- return OK;
+ return mRoleMap;
}
-status_t MediaCodecsXmlParser::getQuirks(const char *name, std::vector<AString> *quirks) const {
- if (mQuirks.count(name) == 0) {
- ALOGE("Codec not found with name '%s'", name);
- return NAME_NOT_FOUND;
+const char* MediaCodecsXmlParser::getCommonPrefix() const {
+ if (mCommonPrefix.empty()) {
+ generateCommonPrefix();
}
- quirks->clear();
- quirks->insert(quirks->end(), mQuirks.at(name).begin(), mQuirks.at(name).end());
- return OK;
+ return mCommonPrefix.data();
}
-} // namespace android
+status_t MediaCodecsXmlParser::getParsingStatus() const {
+ return mParsingStatus;
+}
+
+void MediaCodecsXmlParser::generateRoleMap() const {
+ for (const auto& codec : mCodecMap) {
+ const auto& codecName = codec.first;
+ bool isEncoder = codec.second.isEncoder;
+ size_t order = codec.second.order;
+ const auto& typeMap = codec.second.typeMap;
+ for (const auto& type : typeMap) {
+ const auto& typeName = type.first;
+ const char* roleName = GetComponentRole(isEncoder, typeName.data());
+ if (roleName == nullptr) {
+ ALOGE("Cannot find the role for %s of type %s",
+ isEncoder ? "an encoder" : "a decoder",
+ typeName.data());
+ continue;
+ }
+ const auto& typeAttributeMap = type.second;
+
+ auto roleIterator = mRoleMap.find(roleName);
+ std::multimap<size_t, NodeInfo>* nodeList;
+ if (roleIterator == mRoleMap.end()) {
+ RoleProperties roleProperties;
+ roleProperties.type = typeName;
+ roleProperties.isEncoder = isEncoder;
+ auto insertResult = mRoleMap.insert(
+ std::make_pair(roleName, roleProperties));
+ if (!insertResult.second) {
+ ALOGE("Cannot add role %s", roleName);
+ continue;
+ }
+ nodeList = &insertResult.first->second.nodeList;
+ } else {
+ if (roleIterator->second.type != typeName) {
+ ALOGE("Role %s has mismatching types: %s and %s",
+ roleName,
+ roleIterator->second.type.data(),
+ typeName.data());
+ continue;
+ }
+ if (roleIterator->second.isEncoder != isEncoder) {
+ ALOGE("Role %s cannot be both an encoder and a decoder",
+ roleName);
+ continue;
+ }
+ nodeList = &roleIterator->second.nodeList;
+ }
+
+ NodeInfo nodeInfo;
+ nodeInfo.name = codecName;
+ nodeInfo.attributeList.reserve(typeAttributeMap.size());
+ for (const auto& attribute : typeAttributeMap) {
+ nodeInfo.attributeList.push_back(
+ Attribute{attribute.first, attribute.second});
+ }
+ nodeList->insert(std::make_pair(
+ std::move(order), std::move(nodeInfo)));
+ }
+ }
+}
+
+void MediaCodecsXmlParser::generateCommonPrefix() const {
+ if (mCodecMap.empty()) {
+ return;
+ }
+ auto i = mCodecMap.cbegin();
+ auto first = i->first.cbegin();
+ auto last = i->first.cend();
+ for (++i; i != mCodecMap.cend(); ++i) {
+ last = std::mismatch(
+ first, last, i->first.cbegin(), i->first.cend()).first;
+ }
+ mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index b324cd8..cc69e52 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -14,65 +14,107 @@
* limitations under the License.
*/
-#ifndef MEDIA_CODECS_XML_PARSER_H_
-
-#define MEDIA_CODECS_XML_PARSER_H_
-
-#include <map>
-#include <vector>
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
+#ifndef MEDIA_STAGEFRIGHT_XMLPARSER_H_
+#define MEDIA_STAGEFRIGHT_XMLPARSER_H_
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/Vector.h>
#include <utils/StrongPointer.h>
+#include <string>
+#include <set>
+#include <map>
+#include <vector>
+
namespace android {
-struct AMessage;
-
-// Quirk still supported, even though deprecated
-enum Quirks {
- kRequiresAllocateBufferOnInputPorts = 1,
- kRequiresAllocateBufferOnOutputPorts = 2,
-
- kQuirksMask = kRequiresAllocateBufferOnInputPorts
- | kRequiresAllocateBufferOnOutputPorts,
-};
-
-// Lightweight struct for querying components.
-struct TypeInfo {
- AString mName;
- std::map<AString, AString> mStringFeatures;
- std::map<AString, bool> mBoolFeatures;
- std::map<AString, AString> mDetails;
-};
-
-struct ProfileLevel {
- uint32_t mProfile;
- uint32_t mLevel;
-};
-
-struct CodecInfo {
- std::vector<TypeInfo> mTypes;
- std::vector<ProfileLevel> mProfileLevels;
- std::vector<uint32_t> mColorFormats;
- uint32_t mFlags;
- bool mIsEncoder;
-};
-
class MediaCodecsXmlParser {
public:
- MediaCodecsXmlParser();
+
+ // Treblized media codec list will be located in /odm/etc or /vendor/etc.
+ static constexpr char const* defaultSearchDirs[] =
+ {"/odm/etc", "/vendor/etc", "/etc", nullptr};
+ static constexpr char const* defaultMainXmlName =
+ "media_codecs.xml";
+ static constexpr char const* defaultPerformanceXmlName =
+ "media_codecs_performance.xml";
+ static constexpr char const* defaultProfilingResultsXmlPath =
+ "/data/misc/media/media_codecs_profiling_results.xml";
+
+ MediaCodecsXmlParser(
+ const char* const* searchDirs = defaultSearchDirs,
+ const char* mainXmlName = defaultMainXmlName,
+ const char* performanceXmlName = defaultPerformanceXmlName,
+ const char* profilingResultsXmlPath = defaultProfilingResultsXmlPath);
~MediaCodecsXmlParser();
- void getGlobalSettings(std::map<AString, AString> *settings) const;
+ typedef std::pair<std::string, std::string> Attribute;
+ typedef std::map<std::string, std::string> AttributeMap;
- status_t getCodecInfo(const char *name, CodecInfo *info) const;
+ typedef std::pair<std::string, AttributeMap> Type;
+ typedef std::map<std::string, AttributeMap> TypeMap;
- status_t getQuirks(const char *name, std::vector<AString> *quirks) const;
+ typedef std::set<std::string> QuirkSet;
+
+ /**
+ * Properties of a codec (node)
+ */
+ struct CodecProperties {
+ bool isEncoder; ///< Whether this codec is an encoder or a decoder
+ size_t order; ///< Order of appearance in the file (starting from 0)
+ QuirkSet quirkSet; ///< Set of quirks requested by this codec
+ TypeMap typeMap; ///< Map of types supported by this codec
+ };
+
+ typedef std::pair<std::string, CodecProperties> Codec;
+ typedef std::map<std::string, CodecProperties> CodecMap;
+
+ /**
+ * Properties of a node (for IOmxStore)
+ */
+ struct NodeInfo {
+ std::string name;
+ std::vector<Attribute> attributeList;
+ };
+
+ /**
+ * Properties of a role (for IOmxStore)
+ */
+ struct RoleProperties {
+ std::string type;
+ bool isEncoder;
+ std::multimap<size_t, NodeInfo> nodeList;
+ };
+
+ typedef std::pair<std::string, RoleProperties> Role;
+ typedef std::map<std::string, RoleProperties> RoleMap;
+
+ /**
+ * Return a map for attributes that are service-specific.
+ */
+ const AttributeMap& getServiceAttributeMap() const;
+
+ /**
+ * Return a map for codecs and their properties.
+ */
+ const CodecMap& getCodecMap() const;
+
+ /**
+ * Return a map for roles and their properties.
+ * This map is generated from the CodecMap.
+ */
+ const RoleMap& getRoleMap() const;
+
+ /**
+ * Return a common prefix of all node names.
+ *
+ * The prefix is not provided in the xml, so it has to be computed by taking
+ * the longest common prefix of all node names.
+ */
+ const char* getCommonPrefix() const;
+
+ status_t getParsingStatus() const;
private:
enum Section {
@@ -87,23 +129,31 @@
SECTION_INCLUDE,
};
- status_t mInitCheck;
+ status_t mParsingStatus;
Section mCurrentSection;
bool mUpdate;
- Vector<Section> mPastSections;
- int32_t mDepth;
- AString mHrefBase;
+ std::vector<Section> mSectionStack;
+ std::string mHrefBase;
- std::map<AString, AString> mGlobalSettings;
+ // Service attributes
+ AttributeMap mServiceAttributeMap;
- // name -> CodecInfo
- std::map<AString, CodecInfo> mCodecInfos;
- std::map<AString, std::vector<AString>> mQuirks;
- AString mCurrentName;
- std::vector<TypeInfo>::iterator mCurrentType;
+ // Codec attributes
+ std::string mCurrentName;
+ std::set<std::string> mCodecSet;
+ Codec mCodecListTemp[2048];
+ CodecMap mCodecMap;
+ size_t mCodecCounter;
+ CodecMap::iterator mCurrentCodec;
+ TypeMap::iterator mCurrentType;
- status_t initCheck() const;
- void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
+ // Role map
+ mutable RoleMap mRoleMap;
+
+ // Computed longest common prefix
+ mutable std::string mCommonPrefix;
+
+ bool parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
void parseXMLFile(const char *path);
@@ -118,7 +168,8 @@
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 addMediaCodec(bool encoder, const char *name,
+ const char *type = nullptr);
status_t addQuirk(const char **attrs);
status_t addTypeFromAttributes(const char **attrs, bool encoder);
@@ -126,10 +177,14 @@
status_t addFeature(const char **attrs);
void addType(const char *name);
- DISALLOW_EVIL_CONSTRUCTORS(MediaCodecsXmlParser);
+ void generateRoleMap() const;
+ void generateCommonPrefix() const;
+
+ MediaCodecsXmlParser(const MediaCodecsXmlParser&) = delete;
+ MediaCodecsXmlParser& operator=(const MediaCodecsXmlParser&) = delete;
};
-} // namespace android
+} // namespace android
-#endif // MEDIA_CODECS_XML_PARSER_H_
+#endif // MEDIA_STAGEFRIGHT_XMLPARSER_H_
diff --git a/media/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
index 4132fed..8d894c1 100644
--- a/media/mtp/MtpFfsHandle.cpp
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -62,6 +62,8 @@
constexpr int USB_FFS_MAX_WRITE = MTP_BUFFER_SIZE;
constexpr int USB_FFS_MAX_READ = MTP_BUFFER_SIZE;
+constexpr unsigned FFS_NUM_EVENTS = 5;
+
static_assert(USB_FFS_MAX_WRITE > 0, "Max r/w values must be > 0!");
static_assert(USB_FFS_MAX_READ > 0, "Max r/w values must be > 0!");
@@ -102,8 +104,11 @@
__le32 fs_count;
__le32 hs_count;
__le32 ss_count;
+ __le32 os_count;
struct func_desc fs_descs, hs_descs;
struct ss_func_desc ss_descs;
+ struct usb_os_desc_header os_header;
+ struct usb_ext_compat_desc os_desc;
} __attribute__((packed));
const struct usb_interface_descriptor mtp_interface_desc = {
@@ -287,6 +292,36 @@
},
};
+struct usb_os_desc_header mtp_os_desc_header = {
+ .interface = htole32(1),
+ .dwLength = htole32(sizeof(usb_os_desc_header) + sizeof(usb_ext_compat_desc)),
+ .bcdVersion = htole16(1),
+ .wIndex = htole16(4),
+ .bCount = htole16(1),
+ .Reserved = htole16(0),
+};
+
+struct usb_ext_compat_desc mtp_os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = htole32(1),
+ .CompatibleID = { 'M', 'T', 'P' },
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
+struct usb_ext_compat_desc ptp_os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = htole32(1),
+ .CompatibleID = { 'P', 'T', 'P' },
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
+struct mtp_device_status {
+ uint16_t wLength;
+ uint16_t wCode;
+};
+
} // anonymous namespace
namespace android {
@@ -311,13 +346,16 @@
v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC;
+ FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
v2_descriptor.fs_count = 4;
v2_descriptor.hs_count = 4;
v2_descriptor.ss_count = 7;
+ v2_descriptor.os_count = 1;
v2_descriptor.fs_descs = mPtp ? ptp_fs_descriptors : mtp_fs_descriptors;
v2_descriptor.hs_descs = mPtp ? ptp_hs_descriptors : mtp_hs_descriptors;
v2_descriptor.ss_descs = mPtp ? ptp_ss_descriptors : mtp_ss_descriptors;
+ v2_descriptor.os_header = mtp_os_desc_header;
+ v2_descriptor.os_desc = mPtp ? ptp_os_desc_compat : mtp_os_desc_compat;
if (mControl < 0) { // might have already done this before
mControl.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP0, O_RDWR)));
@@ -361,6 +399,88 @@
mControl.reset();
}
+void MtpFfsHandle::controlLoop() {
+ while (!handleEvent()) {}
+ LOG(DEBUG) << "Mtp server shutting down";
+}
+
+int MtpFfsHandle::handleEvent() {
+ std::vector<usb_functionfs_event> events(FFS_NUM_EVENTS);
+ usb_functionfs_event *event = events.data();
+ int nbytes = TEMP_FAILURE_RETRY(::read(mControl, event,
+ events.size() * sizeof(usb_functionfs_event)));
+ if (nbytes == -1) {
+ return -1;
+ }
+ int ret = 0;
+ for (size_t n = nbytes / sizeof *event; n; --n, ++event) {
+ switch (event->type) {
+ case FUNCTIONFS_BIND:
+ case FUNCTIONFS_ENABLE:
+ case FUNCTIONFS_RESUME:
+ ret = 0;
+ errno = 0;
+ break;
+ case FUNCTIONFS_SUSPEND:
+ case FUNCTIONFS_UNBIND:
+ case FUNCTIONFS_DISABLE:
+ errno = ESHUTDOWN;
+ ret = -1;
+ break;
+ case FUNCTIONFS_SETUP:
+ if (handleControlRequest(&event->u.setup) == -1)
+ ret = -1;
+ break;
+ default:
+ LOG(DEBUG) << "Mtp Event " << event->type << " (unknown)";
+ }
+ }
+ return ret;
+}
+
+int MtpFfsHandle::handleControlRequest(const struct usb_ctrlrequest *setup) {
+ uint8_t type = setup->bRequestType;
+ uint8_t code = setup->bRequest;
+ uint16_t length = setup->wLength;
+ uint16_t index = setup->wIndex;
+ uint16_t value = setup->wValue;
+ std::vector<char> buf;
+ buf.resize(length);
+
+ if (!(type & USB_DIR_IN)) {
+ if (::read(mControl, buf.data(), length) != length) {
+ PLOG(DEBUG) << "Mtp error ctrlreq read data";
+ }
+ }
+
+ if ((type & USB_TYPE_MASK) == USB_TYPE_CLASS && index == 0 && value == 0) {
+ switch(code) {
+ case MTP_REQ_GET_DEVICE_STATUS:
+ {
+ if (length < sizeof(struct mtp_device_status)) {
+ return -1;
+ }
+ struct mtp_device_status *st = reinterpret_cast<struct mtp_device_status*>(buf.data());
+ st->wLength = htole16(sizeof(st));
+ st->wCode = MTP_RESPONSE_OK;
+ length = st->wLength;
+ break;
+ }
+ default:
+ LOG(DEBUG) << "Unrecognized Mtp class request! " << code;
+ }
+ } else {
+ LOG(DEBUG) << "Unrecognized request type " << type;
+ }
+
+ if (type & USB_DIR_IN) {
+ if (::write(mControl, buf.data(), length) != length) {
+ PLOG(DEBUG) << "Mtp error ctrlreq write data";
+ }
+ }
+ return 0;
+}
+
int MtpFfsHandle::writeHandle(int fd, const void* data, int len) {
LOG(VERBOSE) << "MTP about to write fd = " << fd << ", len=" << len;
int ret = 0;
@@ -460,6 +580,10 @@
posix_madvise(mBuffer2.data(), MAX_FILE_CHUNK_SIZE,
POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED);
+ // Handle control requests.
+ std::thread t([this]() { this->controlLoop(); });
+ t.detach();
+
// Get device specific r/w size
mMaxWrite = android::base::GetIntProperty("sys.usb.ffs.max_write", USB_FFS_MAX_WRITE);
mMaxRead = android::base::GetIntProperty("sys.usb.ffs.max_read", USB_FFS_MAX_READ);
@@ -542,6 +666,7 @@
size_t length;
bool read = false;
bool write = false;
+ bool short_packet = false;
posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
@@ -586,6 +711,7 @@
// For larger files, receive until a short packet is received.
if (static_cast<size_t>(ret) < length) {
file_length = 0;
+ short_packet = true;
}
} else {
// Receive an empty packet if size is a multiple of the endpoint size.
@@ -605,7 +731,7 @@
read = false;
}
}
- if (ret % packet_size == 0 || zero_packet) {
+ if ((ret % packet_size == 0 && !short_packet) || zero_packet) {
if (TEMP_FAILURE_RETRY(::read(mBulkOut, data, packet_size)) != 0) {
return -1;
}
diff --git a/media/mtp/MtpFfsHandle.h b/media/mtp/MtpFfsHandle.h
index b637d65..da0cd9f 100644
--- a/media/mtp/MtpFfsHandle.h
+++ b/media/mtp/MtpFfsHandle.h
@@ -35,6 +35,10 @@
void closeEndpoints();
void doSendEvent(mtp_event me);
+ void controlLoop();
+ int handleEvent();
+ int handleControlRequest(const struct usb_ctrlrequest *setup);
+
bool mPtp;
std::timed_mutex mLock;
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 13cc859..1c7829d 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -494,4 +494,10 @@
#define MTP_ASSOCIATION_TYPE_UNDEFINED 0x0000
#define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER 0x0001
+// MTP class reqeusts
+#define MTP_REQ_CANCEL 0x64
+#define MTP_REQ_GET_EXT_EVENT_DATA 0x65
+#define MTP_REQ_RESET 0x66
+#define MTP_REQ_GET_DEVICE_STATUS 0x67
+
#endif // _MTP_H
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 51143ac..eecc858 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -421,7 +421,7 @@
for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
- keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
+ keyValuePairs[i].mValue = mObj->mQueryResults.valueAt(i).string();
}
*numPairs = mObj->mQueryResults.size();
return AMEDIA_OK;
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 1d4386c..b7bce55 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -694,6 +694,7 @@
audio_io_handle_t *ioHandle,
audio_devices_t *device)
{
+ Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
@@ -703,6 +704,7 @@
status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session)
{
+ Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 3f4017f..0d2dba1 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -121,18 +121,17 @@
if (mCallbackStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight, currentFormat;
- res = device->getStreamInfo(mCallbackStreamId,
- ¤tWidth, ¤tHeight, ¤tFormat, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mCallbackStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying callback output stream info: "
"%s (%d)", __FUNCTION__, mId,
strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.previewWidth ||
- currentHeight != (uint32_t)params.previewHeight ||
- currentFormat != (uint32_t)callbackFormat) {
+ if (streamInfo.width != (uint32_t)params.previewWidth ||
+ streamInfo.height != (uint32_t)params.previewHeight ||
+ !streamInfo.matchFormat((uint32_t)callbackFormat)) {
// Since size should only change while preview is not running,
// assuming that all existing use of old callback stream is
// completed.
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index d6d8dde..d8b7af2 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -136,17 +136,16 @@
if (mCaptureStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- res = device->getStreamInfo(mCaptureStreamId,
- ¤tWidth, ¤tHeight, 0, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mCaptureStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying capture output stream info: "
"%s (%d)", __FUNCTION__,
mId, strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.pictureWidth ||
- currentHeight != (uint32_t)params.pictureHeight) {
+ if (streamInfo.width != (uint32_t)params.pictureWidth ||
+ streamInfo.height != (uint32_t)params.pictureHeight) {
ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
__FUNCTION__, mId, mCaptureStreamId);
res = device->deleteStream(mCaptureStreamId);
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index d79e430..b6f443a 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -161,18 +161,17 @@
if (mPreviewStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- res = device->getStreamInfo(mPreviewStreamId,
- ¤tWidth, ¤tHeight, 0, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mPreviewStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying preview stream info: "
"%s (%d)", __FUNCTION__, mId, strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.previewWidth ||
- currentHeight != (uint32_t)params.previewHeight) {
+ if (streamInfo.width != (uint32_t)params.previewWidth ||
+ streamInfo.height != (uint32_t)params.previewHeight) {
ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d",
- __FUNCTION__, mId, currentWidth, currentHeight,
+ __FUNCTION__, mId, streamInfo.width, streamInfo.height,
params.previewWidth, params.previewHeight);
res = device->waitUntilDrained();
if (res != OK) {
@@ -312,10 +311,8 @@
return INVALID_OPERATION;
}
- uint32_t currentWidth, currentHeight, currentFormat;
- android_dataspace currentDataSpace;
- res = device->getStreamInfo(mRecordingStreamId,
- ¤tWidth, ¤tHeight, ¤tFormat, ¤tDataSpace);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mRecordingStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying recording output stream info: "
"%s (%d)", __FUNCTION__, mId,
@@ -324,10 +321,10 @@
}
if (mRecordingWindow == nullptr ||
- currentWidth != (uint32_t)params.videoWidth ||
- currentHeight != (uint32_t)params.videoHeight ||
- currentFormat != (uint32_t)params.videoFormat ||
- currentDataSpace != params.videoDataSpace) {
+ streamInfo.width != (uint32_t)params.videoWidth ||
+ streamInfo.height != (uint32_t)params.videoHeight ||
+ !streamInfo.matchFormat((uint32_t)params.videoFormat) ||
+ streamInfo.dataSpace != params.videoDataSpace) {
*needsUpdate = true;
return res;
}
@@ -348,22 +345,18 @@
if (mRecordingStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- uint32_t currentFormat;
- android_dataspace currentDataSpace;
- res = device->getStreamInfo(mRecordingStreamId,
- ¤tWidth, ¤tHeight,
- ¤tFormat, ¤tDataSpace);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mRecordingStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying recording output stream info: "
"%s (%d)", __FUNCTION__, mId,
strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.videoWidth ||
- currentHeight != (uint32_t)params.videoHeight ||
- currentFormat != (uint32_t)params.videoFormat ||
- currentDataSpace != params.videoDataSpace) {
+ if (streamInfo.width != (uint32_t)params.videoWidth ||
+ streamInfo.height != (uint32_t)params.videoHeight ||
+ !streamInfo.matchFormat((uint32_t)params.videoFormat) ||
+ streamInfo.dataSpace != params.videoDataSpace) {
// TODO: Should wait to be sure previous recording has finished
res = device->deleteStream(mRecordingStreamId);
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 9bc31b9..b0607fb 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -233,17 +233,16 @@
if ((mZslStreamId != NO_STREAM) || (mInputStreamId != NO_STREAM)) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- res = device->getStreamInfo(mZslStreamId,
- ¤tWidth, ¤tHeight, 0, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mZslStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying capture output stream info: "
"%s (%d)", __FUNCTION__,
client->getCameraId(), strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.fastInfo.arrayWidth ||
- currentHeight != (uint32_t)params.fastInfo.arrayHeight) {
+ if (streamInfo.width != (uint32_t)params.fastInfo.arrayWidth ||
+ streamInfo.height != (uint32_t)params.fastInfo.arrayHeight) {
if (mZslStreamId != NO_STREAM) {
ALOGV("%s: Camera %d: Deleting stream %d since the buffer "
"dimensions changed",
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 6fd9263..c03e8a2 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1354,7 +1354,7 @@
sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
if (remoteCb != 0) {
- remoteCb->onRepeatingRequestError(lastFrameNumber);
+ remoteCb->onRepeatingRequestError(lastFrameNumber, mStreamingRequestId);
}
Mutex::Autolock idLock(mStreamingRequestIdLock);
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 54fcb0a..fe4c8d7 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -142,12 +142,32 @@
virtual status_t createInputStream(uint32_t width, uint32_t height,
int32_t format, /*out*/ int32_t *id) = 0;
+ struct StreamInfo {
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ bool formatOverridden;
+ uint32_t originalFormat;
+ android_dataspace dataSpace;
+ StreamInfo() : width(0), height(0), format(0), formatOverridden(false), originalFormat(0),
+ dataSpace(HAL_DATASPACE_UNKNOWN) {}
+ /**
+ * Check whether the format matches the current or the original one in case
+ * it got overridden.
+ */
+ bool matchFormat(uint32_t clientFormat) {
+ if ((formatOverridden && (originalFormat == clientFormat)) ||
+ (format == clientFormat)) {
+ return true;
+ }
+ return false;
+ }
+ };
+
/**
* Get information about a given stream.
*/
- virtual status_t getStreamInfo(int id,
- uint32_t *width, uint32_t *height,
- uint32_t *format, android_dataspace *dataSpace) = 0;
+ virtual status_t getStreamInfo(int id, StreamInfo *streamInfo) = 0;
/**
* Set stream gralloc buffer transform
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index 991b50f..f539ac1 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -97,11 +97,13 @@
ALOGE("%s: CameraHeapMemory has FD %d (expect >= 0)", __FUNCTION__, memPoolId);
return 0;
}
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
mHidlMemPoolMap.insert(std::make_pair(memPoolId, mem));
return memPoolId;
}
hardware::Return<void> CameraHardwareInterface::unregisterMemory(uint32_t memId) {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
if (mHidlMemPoolMap.count(memId) == 0) {
ALOGE("%s: memory pool ID %d not found", __FUNCTION__, memId);
return hardware::Void();
@@ -115,6 +117,7 @@
hardware::Return<void> CameraHardwareInterface::dataCallback(
DataCallbackMsg msgType, uint32_t data, uint32_t bufferIndex,
const hardware::camera::device::V1_0::CameraFrameMetadata& metadata) {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
if (mHidlMemPoolMap.count(data) == 0) {
ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
return hardware::Void();
@@ -129,6 +132,7 @@
hardware::Return<void> CameraHardwareInterface::dataCallbackTimestamp(
DataCallbackMsg msgType, uint32_t data,
uint32_t bufferIndex, int64_t timestamp) {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
if (mHidlMemPoolMap.count(data) == 0) {
ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
return hardware::Void();
@@ -140,6 +144,7 @@
hardware::Return<void> CameraHardwareInterface::handleCallbackTimestamp(
DataCallbackMsg msgType, const hidl_handle& frameData, uint32_t data,
uint32_t bufferIndex, int64_t timestamp) {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
if (mHidlMemPoolMap.count(data) == 0) {
ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
return hardware::Void();
@@ -158,6 +163,7 @@
std::vector<android::HandleTimestampMessage> msgs;
msgs.reserve(messages.size());
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
for (const auto& hidl_msg : messages) {
if (mHidlMemPoolMap.count(hidl_msg.data) == 0) {
ALOGE("%s: memory pool ID %d not found", __FUNCTION__, hidl_msg.data);
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 6a1b4fb..e519b04 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -479,6 +479,7 @@
uint64_t mNextBufferId = 1;
static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+ std::mutex mHidlMemPoolMapLock; // protecting mHidlMemPoolMap
std::unordered_map<int, camera_memory_t*> mHidlMemPoolMap;
};
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 94e8f3b..669f763 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1345,10 +1345,11 @@
return OK;
}
-status_t Camera3Device::getStreamInfo(int id,
- uint32_t *width, uint32_t *height,
- uint32_t *format, android_dataspace *dataSpace) {
+status_t Camera3Device::getStreamInfo(int id, StreamInfo *streamInfo) {
ATRACE_CALL();
+ if (nullptr == streamInfo) {
+ return BAD_VALUE;
+ }
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -1375,10 +1376,12 @@
return idx;
}
- if (width) *width = mOutputStreams[idx]->getWidth();
- if (height) *height = mOutputStreams[idx]->getHeight();
- if (format) *format = mOutputStreams[idx]->getFormat();
- if (dataSpace) *dataSpace = mOutputStreams[idx]->getDataSpace();
+ streamInfo->width = mOutputStreams[idx]->getWidth();
+ streamInfo->height = mOutputStreams[idx]->getHeight();
+ streamInfo->format = mOutputStreams[idx]->getFormat();
+ streamInfo->dataSpace = mOutputStreams[idx]->getDataSpace();
+ streamInfo->formatOverridden = mOutputStreams[idx]->isFormatOverridden();
+ streamInfo->originalFormat = mOutputStreams[idx]->getOriginalFormat();
return OK;
}
@@ -3233,6 +3236,8 @@
}
HalStream &src = finalConfiguration.streams[realIdx];
+ Camera3Stream* dstStream = Camera3Stream::cast(dst);
+ dstStream->setFormatOverride(false);
int overrideFormat = mapToFrameworkFormat(src.overrideFormat);
if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
if (dst->format != overrideFormat) {
@@ -3240,6 +3245,8 @@
streamId, dst->format);
}
} else {
+ dstStream->setFormatOverride((dst->format != overrideFormat) ? true : false);
+ dstStream->setOriginalFormat(dst->format);
// Override allowed with IMPLEMENTATION_DEFINED
dst->format = overrideFormat;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 363bd88..298a3d8 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -128,9 +128,7 @@
uint32_t width, uint32_t height, int format,
int *id) override;
- status_t getStreamInfo(int id,
- uint32_t *width, uint32_t *height,
- uint32_t *format, android_dataspace *dataSpace) override;
+ status_t getStreamInfo(int id, StreamInfo *streamInfo) override;
status_t setStreamTransform(int id, int transform) override;
status_t deleteStream(int id) override;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 25e44a5..c186208 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -62,7 +62,9 @@
mPrepared(false),
mPreparedBufferIdx(0),
mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX),
- mBufferLimitLatency(kBufferLimitLatencyBinSize) {
+ mBufferLimitLatency(kBufferLimitLatencyBinSize),
+ mFormatOverridden(false),
+ mOriginalFormat(-1) {
camera3_stream::stream_type = type;
camera3_stream::width = width;
@@ -112,6 +114,22 @@
mUsage = usage;
}
+void Camera3Stream::setFormatOverride(bool formatOverridden) {
+ mFormatOverridden = formatOverridden;
+}
+
+bool Camera3Stream::isFormatOverridden() {
+ return mFormatOverridden;
+}
+
+void Camera3Stream::setOriginalFormat(int originalFormat) {
+ mOriginalFormat = originalFormat;
+}
+
+int Camera3Stream::getOriginalFormat() {
+ return mOriginalFormat;
+}
+
camera3_stream* Camera3Stream::startConfiguration() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 9090f83..1843ae8 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -146,6 +146,10 @@
android_dataspace getDataSpace() const;
uint64_t getUsage() const;
void setUsage(uint64_t usage);
+ void setFormatOverride(bool formatOverriden);
+ bool isFormatOverridden();
+ void setOriginalFormat(int originalFormat);
+ int getOriginalFormat();
camera3_stream* asHalStream() override {
return this;
@@ -514,6 +518,10 @@
// max_buffers.
static const int32_t kBufferLimitLatencyBinSize = 33; //in ms
CameraLatencyHistogram mBufferLimitLatency;
+
+ //Keep track of original format in case it gets overridden
+ bool mFormatOverridden;
+ int mOriginalFormat;
}; // class Camera3Stream
}; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 0544a1b..63456c4 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -71,6 +71,10 @@
virtual uint32_t getHeight() const = 0;
virtual int getFormat() const = 0;
virtual android_dataspace getDataSpace() const = 0;
+ virtual void setFormatOverride(bool formatOverriden) = 0;
+ virtual bool isFormatOverridden() = 0;
+ virtual void setOriginalFormat(int originalFormat) = 0;
+ virtual int getOriginalFormat() = 0;
/**
* Get a HAL3 handle for the stream, without starting stream configuration.
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 3c39883..2443301 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -257,9 +257,21 @@
break;
}
- item->setPkgName(getPkgName(item->getUid(), true));
- item->setPkgVersionCode(0);
- ALOGD("info is from uid %d pkg '%s', version %d", item->getUid(), item->getPkgName().c_str(), item->getPkgVersionCode());
+ // Overwrite package name and version if the caller was untrusted.
+ if (!isTrusted) {
+ item->setPkgName(getPkgName(item->getUid(), true));
+ item->setPkgVersionCode(0);
+ } else if (item->getPkgName().empty()) {
+ // Only overwrite the package name if it was empty. Trust whatever
+ // version code was provided by the trusted caller.
+ item->setPkgName(getPkgName(uid, true));
+ }
+
+ ALOGV("given uid %d; sanitized uid: %d sanitized pkg: %s "
+ "sanitized pkg version: %d",
+ uid_given, item->getUid(),
+ item->getPkgName().c_str(),
+ item->getPkgVersionCode());
mItemsSubmitted++;
@@ -622,9 +634,6 @@
while (l->size() > 0) {
MediaAnalyticsItem * oitem = *(l->begin());
nsecs_t when = oitem->getTimestamp();
-// DEBUGGING -- remove this ALOGD() call
- if(0) ALOGD("@ now=%10" PRId64 " record when=%10" PRId64 "",
- now, when);
// careful about timejumps too
if ((now > when) && (now-when) <= mMaxRecordAgeNs) {
// this (and the rest) are recent enough to keep
@@ -641,11 +650,6 @@
// are they alike enough that nitem can be folded into oitem?
static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) {
- if (0) {
- ALOGD("Compare: o %s n %s",
- oitem->toString().c_str(), nitem->toString().c_str());
- }
-
// general safety
if (nitem->getUid() != oitem->getUid()) {
return false;
diff --git a/services/mediaanalytics/mediametrics.rc b/services/mediaanalytics/mediametrics.rc
index 3829f8c..1efde5e 100644
--- a/services/mediaanalytics/mediametrics.rc
+++ b/services/mediaanalytics/mediametrics.rc
@@ -1,5 +1,6 @@
service mediametrics /system/bin/mediametrics
class main
user media
+ group media
ioprio rt 4
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 6997b5a..faeb0a7 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -9,13 +9,8 @@
libgui \
libutils \
liblog \
- libstagefright_omx
-LOCAL_C_INCLUDES := \
- frameworks/av/include \
- frameworks/av/media/libstagefright \
- frameworks/av/media/libstagefright/include \
- frameworks/native/include \
- frameworks/native/include/media/openmax
+ libstagefright_omx \
+ libstagefright_xmlparser
LOCAL_MODULE:= libmediacodecservice
LOCAL_VENDOR_MODULE := true
LOCAL_32_BIT_ONLY := true
@@ -38,15 +33,10 @@
libhwbinder \
libhidltransport \
libstagefright_omx \
+ libstagefright_xmlparser \
android.hardware.media.omx@1.0 \
android.hidl.memory@1.0
-LOCAL_C_INCLUDES := \
- frameworks/av/include \
- frameworks/av/media/libstagefright \
- frameworks/av/media/libstagefright/include \
- frameworks/native/include \
- frameworks/native/include/media/openmax
LOCAL_MODULE := android.hardware.media.omx@1.0-service
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_VENDOR_MODULE := true
diff --git a/services/mediacodec/MediaCodecService.cpp b/services/mediacodec/MediaCodecService.cpp
index fc1e5d9..6b510c6 100644
--- a/services/mediacodec/MediaCodecService.cpp
+++ b/services/mediacodec/MediaCodecService.cpp
@@ -24,15 +24,25 @@
sp<IOMX> MediaCodecService::getOMX() {
- Mutex::Autolock autoLock(mLock);
+ Mutex::Autolock autoLock(mOMXLock);
if (mOMX.get() == NULL) {
- mOMX = new OMX;
+ mOMX = new OMX();
}
return mOMX;
}
+sp<IOMXStore> MediaCodecService::getOMXStore() {
+
+ Mutex::Autolock autoLock(mOMXStoreLock);
+
+ if (mOMXStore.get() == NULL) {
+ mOMXStore = new OMXStore();
+ }
+
+ return mOMXStore;
+}
status_t MediaCodecService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags)
diff --git a/services/mediacodec/MediaCodecService.h b/services/mediacodec/MediaCodecService.h
index 0d2c9d8..9301135 100644
--- a/services/mediacodec/MediaCodecService.h
+++ b/services/mediacodec/MediaCodecService.h
@@ -20,10 +20,12 @@
#include <binder/BinderService.h>
#include <media/IMediaCodecService.h>
#include <media/stagefright/omx/OMX.h>
+#include <media/stagefright/omx/OMXStore.h>
namespace android {
-class MediaCodecService : public BinderService<MediaCodecService>, public BnMediaCodecService
+class MediaCodecService : public BinderService<MediaCodecService>,
+ public BnMediaCodecService
{
friend class BinderService<MediaCodecService>; // for MediaCodecService()
public:
@@ -31,16 +33,20 @@
virtual ~MediaCodecService() { }
virtual void onFirstRef() { }
- static const char* getServiceName() { return "media.codec"; }
+ static const char* getServiceName() { return "media.codec"; }
- virtual sp<IOMX> getOMX();
+ virtual sp<IOMX> getOMX();
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags);
+ virtual sp<IOMXStore> getOMXStore();
+
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
private:
- Mutex mLock;
- sp<IOMX> mOMX;
+ Mutex mOMXLock;
+ sp<IOMX> mOMX;
+ Mutex mOMXStoreLock;
+ sp<IOMXStore> mOMXStore;
};
} // namespace android
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 855ae69..5b34895 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -215,8 +215,7 @@
return AAUDIO_ERROR_INVALID_HANDLE;
}
- aaudio_result_t result = serviceStream->start();
- return result;
+ return serviceStream->start();
}
aaudio_result_t AAudioService::pauseStream(aaudio_handle_t streamHandle) {
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 58213f8..4be25c8 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -250,6 +250,8 @@
aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
audio_port_handle_t *clientHandle) {
+ // Start the client on behalf of the AAudio service.
+ // Use the port handle that was provided by openMmapStream().
return startClient(mMmapClient, &mPortHandle);
}
@@ -262,11 +264,12 @@
aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle) {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+ ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d))",
+ &client, client.clientUid, client.clientPid);
audio_port_handle_t originalHandle = *clientHandle;
- aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->start(client,
- clientHandle));
- ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d), %d => %d) returns %d",
- &client, client.clientUid, client.clientPid,
+ status_t status = mMmapStream->start(client, clientHandle);
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+ ALOGD("AAudioServiceEndpointMMAP::startClient() , %d => %d returns %d",
originalHandle, *clientHandle, result);
return result;
}
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 43d73b7..18dc12f 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -122,7 +122,6 @@
startSharingThread_l();
}
if (result == AAUDIO_OK) {
- ALOGD("AAudioServiceEndpointShared::startStream() use shared stream client.");
result = getStreamInternal()->startClient(sharedStream->getAudioClient(), clientHandle);
}
return result;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 2dc62a0..ca7b528 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -42,7 +42,7 @@
AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService)
: mUpMessageQueue(nullptr)
- , mAAudioThread()
+ , mTimestampThread()
, mAtomicTimestamp()
, mAudioService(audioService) {
mMmapClient.clientUid = -1;
@@ -54,10 +54,10 @@
ALOGD("AAudioServiceStreamBase::~AAudioServiceStreamBase() destroying %p", this);
// If the stream is deleted when OPEN or in use then audio resources will leak.
// This would indicate an internal error. So we want to find this ASAP.
- LOG_ALWAYS_FATAL_IF(!(mState == AAUDIO_STREAM_STATE_CLOSED
- || mState == AAUDIO_STREAM_STATE_UNINITIALIZED
- || mState == AAUDIO_STREAM_STATE_DISCONNECTED),
- "service stream still open, state = %d", mState);
+ LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
+ || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
+ || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
+ "service stream still open, state = %d", getState());
}
std::string AAudioServiceStreamBase::dumpHeader() {
@@ -71,7 +71,7 @@
<< std::dec << std::setfill(' ') ;
result << std::setw(6) << mMmapClient.clientUid;
result << std::setw(4) << (isRunning() ? "yes" : " no");
- result << std::setw(6) << mState;
+ result << std::setw(6) << getState();
result << std::setw(7) << getFormat();
result << std::setw(6) << mFramesPerBurst;
result << std::setw(5) << getSamplesPerFrame();
@@ -124,7 +124,7 @@
aaudio_result_t AAudioServiceStreamBase::close() {
aaudio_result_t result = AAUDIO_OK;
- if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
return AAUDIO_OK;
}
@@ -146,37 +146,50 @@
mUpMessageQueue = nullptr;
}
- mState = AAUDIO_STREAM_STATE_CLOSED;
+ setState(AAUDIO_STREAM_STATE_CLOSED);
return result;
}
+aaudio_result_t AAudioServiceStreamBase::startDevice() {
+ mClientHandle = AUDIO_PORT_HANDLE_NONE;
+ return mServiceEndpoint->startStream(this, &mClientHandle);
+}
+
/**
* Start the flow of audio data.
*
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamBase::start() {
+ aaudio_result_t result = AAUDIO_OK;
if (isRunning()) {
return AAUDIO_OK;
}
if (mServiceEndpoint == nullptr) {
ALOGE("AAudioServiceStreamBase::start() missing endpoint");
- return AAUDIO_ERROR_INVALID_STATE;
+ result = AAUDIO_ERROR_INVALID_STATE;
+ goto error;
}
+
+ // Start with fresh presentation timestamps.
+ mAtomicTimestamp.clear();
+
mClientHandle = AUDIO_PORT_HANDLE_NONE;
- aaudio_result_t result = mServiceEndpoint->startStream(this, &mClientHandle);
- if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamBase::start() mServiceEndpoint returned %d", result);
- disconnect();
- } else {
- if (result == AAUDIO_OK) {
- sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
- mState = AAUDIO_STREAM_STATE_STARTED;
- mThreadEnabled.store(true);
- result = mAAudioThread.start(this);
- }
- }
+ result = startDevice();
+ if (result != AAUDIO_OK) goto error;
+
+ // This should happen at the end of the start.
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+ setState(AAUDIO_STREAM_STATE_STARTED);
+ mThreadEnabled.store(true);
+ result = mTimestampThread.start(this);
+ if (result != AAUDIO_OK) goto error;
+
+ return result;
+
+error:
+ disconnect();
return result;
}
@@ -197,13 +210,13 @@
sendCurrentTimestamp();
mThreadEnabled.store(false);
- result = mAAudioThread.stop();
+ result = mTimestampThread.stop();
if (result != AAUDIO_OK) {
disconnect();
return result;
}
sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
- mState = AAUDIO_STREAM_STATE_PAUSED;
+ setState(AAUDIO_STREAM_STATE_PAUSED);
return result;
}
@@ -234,7 +247,7 @@
}
sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
- mState = AAUDIO_STREAM_STATE_STOPPED;
+ setState(AAUDIO_STREAM_STATE_STOPPED);
return result;
}
@@ -242,20 +255,20 @@
aaudio_result_t result = AAUDIO_OK;
// clear flag that tells thread to loop
if (mThreadEnabled.exchange(false)) {
- result = mAAudioThread.stop();
+ result = mTimestampThread.stop();
}
return result;
}
aaudio_result_t AAudioServiceStreamBase::flush() {
- if (mState != AAUDIO_STREAM_STATE_PAUSED) {
+ if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
ALOGE("AAudioServiceStreamBase::flush() stream not paused, state = %s",
AAudio_convertStreamStateToText(mState));
return AAUDIO_ERROR_INVALID_STATE;
}
// Data will get flushed when the client receives the FLUSHED event.
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
- mState = AAUDIO_STREAM_STATE_FLUSHED;
+ setState(AAUDIO_STREAM_STATE_FLUSHED);
return AAUDIO_OK;
}
@@ -283,9 +296,9 @@
}
void AAudioServiceStreamBase::disconnect() {
- if (mState != AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
- mState = AAUDIO_STREAM_STATE_DISCONNECTED;
+ setState(AAUDIO_STREAM_STATE_DISCONNECTED);
}
}
@@ -321,6 +334,9 @@
aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
+ ALOGV("sendCurrentTimestamp() SERVICE %8lld at %lld",
+ (long long) command.timestamp.position,
+ (long long) command.timestamp.timestamp);
command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE;
result = writeUpMessageQueue(&command);
@@ -329,13 +345,16 @@
result = getHardwareTimestamp(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
+ ALOGV("sendCurrentTimestamp() HARDWARE %8lld at %lld",
+ (long long) command.timestamp.position,
+ (long long) command.timestamp.timestamp);
command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE;
result = writeUpMessageQueue(&command);
}
}
}
- if (result == AAUDIO_ERROR_UNAVAILABLE) {
+ if (result == AAUDIO_ERROR_UNAVAILABLE) { // TODO review best error code
result = AAUDIO_OK; // just not available yet, try again later
}
return result;
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 301795d..6f61401 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -191,6 +191,12 @@
mState = state;
}
+ /**
+ * Device specific startup.
+ * @return AAUDIO_OK or negative error.
+ */
+ virtual aaudio_result_t startDevice();
+
aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
aaudio_result_t sendCurrentTimestamp();
@@ -213,7 +219,7 @@
SharedRingBuffer* mUpMessageQueue;
std::mutex mUpMessageQueueLock;
- AAudioThread mAAudioThread;
+ AAudioThread mTimestampThread;
// This is used by one thread to tell another thread to exit. So it must be atomic.
std::atomic<bool> mThreadEnabled{false};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 47041c5..a629ed6 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -50,7 +50,7 @@
}
aaudio_result_t AAudioServiceStreamMMAP::close() {
- if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
return AAUDIO_OK;
}
@@ -67,7 +67,6 @@
aaudio_result_t result = AAudioServiceStreamBase::open(request,
AAUDIO_SHARING_MODE_EXCLUSIVE);
if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamBase open returned %d", result);
return result;
}
@@ -85,13 +84,10 @@
/**
* Start the flow of data.
*/
-aaudio_result_t AAudioServiceStreamMMAP::start() {
- if (isRunning()) {
- return AAUDIO_OK;
- }
-
- aaudio_result_t result = AAudioServiceStreamBase::start();
+aaudio_result_t AAudioServiceStreamMMAP::startDevice() {
+ aaudio_result_t result = AAudioServiceStreamBase::startDevice();
if (!mInService && result == AAUDIO_OK) {
+ // Note that this can sometimes take 200 to 300 msec for a cold start!
result = startClient(mMmapClient, &mClientHandle);
}
return result;
@@ -126,6 +122,7 @@
aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle) {
+ // Start the client on behalf of the application. Generate a new porthandle.
aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
return result;
}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index bf0aab3..83cd2ef 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -53,14 +53,6 @@
aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
/**
- * Start the flow of audio data.
- *
- * This is not guaranteed to be synchronous but it currently is.
- * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
- */
- aaudio_result_t start() override;
-
- /**
* Stop the flow of data so that start() can resume without loss of data.
*
* This is not guaranteed to be synchronous but it currently is.
@@ -89,6 +81,12 @@
aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+ /**
+ * Device specific startup.
+ * @return AAUDIO_OK or negative error.
+ */
+ aaudio_result_t startDevice() override;
+
private:
bool mInService = false;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 834f39f..348d407 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -237,9 +237,15 @@
aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positionFrames,
int64_t *timeNanos) {
- aaudio_result_t result = mServiceEndpoint->getTimestamp(positionFrames, timeNanos);
+ int64_t position = 0;
+ aaudio_result_t result = mServiceEndpoint->getTimestamp(&position, timeNanos);
if (result == AAUDIO_OK) {
- *positionFrames -= mTimestampPositionOffset.load(); // Offset from shared MMAP stream
+ int64_t offset = mTimestampPositionOffset.load();
+ // TODO, do not go below starting value
+ position -= offset; // Offset from shared MMAP stream
+ ALOGV("getHardwareTimestamp() %8lld = %8lld - %8lld",
+ (long long) position, (long long) (position + offset), (long long) offset);
}
+ *positionFrames = position;
return result;
}
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 8891aba..952acd7 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -840,7 +840,7 @@
}
const bool supports_stop_all =
- (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() == ENOSYS);
+ (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() != -ENOSYS);
for (size_t i = 0; i < mModels.size(); i++) {
sp<Model> model = mModels.valueAt(i);