Merge "add tuner delay hint to TunerFilter"
diff --git a/media/extractors/TEST_MAPPING b/media/extractors/TEST_MAPPING
index 4984b8f..a7c2cfe 100644
--- a/media/extractors/TEST_MAPPING
+++ b/media/extractors/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"presubmit": [
+ {
+ "name": "CtsMediaTranscodingTestCases"
+ }
// TODO(b/153661591) enable test once the bug is fixed
// This tests the extractor path
// {
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 9617556..d9fddf3 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -25,6 +25,7 @@
#include <android/media/IAudioPolicyService.h>
#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
#include <audio_utils/clock.h>
#include <audio_utils/primitives.h>
#include <binder/IPCThreadState.h>
@@ -44,6 +45,7 @@
static const int kMaxLoopCountNotifications = 32;
using ::android::aidl_utils::statusTFromBinderStatus;
+using ::android::base::StringPrintf;
namespace android {
// ---------------------------------------------------------------------------
@@ -539,6 +541,7 @@
uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(attributionSource.pid));
sp<IAudioTrackCallback> _callback = callback.promote();
+ std::string errorMessage;
// Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
"flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
@@ -563,32 +566,34 @@
case TRANSFER_CALLBACK:
case TRANSFER_SYNC_NOTIF_CALLBACK:
if (_callback == nullptr || sharedBuffer != 0) {
- ALOGE("%s(): Transfer type %s but callback == nullptr || sharedBuffer != 0",
+ errorMessage = StringPrintf(
+ "%s: Transfer type %s but callback == nullptr || sharedBuffer != 0",
convertTransferToText(transferType), __func__);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
break;
case TRANSFER_OBTAIN:
case TRANSFER_SYNC:
if (sharedBuffer != 0) {
- ALOGE("%s(): Transfer type TRANSFER_OBTAIN but sharedBuffer != 0", __func__);
+ errorMessage = StringPrintf(
+ "%s: Transfer type TRANSFER_OBTAIN but sharedBuffer != 0", __func__);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
break;
case TRANSFER_SHARED:
if (sharedBuffer == 0) {
- ALOGE("%s(): Transfer type TRANSFER_SHARED but sharedBuffer == 0", __func__);
+ errorMessage = StringPrintf(
+ "%s: Transfer type TRANSFER_SHARED but sharedBuffer == 0", __func__);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
break;
default:
- ALOGE("%s(): Invalid transfer type %d",
- __func__, transferType);
+ errorMessage = StringPrintf("%s: Invalid transfer type %d", __func__, transferType);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
mSharedBuffer = sharedBuffer;
mTransfer = transferType;
@@ -602,9 +607,9 @@
// invariant that mAudioTrack != 0 is true only after set() returns successfully
if (mAudioTrack != 0) {
- ALOGE("%s(): Track already in use", __func__);
+ errorMessage = StringPrintf("%s: Track already in use", __func__);
status = INVALID_OPERATION;
- goto exit;
+ goto error;
}
// handle default values first.
@@ -613,9 +618,9 @@
}
if (pAttributes == NULL) {
if (uint32_t(streamType) >= AUDIO_STREAM_PUBLIC_CNT) {
- ALOGE("%s(): Invalid stream type %d", __func__, streamType);
+ errorMessage = StringPrintf("%s: Invalid stream type %d", __func__, streamType);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
mOriginalStreamType = streamType;
@@ -639,16 +644,16 @@
// validate parameters
if (!audio_is_valid_format(format)) {
- ALOGE("%s(): Invalid format %#x", __func__, format);
+ errorMessage = StringPrintf("%s: Invalid format %#x", __func__, format);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
mFormat = format;
if (!audio_is_output_channel(channelMask)) {
- ALOGE("%s(): Invalid channel mask %#x", __func__, channelMask);
+ errorMessage = StringPrintf("%s: Invalid channel mask %#x", __func__, channelMask);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
mChannelMask = channelMask;
channelCount = audio_channel_count_from_out_mask(channelMask);
@@ -687,8 +692,10 @@
// sampling rate must be specified for direct outputs
if (sampleRate == 0 && (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+ errorMessage = StringPrintf(
+ "%s: sample rate must be specified for direct outputs", __func__);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
mSampleRate = sampleRate;
mOriginalSampleRate = sampleRate;
@@ -718,16 +725,17 @@
mNotificationsPerBufferReq = 0;
} else {
if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
- ALOGE("%s(): notificationFrames=%d not permitted for non-fast track",
+ errorMessage = StringPrintf(
+ "%s: notificationFrames=%d not permitted for non-fast track",
__func__, notificationFrames);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
if (frameCount > 0) {
ALOGE("%s(): notificationFrames=%d not permitted with non-zero frameCount=%zu",
__func__, notificationFrames, frameCount);
status = BAD_VALUE;
- goto exit;
+ goto error;
}
mNotificationFramesReq = 0;
const uint32_t minNotificationsPerBuffer = 1;
@@ -772,6 +780,7 @@
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
}
+ // We do not goto error to prevent double-logging errors.
goto exit;
}
@@ -805,6 +814,12 @@
mFramesWrittenAtRestore = -1; // -1 is a unique initializer.
mVolumeHandler = new media::VolumeHandler();
+error:
+ if (status != NO_ERROR) {
+ ALOGE_IF(!errorMessage.empty(), "%s", errorMessage.c_str());
+ reportError(status, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE, errorMessage.c_str());
+ }
+ // fall through
exit:
mStatus = status;
return status;
@@ -1832,12 +1847,13 @@
{
status_t status;
bool callbackAdded = false;
+ std::string errorMessage;
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
if (audioFlinger == 0) {
- ALOGE("%s(%d): Could not get audioflinger",
+ errorMessage = StringPrintf("%s(%d): Could not get audioflinger",
__func__, mPortId);
- status = NO_INIT;
+ status = DEAD_OBJECT;
goto exit;
}
@@ -1914,10 +1930,11 @@
}
if (status != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
- ALOGE("%s(%d): AudioFlinger could not create track, status: %d output %d",
+ errorMessage = StringPrintf(
+ "%s(%d): AudioFlinger could not create track, status: %d output %d",
__func__, mPortId, status, output.outputId);
if (status == NO_ERROR) {
- status = NO_INIT;
+ status = INVALID_OPERATION; // device not ready
}
goto exit;
}
@@ -1948,8 +1965,8 @@
output.audioTrack->getCblk(&sfr);
sp<IMemory> iMem = VALUE_OR_FATAL(aidl2legacy_NullableSharedFileRegion_IMemory(sfr));
if (iMem == 0) {
- ALOGE("%s(%d): Could not get control block", __func__, mPortId);
- status = NO_INIT;
+ errorMessage = StringPrintf("%s(%d): Could not get control block", __func__, mPortId);
+ status = FAILED_TRANSACTION;
goto exit;
}
// TODO: Using unsecurePointer() has some associated security pitfalls
@@ -1958,8 +1975,9 @@
// issue (e.g. by copying).
void *iMemPointer = iMem->unsecurePointer();
if (iMemPointer == NULL) {
- ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId);
- status = NO_INIT;
+ errorMessage = StringPrintf(
+ "%s(%d): Could not get control block pointer", __func__, mPortId);
+ status = FAILED_TRANSACTION;
goto exit;
}
// invariant that mAudioTrack != 0 is true only after set() returns successfully
@@ -2017,8 +2035,10 @@
// issue (e.g. by copying).
buffers = mSharedBuffer->unsecurePointer();
if (buffers == NULL) {
- ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId);
- status = NO_INIT;
+ errorMessage = StringPrintf(
+ "%s(%d): Could not get buffer pointer", __func__, mPortId);
+ ALOGE("%s", errorMessage.c_str());
+ status = FAILED_TRANSACTION;
goto exit;
}
}
@@ -2116,17 +2136,44 @@
}
exit:
- if (status != NO_ERROR && callbackAdded) {
- // note: mOutput is always valid is callbackAdded is true
- AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
+ if (status != NO_ERROR) {
+ if (callbackAdded) {
+ // note: mOutput is always valid is callbackAdded is true
+ AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
+ }
+ ALOGE_IF(!errorMessage.empty(), "%s", errorMessage.c_str());
+ reportError(status, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE, errorMessage.c_str());
}
-
mStatus = status;
// sp<IAudioTrack> track destructor will cause releaseOutput() to be called by AudioFlinger
return status;
}
+void AudioTrack::reportError(status_t status, const char *event, const char *message) const
+{
+ if (status == NO_ERROR) return;
+ // We report error on the native side because some callers do not come
+ // from Java.
+ mediametrics::LogItem(std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) + "error")
+ .set(AMEDIAMETRICS_PROP_EVENT, event)
+ .set(AMEDIAMETRICS_PROP_ERROR, mediametrics::statusToErrorString(status))
+ .set(AMEDIAMETRICS_PROP_ERRORMESSAGE, message)
+ .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str())
+ .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
+ .set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(mAttributes.content_type).c_str())
+ .set(AMEDIAMETRICS_PROP_USAGE, toString(mAttributes.usage).c_str())
+ .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId)
+ .set(AMEDIAMETRICS_PROP_ENCODING, toString(mFormat).c_str())
+ .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
+ .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mReqFrameCount) // requested frame count
+ // the following are NOT immutable
+ .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
+ .set(AMEDIAMETRICS_PROP_PLAYBACK_SPEED, (double)mPlaybackRate.mSpeed)
+ .set(AMEDIAMETRICS_PROP_PLAYBACK_PITCH, (double)mPlaybackRate.mPitch)
+ .record();
+}
+
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig)
{
if (audioBuffer == NULL) {
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index dd4d2da..ee262f3 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -40,7 +40,7 @@
// ----------------------------------------------------------------------------
-class AudioEffect : public RefBase
+class AudioEffect : public virtual RefBase
{
public:
diff --git a/media/libaudioclient/include/media/AudioIoDescriptor.h b/media/libaudioclient/include/media/AudioIoDescriptor.h
index ef729ed..405ec7d 100644
--- a/media/libaudioclient/include/media/AudioIoDescriptor.h
+++ b/media/libaudioclient/include/media/AudioIoDescriptor.h
@@ -39,7 +39,7 @@
// audio input/output descriptor used to cache output configurations in client process to avoid
// frequent calls through IAudioFlinger
-class AudioIoDescriptor : public RefBase {
+class AudioIoDescriptor : public virtual RefBase {
public:
AudioIoDescriptor() = default;
// For AUDIO_{INPUT|OUTPUT}_CLOSED events.
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 7f5c29a..368b20a 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -538,7 +538,7 @@
// A listener for capture state changes.
- class CaptureStateListener : public RefBase {
+ class CaptureStateListener : public virtual RefBase {
public:
// Called whenever capture state changes.
virtual void onStateChanged(bool active) = 0;
@@ -563,7 +563,7 @@
// ----------------------------------------------------------------------------
- class AudioVolumeGroupCallback : public RefBase
+ class AudioVolumeGroupCallback : public virtual RefBase
{
public:
@@ -578,7 +578,7 @@
static status_t addAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback);
static status_t removeAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback);
- class AudioPortCallback : public RefBase
+ class AudioPortCallback : public virtual RefBase
{
public:
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 1bc2c32..56d341e 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1489,6 +1489,9 @@
std::string mMetricsId; // GUARDED_BY(mLock), could change in createTrack_l().
std::string mCallerName; // for example "aaudio"
+ // report error to mediametrics.
+ void reportError(status_t status, const char *event, const char *message) const;
+
private:
class AudioTrackCallback : public media::BnAudioTrackCallback {
public:
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 485648d..b4ee4dc 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -65,7 +65,7 @@
// ----------------------------------------------------------------------------
-class IAudioFlinger : public RefBase {
+class IAudioFlinger : public virtual RefBase {
public:
static constexpr char DEFAULT_SERVICE_NAME[] = "media.audio_flinger";
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 92140d5..86e0566 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -4101,7 +4101,7 @@
}
if (outputDesc != nullptr) {
audio_port_config srcMixPortConfig = {};
- outputDesc->toAudioPortConfig(&srcMixPortConfig, &patch->sources[0]);
+ outputDesc->toAudioPortConfig(&srcMixPortConfig, nullptr);
// for volume control, we may need a valid stream
srcMixPortConfig.ext.mix.usecase.stream = sourceDesc != nullptr ?
sourceDesc->stream() : AUDIO_STREAM_PATCH;
@@ -5958,14 +5958,20 @@
client->getSecondaryOutputs().begin(),
client->getSecondaryOutputs().end(),
secondaryDescs.begin(), secondaryDescs.end())) {
- std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
- std::vector<audio_io_handle_t> secondaryOutputIds;
- for (const auto& secondaryDesc : secondaryDescs) {
- secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
- weakSecondaryDescs.push_back(secondaryDesc);
+ if (!audio_is_linear_pcm(client->config().format)) {
+ // If the format is not PCM, the tracks should be invalidated to get correct
+ // behavior when the secondary output is changed.
+ streamsToInvalidate.insert(client->stream());
+ } else {
+ std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
+ std::vector<audio_io_handle_t> secondaryOutputIds;
+ for (const auto &secondaryDesc: secondaryDescs) {
+ secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
+ weakSecondaryDescs.push_back(secondaryDesc);
+ }
+ trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
+ client->setSecondaryOutputs(std::move(weakSecondaryDescs));
}
- trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
- client->setSecondaryOutputs(std::move(weakSecondaryDescs));
}
}
}
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index 0b5ad79..8a67b0a 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -19,12 +19,14 @@
LOCAL_SHARED_LIBRARIES := \
libbase \
+ libbinder \
libcutils \
libcameraservice \
libhidlbase \
liblog \
libcamera_client \
libcamera_metadata \
+ libgui \
libui \
libutils \
libjpeg \
diff --git a/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp b/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp
new file mode 100644
index 0000000..6586b74
--- /dev/null
+++ b/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2021 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 "PreviewSchedulerTest"
+
+#include <chrono>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/Surface.h>
+
+#include "../device3/Camera3OutputStream.h"
+#include "../device3/PreviewFrameScheduler.h"
+
+using namespace android;
+using namespace android::camera3;
+
+// Consumer buffer available listener
+class SimpleListener : public BufferItemConsumer::FrameAvailableListener {
+public:
+ SimpleListener(size_t frameCount): mFrameCount(frameCount) {}
+
+ void waitForFrames() {
+ Mutex::Autolock lock(mMutex);
+ while (mFrameCount > 0) {
+ mCondition.wait(mMutex);
+ }
+ }
+
+ void onFrameAvailable(const BufferItem& /*item*/) override {
+ Mutex::Autolock lock(mMutex);
+ if (mFrameCount > 0) {
+ mFrameCount--;
+ mCondition.signal();
+ }
+ }
+
+ void reset(size_t frameCount) {
+ Mutex::Autolock lock(mMutex);
+ mFrameCount = frameCount;
+ }
+private:
+ size_t mFrameCount;
+ Mutex mMutex;
+ Condition mCondition;
+};
+
+// Test the PreviewFrameScheduler functionatliy of re-timing buffers
+TEST(PreviewSchedulerTest, BasicPreviewSchedulerTest) {
+ const int ID = 0;
+ const int FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ const uint32_t WIDTH = 640;
+ const uint32_t HEIGHT = 480;
+ const int32_t TRANSFORM = 0;
+ const nsecs_t T_OFFSET = 0;
+ const android_dataspace DATASPACE = HAL_DATASPACE_UNKNOWN;
+ const camera_stream_rotation_t ROTATION = CAMERA_STREAM_ROTATION_0;
+ const String8 PHY_ID;
+ const std::unordered_set<int32_t> PIX_MODES;
+ const int BUFFER_COUNT = 4;
+ const int TOTAL_BUFFER_COUNT = BUFFER_COUNT * 2;
+
+ // Create buffer queue
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ ASSERT_NE(producer, nullptr);
+ ASSERT_NE(consumer, nullptr);
+ ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(WIDTH, HEIGHT));
+
+ // Set up consumer
+ sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumer,
+ GRALLOC_USAGE_HW_COMPOSER, BUFFER_COUNT);
+ ASSERT_NE(bufferConsumer, nullptr);
+ sp<SimpleListener> consumerListener = new SimpleListener(BUFFER_COUNT);
+ bufferConsumer->setFrameAvailableListener(consumerListener);
+
+ // Set up producer
+ sp<Surface> surface = new Surface(producer);
+ sp<StubProducerListener> listener = new StubProducerListener();
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+ sp<ANativeWindow> anw(surface);
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), TOTAL_BUFFER_COUNT));
+
+ // Create Camera3OutputStream and PreviewFrameScheduler
+ sp<Camera3OutputStream> stream = new Camera3OutputStream(ID, surface, WIDTH, HEIGHT,
+ FORMAT, DATASPACE, ROTATION, T_OFFSET, PHY_ID, PIX_MODES);
+ ASSERT_NE(stream, nullptr);
+ std::unique_ptr<PreviewFrameScheduler> scheduler =
+ std::make_unique<PreviewFrameScheduler>(*stream, surface);
+ ASSERT_NE(scheduler, nullptr);
+
+ // The pair of nsecs_t: camera timestamp delta (negative means in the past) and frame interval
+ const std::pair<nsecs_t, nsecs_t> inputTimestamps[][BUFFER_COUNT] = {
+ // 30fps, no interval
+ {{-100000000LL, 0}, {-66666667LL, 0},
+ {-33333333LL, 0}, {0, 0}},
+ // 30fps, 33ms interval
+ {{-100000000LL, 33333333LL}, {-66666667LL, 33333333LL},
+ {-33333333LL, 33333333LL}, {0, 0}},
+ // 30fps, variable interval
+ {{-100000000LL, 16666667LL}, {-66666667LL, 33333333LL},
+ {-33333333LL, 50000000LL}, {0, 0}},
+ // 60fps, 16.7ms interval
+ {{-50000000LL, 16666667LL}, {-33333333LL, 16666667LL},
+ {-16666667LL, 16666667LL}, {0, 0}},
+ // 60fps, variable interval
+ {{-50000000LL, 8666667LL}, {-33333333LL, 19666667LL},
+ {-16666667LL, 20666667LL}, {0, 0}},
+ };
+
+ const nsecs_t USE_AS_IS = -1; // Use the producer set timestamp
+ const nsecs_t USE_OVERRIDE = -2; // Use the scheduler overridden timestamp
+ const nsecs_t expectedTimestamps[][BUFFER_COUNT] = {
+ // 30fps, no interval: first 2 frames as is, and last 2 frames are
+ // overridden.
+ {USE_AS_IS, USE_AS_IS, USE_OVERRIDE, USE_OVERRIDE},
+ // 30fps, 33ms interval: all frames are overridden
+ {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+ // 30fps, variable interval: all frames are overridden
+ {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+ // 60fps, 16.7ms interval: all frames are overridden
+ {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+ // 60fps, variable interval: all frames are overridden
+ {USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE, USE_OVERRIDE},
+ };
+
+ // Go through different use cases, and check the buffer timestamp
+ size_t iterations = sizeof(inputTimestamps)/sizeof(inputTimestamps[0]);
+ for (size_t i = 0; i < iterations; i++) {
+ // Space out different test sets to reset the frame scheduler
+ nsecs_t timeBase = systemTime() - s2ns(1) * (iterations - i);
+ nsecs_t lastQueueTime = 0;
+ nsecs_t duration = 0;
+ for (size_t j = 0; j < BUFFER_COUNT; j++) {
+ ANativeWindowBuffer* buffer = nullptr;
+ int fenceFd;
+ ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buffer, &fenceFd));
+
+ // Sleep to space out queuePreviewBuffer
+ nsecs_t currentTime = systemTime();
+ if (duration > 0 && duration > currentTime - lastQueueTime) {
+ std::this_thread::sleep_for(
+ std::chrono::nanoseconds(duration + lastQueueTime - currentTime));
+ }
+ nsecs_t timestamp = timeBase + inputTimestamps[i][j].first;
+ ASSERT_EQ(NO_ERROR,
+ scheduler->queuePreviewBuffer(timestamp, TRANSFORM, buffer, fenceFd));
+
+ lastQueueTime = systemTime();
+ duration = inputTimestamps[i][j].second;
+ }
+
+ // Collect output timestamps, making sure they are either set by
+ // producer, or set by the scheduler.
+ consumerListener->waitForFrames();
+ nsecs_t outputTimestamps[BUFFER_COUNT];
+ for (size_t j = 0; j < BUFFER_COUNT; j++) {
+ BufferItem bufferItem;
+ ASSERT_EQ(NO_ERROR, bufferConsumer->acquireBuffer(&bufferItem, 0/*presentWhen*/));
+
+ outputTimestamps[j] = bufferItem.mTimestamp;
+ ALOGV("%s: [%zu][%zu]: input: %" PRId64 ", output: %" PRId64, __FUNCTION__,
+ i, j, timeBase + inputTimestamps[i][j].first, bufferItem.mTimestamp);
+ if (expectedTimestamps[i][j] == USE_OVERRIDE) {
+ ASSERT_GT(bufferItem.mTimestamp, inputTimestamps[i][j].first);
+ } else if (expectedTimestamps[i][j] == USE_AS_IS) {
+ ASSERT_EQ(bufferItem.mTimestamp, timeBase + inputTimestamps[i][j].first);
+ }
+
+ ASSERT_EQ(NO_ERROR, bufferConsumer->releaseBuffer(bufferItem));
+ }
+
+ // Check the output timestamp intervals are aligned with input intervals
+ const nsecs_t SHIFT_THRESHOLD = ms2ns(2);
+ for (size_t j = 0; j < BUFFER_COUNT - 1; j ++) {
+ if (expectedTimestamps[i][j] == USE_OVERRIDE &&
+ expectedTimestamps[i][j+1] == USE_OVERRIDE) {
+ nsecs_t interval_shift = outputTimestamps[j+1] - outputTimestamps[j] -
+ (inputTimestamps[i][j+1].first - inputTimestamps[i][j].first);
+ ASSERT_LE(std::abs(interval_shift), SHIFT_THRESHOLD);
+ }
+ }
+
+ consumerListener->reset(BUFFER_COUNT);
+ }
+
+ // Disconnect the surface
+ ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
diff --git a/services/mediaextractor/TEST_MAPPING b/services/mediaextractor/TEST_MAPPING
new file mode 100644
index 0000000..7a66eeb
--- /dev/null
+++ b/services/mediaextractor/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsMediaTranscodingTestCases"
+ }
+ ]
+}
diff --git a/services/tuner/TunerFilter.cpp b/services/tuner/TunerFilter.cpp
index 7e38a1e..fb5bfa3 100644
--- a/services/tuner/TunerFilter.cpp
+++ b/services/tuner/TunerFilter.cpp
@@ -346,7 +346,7 @@
return res;
}
-::ndk::ScopedAStatus TunerFilter::createSharedFilter(string* _aidl_return) {
+::ndk::ScopedAStatus TunerFilter::acquireSharedFilterToken(string* _aidl_return) {
Mutex::Autolock _l(mLock);
if (mFilter == nullptr) {
ALOGE("IFilter is not initialized");
@@ -369,7 +369,7 @@
return ::ndk::ScopedAStatus::ok();
}
-::ndk::ScopedAStatus TunerFilter::releaseSharedFilter(const string& /* in_filterToken */) {
+::ndk::ScopedAStatus TunerFilter::freeSharedFilterToken(const string& /* in_filterToken */) {
Mutex::Autolock _l(mLock);
if (mFilter == nullptr) {
ALOGE("IFilter is not initialized");
diff --git a/services/tuner/TunerFilter.h b/services/tuner/TunerFilter.h
index 1ab712c..529c191 100644
--- a/services/tuner/TunerFilter.h
+++ b/services/tuner/TunerFilter.h
@@ -93,8 +93,8 @@
::ndk::ScopedAStatus stop() override;
::ndk::ScopedAStatus flush() override;
::ndk::ScopedAStatus close() override;
- ::ndk::ScopedAStatus createSharedFilter(string* _aidl_return) override;
- ::ndk::ScopedAStatus releaseSharedFilter(const string& in_filterToken) override;
+ ::ndk::ScopedAStatus acquireSharedFilterToken(string* _aidl_return) override;
+ ::ndk::ScopedAStatus freeSharedFilterToken(const string& in_filterToken) override;
::ndk::ScopedAStatus getFilterType(DemuxFilterType* _aidl_return) override;
::ndk::ScopedAStatus setDelayHint(const FilterDelayHint& in_hint) override;
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
index 454634f..dc40f03 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
@@ -102,19 +102,19 @@
void close();
/**
- * Create a new SharedFilter instance.
+ * Acquire a new SharedFilter token.
*
* @return a token of the newly created SharedFilter instance.
*/
- String createSharedFilter();
+ String acquireSharedFilterToken();
/**
- * Release a SharedFilter instance.
+ * Free a SharedFilter token.
*
- * @param filterToken the SharedFilter will be released.
+ * @param filterToken the SharedFilter token will be released.
* @return a token of the newly created SharedFilter instance.
*/
- void releaseSharedFilter(in String filterToken);
+ void freeSharedFilterToken(in String filterToken);
/**
* Get filter type.
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index 879cd84..7b76093 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -19,6 +19,7 @@
#include "TunerHidlFilter.h"
#include <aidl/android/hardware/tv/tuner/Constant.h>
+#include <aidl/android/hardware/tv/tuner/DemuxScIndex.h>
#include <aidl/android/hardware/tv/tuner/Result.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <binder/IPCThreadState.h>
@@ -55,6 +56,7 @@
using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterSettingsFilterSettings;
using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterType;
using ::aidl::android::hardware::tv::tuner::DemuxPid;
+using ::aidl::android::hardware::tv::tuner::DemuxScIndex;
using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettings;
using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettingsFilterSettings;
using ::aidl::android::hardware::tv::tuner::DemuxTsFilterSettings;
@@ -521,7 +523,7 @@
return ::ndk::ScopedAStatus::ok();
}
-::ndk::ScopedAStatus TunerHidlFilter::createSharedFilter(string* _aidl_return) {
+::ndk::ScopedAStatus TunerHidlFilter::acquireSharedFilterToken(string* _aidl_return) {
Mutex::Autolock _l(mLock);
if (mFilter == nullptr) {
ALOGE("IFilter is not initialized");
@@ -545,7 +547,7 @@
return ::ndk::ScopedAStatus::ok();
}
-::ndk::ScopedAStatus TunerHidlFilter::releaseSharedFilter(const string& /* in_filterToken */) {
+::ndk::ScopedAStatus TunerHidlFilter::freeSharedFilterToken(const string& /* in_filterToken */) {
Mutex::Autolock _l(mLock);
if (mFilter == nullptr) {
ALOGE("IFilter is not initialized");
@@ -872,16 +874,26 @@
const DemuxFilterRecordSettings& settings) {
HidlDemuxFilterRecordSettings record{
.tsIndexMask = static_cast<uint32_t>(settings.tsIndexMask),
- .scIndexType = static_cast<HidlDemuxRecordScIndexType>(settings.scIndexType),
};
switch (settings.scIndexMask.getTag()) {
case DemuxFilterScIndexMask::scIndex: {
+ record.scIndexType = static_cast<HidlDemuxRecordScIndexType>(settings.scIndexType);
record.scIndexMask.sc(
static_cast<uint32_t>(settings.scIndexMask.get<DemuxFilterScIndexMask::scIndex>()));
break;
}
+ case DemuxFilterScIndexMask::scAvc: {
+ record.scIndexType = HidlDemuxRecordScIndexType::SC;
+ uint32_t index =
+ static_cast<uint32_t>(settings.scIndexMask.get<DemuxFilterScIndexMask::scAvc>());
+ // HIDL HAL starting from 1 << 4; AIDL starting from 1 << 0.
+ index = index << 4;
+ record.scIndexMask.sc(index);
+ break;
+ }
case DemuxFilterScIndexMask::scHevc: {
+ record.scIndexType = static_cast<HidlDemuxRecordScIndexType>(settings.scIndexType);
record.scIndexMask.scHevc(
static_cast<uint32_t>(settings.scIndexMask.get<DemuxFilterScIndexMask::scHevc>()));
break;
@@ -1100,8 +1112,13 @@
DemuxFilterScIndexMask scIndexMask;
if (tsRecordEvent.scIndexMask.getDiscriminator() ==
HidlDemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::sc) {
- scIndexMask.set<DemuxFilterScIndexMask::scIndex>(
- static_cast<int32_t>(tsRecordEvent.scIndexMask.sc()));
+ int32_t hidlScIndex = static_cast<int32_t>(tsRecordEvent.scIndexMask.sc());
+ if (hidlScIndex <= static_cast<int32_t>(DemuxScIndex::SEQUENCE)) {
+ scIndexMask.set<DemuxFilterScIndexMask::scIndex>(hidlScIndex);
+ } else {
+ // HIDL HAL starting from 1 << 4; AIDL starting from 1 << 0.
+ scIndexMask.set<DemuxFilterScIndexMask::scAvc>(hidlScIndex >> 4);
+ }
} else if (tsRecordEvent.scIndexMask.getDiscriminator() ==
HidlDemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::scHevc) {
scIndexMask.set<DemuxFilterScIndexMask::scHevc>(
diff --git a/services/tuner/hidl/TunerHidlFilter.h b/services/tuner/hidl/TunerHidlFilter.h
index 86b89e5..b8fad22 100644
--- a/services/tuner/hidl/TunerHidlFilter.h
+++ b/services/tuner/hidl/TunerHidlFilter.h
@@ -183,8 +183,8 @@
::ndk::ScopedAStatus stop() override;
::ndk::ScopedAStatus flush() override;
::ndk::ScopedAStatus close() override;
- ::ndk::ScopedAStatus createSharedFilter(string* _aidl_return) override;
- ::ndk::ScopedAStatus releaseSharedFilter(const string& in_filterToken) override;
+ ::ndk::ScopedAStatus acquireSharedFilterToken(string* _aidl_return) override;
+ ::ndk::ScopedAStatus freeSharedFilterToken(const string& in_filterToken) override;
::ndk::ScopedAStatus getFilterType(DemuxFilterType* _aidl_return) override;
::ndk::ScopedAStatus setDelayHint(const FilterDelayHint& in_hint) override;