Merge "Reduce logspam in nudriver"
diff --git a/camera/ndk/ndk_vendor/impl/utils.cpp b/camera/ndk/ndk_vendor/impl/utils.cpp
index 7193006..5d2d47c 100644
--- a/camera/ndk/ndk_vendor/impl/utils.cpp
+++ b/camera/ndk/ndk_vendor/impl/utils.cpp
@@ -70,7 +70,6 @@
return;
}
size_t size = get_camera_metadata_size(src);
- ALOGE("Converting metadata size: %d", (int)size);
dst->setToExternal((uint8_t *) src, size);
return;
}
diff --git a/media/codec2/components/opus/Android.bp b/media/codec2/components/opus/Android.bp
index 240cdb9..0ed141b 100644
--- a/media/codec2/components/opus/Android.bp
+++ b/media/codec2/components/opus/Android.bp
@@ -9,3 +9,14 @@
shared_libs: ["libopus"],
}
+cc_library_shared {
+ name: "libcodec2_soft_opusenc",
+ defaults: [
+ "libcodec2_soft-defaults",
+ "libcodec2_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["C2SoftOpusEnc.cpp"],
+
+ shared_libs: ["libopus"],
+}
diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp
index 2439c3c..3ce1fd6 100644
--- a/media/codec2/components/opus/C2SoftOpusDec.cpp
+++ b/media/codec2/components/opus/C2SoftOpusDec.cpp
@@ -19,10 +19,9 @@
#include <log/log.h>
#include <media/stagefright/foundation/MediaDefs.h>
-
+#include <media/stagefright/foundation/OpusHeader.h>
#include <C2PlatformSupport.h>
#include <SimpleC2Interface.h>
-
#include "C2SoftOpusDec.h"
extern "C" {
@@ -188,16 +187,6 @@
work->workletsProcessed = 1u;
}
-static uint16_t ReadLE16(const uint8_t *data, size_t data_size,
- uint32_t read_offset) {
- if (read_offset + 1 > data_size)
- return 0;
- uint16_t val;
- val = data[read_offset];
- val |= data[read_offset + 1] << 8;
- return val;
-}
-
static const int kRate = 48000;
// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
@@ -216,81 +205,6 @@
static const int kMaxChannelsWithDefaultLayout = 2;
static const uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = { 0, 1 };
-// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header
-static bool ParseOpusHeader(const uint8_t *data, size_t data_size,
- OpusHeader* header) {
- // Size of the Opus header excluding optional mapping information.
- const size_t kOpusHeaderSize = 19;
-
- // Offset to the channel count byte in the Opus header.
- const size_t kOpusHeaderChannelsOffset = 9;
-
- // Offset to the pre-skip value in the Opus header.
- const size_t kOpusHeaderSkipSamplesOffset = 10;
-
- // Offset to the gain value in the Opus header.
- const size_t kOpusHeaderGainOffset = 16;
-
- // Offset to the channel mapping byte in the Opus header.
- const size_t kOpusHeaderChannelMappingOffset = 18;
-
- // Opus Header contains a stream map. The mapping values are in the header
- // beyond the always present |kOpusHeaderSize| bytes of data. The mapping
- // data contains stream count, coupling information, and per channel mapping
- // values:
- // - Byte 0: Number of streams.
- // - Byte 1: Number coupled.
- // - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping
- // values.
- const size_t kOpusHeaderNumStreamsOffset = kOpusHeaderSize;
- const size_t kOpusHeaderNumCoupledOffset = kOpusHeaderNumStreamsOffset + 1;
- const size_t kOpusHeaderStreamMapOffset = kOpusHeaderNumStreamsOffset + 2;
-
- if (data_size < kOpusHeaderSize) {
- ALOGE("Header size is too small.");
- return false;
- }
- header->channels = *(data + kOpusHeaderChannelsOffset);
- if (header->channels <= 0 || header->channels > kMaxChannels) {
- ALOGE("Invalid Header, wrong channel count: %d", header->channels);
- return false;
- }
-
- header->skip_samples = ReadLE16(data,
- data_size,
- kOpusHeaderSkipSamplesOffset);
-
- header->gain_db = static_cast<int16_t>(ReadLE16(data,
- data_size,
- kOpusHeaderGainOffset));
-
- header->channel_mapping = *(data + kOpusHeaderChannelMappingOffset);
- if (!header->channel_mapping) {
- if (header->channels > kMaxChannelsWithDefaultLayout) {
- ALOGE("Invalid Header, missing stream map.");
- return false;
- }
- header->num_streams = 1;
- header->num_coupled = header->channels > 1;
- header->stream_map[0] = 0;
- header->stream_map[1] = 1;
- return true;
- }
- if (data_size < kOpusHeaderStreamMapOffset + header->channels) {
- ALOGE("Invalid stream map; insufficient data for current channel "
- "count: %d", header->channels);
- return false;
- }
- header->num_streams = *(data + kOpusHeaderNumStreamsOffset);
- header->num_coupled = *(data + kOpusHeaderNumCoupledOffset);
- if (header->num_streams + header->num_coupled != header->channels) {
- ALOGE("Inconsistent channel mapping.");
- return false;
- }
- for (int i = 0; i < header->channels; ++i)
- header->stream_map[i] = *(data + kOpusHeaderStreamMapOffset + i);
- return true;
-}
// Convert nanoseconds to number of samples.
static uint64_t ns_to_samples(uint64_t ns, int rate) {
@@ -338,7 +252,19 @@
const uint8_t *data = rView.data() + inOffset;
if (mInputBufferCount < 3) {
if (mInputBufferCount == 0) {
- if (!ParseOpusHeader(data, inSize, &mHeader)) {
+ size_t opusHeadSize = inSize;
+ size_t codecDelayBufSize = 0;
+ size_t seekPreRollBufSize = 0;
+ void *opusHeadBuf = (void *)data;
+ void *codecDelayBuf = NULL;
+ void *seekPreRollBuf = NULL;
+
+ GetOpusHeaderBuffers(data, inSize, &opusHeadBuf,
+ &opusHeadSize, &codecDelayBuf,
+ &codecDelayBufSize, &seekPreRollBuf,
+ &seekPreRollBufSize);
+
+ if (!ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &mHeader)) {
ALOGE("Encountered error while Parsing Opus Header.");
mSignalledError = true;
work->result = C2_CORRUPTED;
@@ -377,6 +303,20 @@
work->result = C2_CORRUPTED;
return;
}
+
+ if (codecDelayBuf && codecDelayBufSize == 8) {
+ uint64_t value;
+ memcpy(&value, codecDelayBuf, sizeof(uint64_t));
+ mCodecDelay = ns_to_samples(value, kRate);
+ mSamplesToDiscard = mCodecDelay;
+ ++mInputBufferCount;
+ }
+ if (seekPreRollBuf && seekPreRollBufSize == 8) {
+ uint64_t value;
+ memcpy(&value, codecDelayBuf, sizeof(uint64_t));
+ mSeekPreRoll = ns_to_samples(value, kRate);
+ ++mInputBufferCount;
+ }
} else {
if (inSize < 8) {
ALOGE("Input sample size is too small.");
@@ -392,29 +332,30 @@
}
else {
mSeekPreRoll = samples;
-
- ALOGI("Configuring decoder: %d Hz, %d channels",
- kRate, mHeader.channels);
- C2StreamSampleRateInfo::output sampleRateInfo(0u, kRate);
- C2StreamChannelCountInfo::output channelCountInfo(0u, mHeader.channels);
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- c2_status_t err = mIntf->config(
- { &sampleRateInfo, &channelCountInfo },
- C2_MAY_BLOCK,
- &failures);
- if (err == OK) {
- work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo));
- work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo));
- } else {
- ALOGE("Config Update failed");
- mSignalledError = true;
- work->result = C2_CORRUPTED;
- return;
- }
}
}
++mInputBufferCount;
+ if (mInputBufferCount == 3) {
+ ALOGI("Configuring decoder: %d Hz, %d channels",
+ kRate, mHeader.channels);
+ C2StreamSampleRateInfo::output sampleRateInfo(0u, kRate);
+ C2StreamChannelCountInfo::output channelCountInfo(0u, mHeader.channels);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config(
+ { &sampleRateInfo, &channelCountInfo },
+ C2_MAY_BLOCK,
+ &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo));
+ work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo));
+ } else {
+ ALOGE("Config Update failed");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ }
fillEmptyWork(work);
if (eos) {
mSignalledOutputEos = true;
diff --git a/media/codec2/components/opus/C2SoftOpusDec.h b/media/codec2/components/opus/C2SoftOpusDec.h
index 92b7426..b0715ac 100644
--- a/media/codec2/components/opus/C2SoftOpusDec.h
+++ b/media/codec2/components/opus/C2SoftOpusDec.h
@@ -24,16 +24,6 @@
namespace android {
-struct OpusHeader {
- int channels;
- int skip_samples;
- int channel_mapping;
- int num_streams;
- int num_coupled;
- int16_t gain_db;
- uint8_t stream_map[8];
-};
-
struct C2SoftOpusDec : public SimpleC2Component {
class IntfImpl;
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp
new file mode 100644
index 0000000..d6ed5ff
--- /dev/null
+++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2019 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 "C2SoftOpusEnc"
+#include <utils/Log.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/OpusHeader.h>
+#include "C2SoftOpusEnc.h"
+
+extern "C" {
+ #include <opus.h>
+ #include <opus_multistream.h>
+}
+
+#define DEFAULT_FRAME_DURATION_MS 20
+namespace android {
+
+constexpr char COMPONENT_NAME[] = "c2.android.opus.encoder";
+
+class C2SoftOpusEnc::IntfImpl : public C2InterfaceHelper {
+public:
+ explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+ : C2InterfaceHelper(helper) {
+
+ setDerivedInstance(this);
+
+ addParameter(
+ DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING)
+ .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatAudio))
+ .build());
+
+ addParameter(
+ DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING)
+ .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatCompressed))
+ .build());
+
+ addParameter(
+ DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING)
+ .withConstValue(AllocSharedString<C2PortMimeConfig::input>(
+ MEDIA_MIMETYPE_AUDIO_RAW))
+ .build());
+
+ addParameter(
+ DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING)
+ .withConstValue(AllocSharedString<C2PortMimeConfig::output>(
+ MEDIA_MIMETYPE_AUDIO_OPUS))
+ .build());
+
+ addParameter(
+ DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING)
+ .withDefault(new C2StreamSampleRateInfo::input(0u, 48000))
+ .withFields({C2F(mSampleRate, value).oneOf({
+ 8000, 12000, 16000, 24000, 48000})})
+ .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+ .build());
+
+ addParameter(
+ DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING)
+ .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
+ .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+ .withSetter((Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps))
+ .build());
+
+ addParameter(
+ DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING)
+ .withDefault(new C2BitrateTuning::output(0u, 128000))
+ .withFields({C2F(mBitrate, value).inRange(500, 512000)})
+ .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+ .build());
+
+ addParameter(
+ DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY)
+ .withDefault(new C2StreamComplexityTuning::output(0u, 10))
+ .withFields({C2F(mComplexity, value).inRange(1, 10)})
+ .withSetter(Setter<decltype(*mComplexity)>::NonStrictValueWithNoDeps)
+ .build());
+
+ addParameter(
+ DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+ .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 3840))
+ .build());
+ }
+
+ uint32_t getSampleRate() const { return mSampleRate->value; }
+ uint32_t getChannelCount() const { return mChannelCount->value; }
+ uint32_t getBitrate() const { return mBitrate->value; }
+ uint32_t getComplexity() const { return mComplexity->value; }
+
+private:
+ std::shared_ptr<C2StreamFormatConfig::input> mInputFormat;
+ std::shared_ptr<C2StreamFormatConfig::output> mOutputFormat;
+ std::shared_ptr<C2PortMimeConfig::input> mInputMediaType;
+ std::shared_ptr<C2PortMimeConfig::output> mOutputMediaType;
+ std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
+ std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
+ std::shared_ptr<C2BitrateTuning::output> mBitrate;
+ std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+ std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftOpusEnc::C2SoftOpusEnc(const char* name, c2_node_id_t id,
+ const std::shared_ptr<IntfImpl>& intfImpl)
+ : SimpleC2Component(
+ std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+ mIntf(intfImpl),
+ mOutputBlock(nullptr),
+ mEncoder(nullptr),
+ mInputBufferPcm16(nullptr),
+ mOutIndex(0u) {
+}
+
+C2SoftOpusEnc::~C2SoftOpusEnc() {
+ onRelease();
+}
+
+c2_status_t C2SoftOpusEnc::onInit() {
+ return initEncoder();
+}
+
+c2_status_t C2SoftOpusEnc::configureEncoder() {
+ unsigned char mono_mapping[256] = {0};
+ unsigned char stereo_mapping[256] = {0, 1};
+ unsigned char surround_mapping[256] = {0, 1, 255};
+ mSampleRate = mIntf->getSampleRate();
+ mChannelCount = mIntf->getChannelCount();
+ uint32_t bitrate = mIntf->getBitrate();
+ int complexity = mIntf->getComplexity();
+ mNumSamplesPerFrame = mSampleRate / (1000 / mFrameDurationMs);
+ mNumPcmBytesPerInputFrame =
+ mChannelCount * mNumSamplesPerFrame * sizeof(int16_t);
+ int err = C2_OK;
+
+ unsigned char* mapping;
+ if (mChannelCount < 2) {
+ mapping = mono_mapping;
+ } else if (mChannelCount == 2) {
+ mapping = stereo_mapping;
+ } else {
+ mapping = surround_mapping;
+ }
+
+ if (mEncoder != nullptr) {
+ opus_multistream_encoder_destroy(mEncoder);
+ }
+
+ mEncoder = opus_multistream_encoder_create(mSampleRate, mChannelCount,
+ 1, 1, mapping, OPUS_APPLICATION_AUDIO, &err);
+ if (err) {
+ ALOGE("Could not create libopus encoder. Error code: %i", err);
+ return C2_CORRUPTED;
+ }
+
+ // Complexity
+ if (opus_multistream_encoder_ctl(
+ mEncoder, OPUS_SET_COMPLEXITY(complexity)) != OPUS_OK) {
+ ALOGE("failed to set complexity");
+ return C2_BAD_VALUE;
+ }
+
+ // DTX
+ if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_DTX(0) != OPUS_OK)) {
+ ALOGE("failed to set dtx");
+ return C2_BAD_VALUE;
+ }
+
+ // Application
+ if (opus_multistream_encoder_ctl(mEncoder,
+ OPUS_SET_APPLICATION(OPUS_APPLICATION_AUDIO)) != OPUS_OK) {
+ ALOGE("failed to set application");
+ return C2_BAD_VALUE;
+ }
+
+ // Signal type
+ if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_SIGNAL(OPUS_AUTO)) !=
+ OPUS_OK) {
+ ALOGE("failed to set signal");
+ return C2_BAD_VALUE;
+ }
+
+ // Unconstrained VBR
+ if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR(0) != OPUS_OK)) {
+ ALOGE("failed to set vbr type");
+ return C2_BAD_VALUE;
+ }
+ if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR_CONSTRAINT(0) !=
+ OPUS_OK)) {
+ ALOGE("failed to set vbr constraint");
+ return C2_BAD_VALUE;
+ }
+
+ // Bitrate
+ if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_BITRATE(bitrate)) !=
+ OPUS_OK) {
+ ALOGE("failed to set bitrate");
+ return C2_BAD_VALUE;
+ }
+
+ // Get codecDelay
+ int32_t lookahead;
+ if (opus_multistream_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&lookahead)) !=
+ OPUS_OK) {
+ ALOGE("failed to get lookahead");
+ return C2_BAD_VALUE;
+ }
+ mCodecDelay = lookahead * 1000000000ll / mSampleRate;
+
+ // Set seek preroll to 80 ms
+ mSeekPreRoll = 80000000;
+ return C2_OK;
+}
+
+c2_status_t C2SoftOpusEnc::initEncoder() {
+ mSignalledEos = false;
+ mSignalledError = false;
+ mHeaderGenerated = false;
+ mIsFirstFrame = true;
+ mEncoderFlushed = false;
+ mBufferAvailable = false;
+ mAnchorTimeStamp = 0ull;
+ mProcessedSamples = 0;
+ mFilledLen = 0;
+ mFrameDurationMs = DEFAULT_FRAME_DURATION_MS;
+ if (!mInputBufferPcm16) {
+ mInputBufferPcm16 =
+ (int16_t*)malloc(kFrameSize * kMaxNumChannels * sizeof(int16_t));
+ }
+ if (!mInputBufferPcm16) return C2_NO_MEMORY;
+
+ /* Default Configurations */
+ c2_status_t status = configureEncoder();
+ return status;
+}
+
+c2_status_t C2SoftOpusEnc::onStop() {
+ mSignalledEos = false;
+ mSignalledError = false;
+ mIsFirstFrame = true;
+ mEncoderFlushed = false;
+ mBufferAvailable = false;
+ mAnchorTimeStamp = 0ull;
+ mProcessedSamples = 0u;
+ mFilledLen = 0;
+ if (mEncoder) {
+ int status = opus_multistream_encoder_ctl(mEncoder, OPUS_RESET_STATE);
+ if (status != OPUS_OK) {
+ ALOGE("OPUS_RESET_STATE failed status = %s", opus_strerror(status));
+ mSignalledError = true;
+ return C2_CORRUPTED;
+ }
+ }
+ if (mOutputBlock) mOutputBlock.reset();
+ mOutputBlock = nullptr;
+
+ return C2_OK;
+}
+
+void C2SoftOpusEnc::onReset() {
+ (void)onStop();
+}
+
+void C2SoftOpusEnc::onRelease() {
+ (void)onStop();
+ if (mInputBufferPcm16) {
+ free(mInputBufferPcm16);
+ mInputBufferPcm16 = nullptr;
+ }
+ if (mEncoder) {
+ opus_multistream_encoder_destroy(mEncoder);
+ mEncoder = nullptr;
+ }
+}
+
+c2_status_t C2SoftOpusEnc::onFlush_sm() {
+ return onStop();
+}
+
+// Drain the encoder to get last frames (if any)
+int C2SoftOpusEnc::drainEncoder(uint8_t* outPtr) {
+ memset((uint8_t *)mInputBufferPcm16 + mFilledLen, 0,
+ (mNumPcmBytesPerInputFrame - mFilledLen));
+ int encodedBytes = opus_multistream_encode(
+ mEncoder, mInputBufferPcm16, mNumSamplesPerFrame, outPtr, kMaxPayload);
+ if (encodedBytes > mOutputBlock->capacity()) {
+ ALOGE("not enough space left to write encoded data, dropping %d bytes",
+ mBytesEncoded);
+ // a fatal error would stop the encoding
+ return -1;
+ }
+ ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes,
+ mNumPcmBytesPerInputFrame);
+ mEncoderFlushed = true;
+ mFilledLen = 0;
+ return encodedBytes;
+}
+
+void C2SoftOpusEnc::process(const std::unique_ptr<C2Work>& work,
+ const std::shared_ptr<C2BlockPool>& pool) {
+ // Initialize output work
+ work->result = C2_OK;
+ work->workletsProcessed = 1u;
+ work->worklets.front()->output.flags = work->input.flags;
+
+ if (mSignalledError || mSignalledEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+ C2ReadView rView = mDummyReadView;
+ size_t inOffset = 0u;
+ size_t inSize = 0u;
+ c2_status_t err = C2_OK;
+ if (!work->input.buffers.empty()) {
+ rView =
+ work->input.buffers[0]->data().linearBlocks().front().map().get();
+ inSize = rView.capacity();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ }
+
+ ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+ inSize, (int)work->input.ordinal.timestamp.peeku(),
+ (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+ if (!mEncoder) {
+ if (initEncoder() != C2_OK) {
+ ALOGE("initEncoder failed with status %d", err);
+ work->result = err;
+ mSignalledError = true;
+ return;
+ }
+ }
+ if (mIsFirstFrame) {
+ mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+ mIsFirstFrame = false;
+ }
+
+ C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+ err = pool->fetchLinearBlock(kMaxPayload, usage, &mOutputBlock);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock for Output failed with status %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+
+ C2WriteView wView = mOutputBlock->map().get();
+ if (wView.error()) {
+ ALOGE("write view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ mOutputBlock.reset();
+ return;
+ }
+
+ size_t inPos = 0;
+ size_t processSize = 0;
+ mBytesEncoded = 0;
+ uint64_t outTimeStamp = 0u;
+ std::shared_ptr<C2Buffer> buffer;
+ uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
+ const uint8_t* inPtr = rView.data() + inOffset;
+
+ class FillWork {
+ public:
+ FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+ const std::shared_ptr<C2Buffer> &buffer)
+ : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {
+ }
+ ~FillWork() = default;
+
+ void operator()(const std::unique_ptr<C2Work>& work) {
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = mOrdinal;
+ work->workletsProcessed = 1u;
+ work->result = C2_OK;
+ if (mBuffer) {
+ work->worklets.front()->output.buffers.push_back(mBuffer);
+ }
+ ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+ mOrdinal.timestamp.peekll(),
+ mOrdinal.frameIndex.peekll(),
+ mBuffer ? "" : "o");
+ }
+
+ private:
+ const uint32_t mFlags;
+ const C2WorkOrdinalStruct mOrdinal;
+ const std::shared_ptr<C2Buffer> mBuffer;
+ };
+
+ C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+
+ if (!mHeaderGenerated) {
+ uint8_t header[AOPUS_UNIFIED_CSD_MAXSIZE];
+ memset(header, 0, sizeof(header));
+ OpusHeader opusHeader;
+ opusHeader.channels = mChannelCount;
+ opusHeader.num_streams = mChannelCount;
+ opusHeader.num_coupled = 0;
+ opusHeader.channel_mapping = ((mChannelCount > 8) ? 255 : (mChannelCount > 2));
+ opusHeader.gain_db = 0;
+ opusHeader.skip_samples = 0;
+ int headerLen = WriteOpusHeaders(opusHeader, mSampleRate, header,
+ sizeof(header), mCodecDelay, mSeekPreRoll);
+
+ std::unique_ptr<C2StreamCsdInfo::output> csd =
+ C2StreamCsdInfo::output::AllocUnique(headerLen, 0u);
+ if (!csd) {
+ ALOGE("CSD allocation failed");
+ mSignalledError = true;
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ ALOGV("put csd, %d bytes", headerLen);
+ memcpy(csd->m.value, header, headerLen);
+ work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+ mHeaderGenerated = true;
+ }
+
+ /*
+ * For buffer size which is not a multiple of mNumPcmBytesPerInputFrame, we will
+ * accumulate the input and keep it. Once the input is filled with expected number
+ * of bytes, we will send it to encoder. mFilledLen manages the bytes of input yet
+ * to be processed. The next call will fill mNumPcmBytesPerInputFrame - mFilledLen
+ * bytes to input and send it to the encoder.
+ */
+ while (inPos < inSize) {
+ const uint8_t* pcmBytes = inPtr + inPos;
+ int filledSamples = mFilledLen / sizeof(int16_t);
+ if ((inPos + (mNumPcmBytesPerInputFrame - mFilledLen)) <= inSize) {
+ processSize = mNumPcmBytesPerInputFrame - mFilledLen;
+ mBufferAvailable = true;
+ } else {
+ processSize = inSize - inPos;
+ mBufferAvailable = false;
+ if (eos) {
+ memset(mInputBufferPcm16 + filledSamples, 0,
+ (mNumPcmBytesPerInputFrame - mFilledLen));
+ mBufferAvailable = true;
+ }
+ }
+ const unsigned nInputSamples = processSize / sizeof(int16_t);
+
+ for (unsigned i = 0; i < nInputSamples; i++) {
+ int32_t data = pcmBytes[2 * i + 1] << 8 | pcmBytes[2 * i];
+ data = ((data & 0xFFFF) ^ 0x8000) - 0x8000;
+ mInputBufferPcm16[i + filledSamples] = data;
+ }
+ inPos += processSize;
+ mFilledLen += processSize;
+ if (!mBufferAvailable) break;
+ uint8_t* outPtr = wView.data() + mBytesEncoded;
+ int encodedBytes =
+ opus_multistream_encode(mEncoder, mInputBufferPcm16,
+ mNumSamplesPerFrame, outPtr, kMaxPayload);
+ ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes,
+ processSize);
+
+ if (encodedBytes < 0 || encodedBytes > kMaxPayload) {
+ ALOGE("opus_encode failed, encodedBytes : %d", encodedBytes);
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ if (buffer) {
+ outOrdinal.frameIndex = mOutIndex++;
+ outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+ cloneAndSend(
+ inputIndex, work,
+ FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+ buffer.reset();
+ }
+ if (encodedBytes > 0) {
+ buffer =
+ createLinearBuffer(mOutputBlock, mBytesEncoded, encodedBytes);
+ }
+ mBytesEncoded += encodedBytes;
+ mProcessedSamples += (filledSamples + nInputSamples);
+ outTimeStamp =
+ mProcessedSamples * 1000000ll / mChannelCount / mSampleRate;
+ if ((processSize + mFilledLen) < mNumPcmBytesPerInputFrame)
+ mEncoderFlushed = true;
+ mFilledLen = 0;
+ }
+
+ uint32_t flags = 0;
+ if (eos) {
+ ALOGV("signalled eos");
+ mSignalledEos = true;
+ if (!mEncoderFlushed) {
+ if (buffer) {
+ outOrdinal.frameIndex = mOutIndex++;
+ outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+ cloneAndSend(
+ inputIndex, work,
+ FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+ buffer.reset();
+ }
+ // drain the encoder for last buffer
+ drainInternal(pool, work);
+ }
+ flags = C2FrameData::FLAG_END_OF_STREAM;
+ }
+ if (buffer) {
+ outOrdinal.frameIndex = mOutIndex++;
+ outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+ FillWork((C2FrameData::flags_t)(flags), outOrdinal, buffer)(work);
+ buffer.reset();
+ }
+ mOutputBlock = nullptr;
+}
+
+c2_status_t C2SoftOpusEnc::drainInternal(
+ const std::shared_ptr<C2BlockPool>& pool,
+ const std::unique_ptr<C2Work>& work) {
+ mBytesEncoded = 0;
+ std::shared_ptr<C2Buffer> buffer = nullptr;
+ C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+ bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+
+ C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+ c2_status_t err = pool->fetchLinearBlock(kMaxPayload, usage, &mOutputBlock);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock for Output failed with status %d", err);
+ return C2_NO_MEMORY;
+ }
+
+ C2WriteView wView = mOutputBlock->map().get();
+ if (wView.error()) {
+ ALOGE("write view map failed %d", wView.error());
+ mOutputBlock.reset();
+ return C2_CORRUPTED;
+ }
+
+ int encBytes = drainEncoder(wView.data());
+ if (encBytes > 0) mBytesEncoded += encBytes;
+ if (mBytesEncoded > 0) {
+ buffer = createLinearBuffer(mOutputBlock, 0, mBytesEncoded);
+ mOutputBlock.reset();
+ }
+ mProcessedSamples += (mNumPcmBytesPerInputFrame / sizeof(int16_t));
+ uint64_t outTimeStamp =
+ mProcessedSamples * 1000000ll / mChannelCount / mSampleRate;
+ outOrdinal.frameIndex = mOutIndex++;
+ outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+ work->worklets.front()->output.flags =
+ (C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0);
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = outOrdinal;
+ work->workletsProcessed = 1u;
+ work->result = C2_OK;
+ if (buffer) {
+ work->worklets.front()->output.buffers.push_back(buffer);
+ }
+ mOutputBlock = nullptr;
+ return C2_OK;
+}
+
+c2_status_t C2SoftOpusEnc::drain(uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool>& pool) {
+ if (drainMode == NO_DRAIN) {
+ ALOGW("drain with NO_DRAIN: no-op");
+ return C2_OK;
+ }
+ if (drainMode == DRAIN_CHAIN) {
+ ALOGW("DRAIN_CHAIN not supported");
+ return C2_OMITTED;
+ }
+ mIsFirstFrame = true;
+ mAnchorTimeStamp = 0ull;
+ mProcessedSamples = 0u;
+ return drainInternal(pool, nullptr);
+}
+
+class C2SoftOpusEncFactory : public C2ComponentFactory {
+public:
+ C2SoftOpusEncFactory()
+ : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+ virtual c2_status_t createComponent(
+ c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(
+ new C2SoftOpusEnc(
+ COMPONENT_NAME, id,
+ std::make_shared<C2SoftOpusEnc::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual c2_status_t createInterface(
+ c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = std::shared_ptr<C2ComponentInterface>(
+ new SimpleInterface<C2SoftOpusEnc::IntfImpl>(
+ COMPONENT_NAME, id,
+ std::make_shared<C2SoftOpusEnc::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftOpusEncFactory() override = default;
+private:
+ std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftOpusEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.h b/media/codec2/components/opus/C2SoftOpusEnc.h
new file mode 100644
index 0000000..69e5240
--- /dev/null
+++ b/media/codec2/components/opus/C2SoftOpusEnc.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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_C2_SOFT_OPUS_ENC_H_
+#define ANDROID_C2_SOFT_OPUS_ENC_H_
+
+#include <atomic>
+#include <SimpleC2Component.h>
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+struct OpusMSEncoder;
+
+namespace android {
+
+struct C2SoftOpusEnc : public SimpleC2Component {
+ class IntfImpl;
+
+ C2SoftOpusEnc(const char *name, c2_node_id_t id,
+ const std::shared_ptr<IntfImpl> &intfImpl);
+ virtual ~C2SoftOpusEnc();
+
+ // From SimpleC2Component
+ c2_status_t onInit() override;
+ c2_status_t onStop() override;
+ void onReset() override;
+ void onRelease() override;
+ c2_status_t onFlush_sm() override;
+ void process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) override;
+ c2_status_t drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) override;
+private:
+ /* OPUS_FRAMESIZE_20_MS */
+ const int kFrameSize = 960;
+ const int kMaxPayload = 4000;
+ const int kMaxNumChannels = 8;
+
+ std::shared_ptr<IntfImpl> mIntf;
+ std::shared_ptr<C2LinearBlock> mOutputBlock;
+
+ OpusMSEncoder* mEncoder;
+ int16_t* mInputBufferPcm16;
+
+ bool mHeaderGenerated;
+ bool mIsFirstFrame;
+ bool mEncoderFlushed;
+ bool mBufferAvailable;
+ bool mSignalledEos;
+ bool mSignalledError;
+ uint32_t mSampleRate;
+ uint32_t mChannelCount;
+ uint32_t mFrameDurationMs;
+ uint64_t mAnchorTimeStamp;
+ uint64_t mProcessedSamples;
+ // Codec delay in ns
+ uint64_t mCodecDelay;
+ // Seek pre-roll in ns
+ uint64_t mSeekPreRoll;
+ int mNumSamplesPerFrame;
+ int mBytesEncoded;
+ int32_t mFilledLen;
+ size_t mNumPcmBytesPerInputFrame;
+ std::atomic_uint64_t mOutIndex;
+ c2_status_t initEncoder();
+ c2_status_t configureEncoder();
+ int drainEncoder(uint8_t* outPtr);
+ c2_status_t drainInternal(const std::shared_ptr<C2BlockPool>& pool,
+ const std::unique_ptr<C2Work>& work);
+
+ C2_DO_NOT_COPY(C2SoftOpusEnc);
+};
+
+} // namespace android
+
+#endif // ANDROID_C2_SOFT_OPUS_ENC_H_
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index cf1f6cf..23939b5 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -638,7 +638,7 @@
LEVEL_VP9_6_1, ///< VP9 Level 6.1
LEVEL_VP9_6_2, ///< VP9 Level 6.2
- // Dolby Vision level
+ // Dolby Vision levels
LEVEL_DV_MAIN_HD_24 = _C2_PL_DV_BASE, ///< Dolby Vision main tier hd24
LEVEL_DV_MAIN_HD_30, ///< Dolby Vision main tier hd30
LEVEL_DV_MAIN_FHD_24, ///< Dolby Vision main tier fhd24
@@ -659,6 +659,7 @@
LEVEL_DV_HIGH_UHD_48, ///< Dolby Vision high tier uhd48
LEVEL_DV_HIGH_UHD_60, ///< Dolby Vision high tier uhd60
+ // AV1 levels
LEVEL_AV1_2 = _C2_PL_AV1_BASE , ///< AV1 Level 2
LEVEL_AV1_2_1, ///< AV1 Level 2.1
LEVEL_AV1_2_2, ///< AV1 Level 2.2
diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
index ba7c2d6..8b1ece3 100644
--- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
+++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
@@ -168,7 +168,7 @@
std::shared_ptr<C2GraphicAllocation> alloc;
C2Handle* handle = WrapNativeCodec2GrallocHandle(
- native_handle_clone(buffer->handle),
+ buffer->handle,
buffer->width, buffer->height,
buffer->format, buffer->usage, buffer->stride);
mAllocatorMutex.lock();
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index 9500aed..03d859a 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -225,18 +225,14 @@
if (omxBuf.mBufferType == OMXBuffer::kBufferTypeANWBuffer
&& omxBuf.mGraphicBuffer != nullptr) {
std::shared_ptr<C2GraphicAllocation> alloc;
- native_handle_t *clonedHandle = native_handle_clone(omxBuf.mGraphicBuffer->handle);
handle = WrapNativeCodec2GrallocHandle(
- clonedHandle,
+ omxBuf.mGraphicBuffer->handle,
omxBuf.mGraphicBuffer->width,
omxBuf.mGraphicBuffer->height,
omxBuf.mGraphicBuffer->format,
omxBuf.mGraphicBuffer->usage,
omxBuf.mGraphicBuffer->stride);
c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
- if (clonedHandle) {
- native_handle_delete(clonedHandle);
- }
if (err != OK) {
return UNKNOWN_ERROR;
}
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 1113ae8..597e8f3 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -578,7 +578,7 @@
ALOGV("VideoNativeMetadata: %dx%d", buffer->width, buffer->height);
C2Handle *handle = WrapNativeCodec2GrallocHandle(
- native_handle_clone(buffer->handle),
+ buffer->handle,
buffer->width,
buffer->height,
buffer->format,
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index 18f2430..e698bf4 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -159,7 +159,7 @@
return xd != nullptr && xd->magic == MAGIC;
}
- static C2HandleGralloc* WrapNativeHandle(
+ static C2HandleGralloc* WrapAndMoveNativeHandle(
const native_handle_t *const handle,
uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) {
@@ -181,6 +181,26 @@
return reinterpret_cast<C2HandleGralloc *>(res);
}
+ static C2HandleGralloc* WrapNativeHandle(
+ const native_handle_t *const handle,
+ uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+ uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) {
+ if (handle == nullptr) {
+ return nullptr;
+ }
+ native_handle_t *clone = native_handle_clone(handle);
+ if (clone == nullptr) {
+ return nullptr;
+ }
+ C2HandleGralloc *res = WrapAndMoveNativeHandle(
+ clone, width, height, format, usage, stride, generation, igbp_id, igbp_slot);
+ if (res == nullptr) {
+ native_handle_close(clone);
+ }
+ native_handle_delete(clone);
+ return res;
+ }
+
static native_handle_t* UnwrapNativeHandle(
const C2Handle *const handle) {
const ExtraData *xd = getExtraData(handle);
@@ -366,7 +386,7 @@
if (mHandle) {
mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot);
}
- mLockedHandle = C2HandleGralloc::WrapNativeHandle(
+ mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle(
mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height,
(uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride,
generation, igbp_id, igbp_slot);
@@ -743,7 +763,7 @@
return;
}
info.stride = stride;
- buffer = std::move(buffers[0]);
+ buffer = buffers[0];
});
if (err != C2_OK) {
return err;
@@ -752,7 +772,7 @@
allocation->reset(new C2AllocationGralloc(
info, mMapper, buffer,
- C2HandleGralloc::WrapNativeHandle(
+ C2HandleGralloc::WrapAndMoveNativeHandle(
buffer.getNativeHandle(),
info.mapperInfo.width, info.mapperInfo.height,
(uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride,
diff --git a/media/codec2/vndk/C2Config.cpp b/media/codec2/vndk/C2Config.cpp
index 782bec5..8a27088 100644
--- a/media/codec2/vndk/C2Config.cpp
+++ b/media/codec2/vndk/C2Config.cpp
@@ -221,6 +221,30 @@
{ "vp9-6", C2Config::LEVEL_VP9_6 },
{ "vp9-6.1", C2Config::LEVEL_VP9_6_1 },
{ "vp9-6.2", C2Config::LEVEL_VP9_6_2 },
+ { "av1-2", C2Config::LEVEL_AV1_2 },
+ { "av1-2.1", C2Config::LEVEL_AV1_2_1 },
+ { "av1-2.2", C2Config::LEVEL_AV1_2_2 },
+ { "av1-2.3", C2Config::LEVEL_AV1_2_3 },
+ { "av1-3", C2Config::LEVEL_AV1_3 },
+ { "av1-3.1", C2Config::LEVEL_AV1_3_1 },
+ { "av1-3.2", C2Config::LEVEL_AV1_3_2 },
+ { "av1-3.3", C2Config::LEVEL_AV1_3_3 },
+ { "av1-4", C2Config::LEVEL_AV1_4 },
+ { "av1-4.1", C2Config::LEVEL_AV1_4_1 },
+ { "av1-4.2", C2Config::LEVEL_AV1_4_2 },
+ { "av1-4.3", C2Config::LEVEL_AV1_4_3 },
+ { "av1-5", C2Config::LEVEL_AV1_5 },
+ { "av1-5.1", C2Config::LEVEL_AV1_5_1 },
+ { "av1-5.2", C2Config::LEVEL_AV1_5_2 },
+ { "av1-5.3", C2Config::LEVEL_AV1_5_3 },
+ { "av1-6", C2Config::LEVEL_AV1_6 },
+ { "av1-6.1", C2Config::LEVEL_AV1_6_1 },
+ { "av1-6.2", C2Config::LEVEL_AV1_6_2 },
+ { "av1-6.3", C2Config::LEVEL_AV1_6_3 },
+ { "av1-7", C2Config::LEVEL_AV1_7 },
+ { "av1-7.1", C2Config::LEVEL_AV1_7_1 },
+ { "av1-7.2", C2Config::LEVEL_AV1_7_2 },
+ { "av1-7.3", C2Config::LEVEL_AV1_7_3 },
}))
DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2BufferData::type_t, ({
diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp
index a5dd203..dc7e89c 100644
--- a/media/codec2/vndk/C2Store.cpp
+++ b/media/codec2/vndk/C2Store.cpp
@@ -817,6 +817,7 @@
emplace("c2.android.mp3.decoder", "libcodec2_soft_mp3dec.so");
emplace("c2.android.vorbis.decoder", "libcodec2_soft_vorbisdec.so");
emplace("c2.android.opus.decoder", "libcodec2_soft_opusdec.so");
+ emplace("c2.android.opus.encoder", "libcodec2_soft_opusenc.so");
emplace("c2.android.vp8.decoder", "libcodec2_soft_vp8dec.so");
emplace("c2.android.vp9.decoder", "libcodec2_soft_vp9dec.so");
emplace("c2.android.vp8.encoder", "libcodec2_soft_vp8enc.so");
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 7a26035..6e71b98 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -269,36 +269,28 @@
}
}
if (slotBuffer) {
- native_handle_t *grallocHandle = native_handle_clone(slotBuffer->handle);
-
- if (grallocHandle) {
- ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
- C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle(
- grallocHandle,
- slotBuffer->width,
- slotBuffer->height,
- slotBuffer->format,
- slotBuffer->usage,
- slotBuffer->stride,
- slotBuffer->getGenerationNumber(),
- mProducerId, slot);
- if (c2Handle) {
- // Moved everything to c2Handle.
- native_handle_delete(grallocHandle);
- std::shared_ptr<C2GraphicAllocation> alloc;
- c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
- if (err != C2_OK) {
- return err;
- }
- std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
- std::make_shared<C2BufferQueueBlockPoolData>(
- slotBuffer->getGenerationNumber(),
- mProducerId, slot, shared_from_this());
- *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
- return C2_OK;
+ ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
+ C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle(
+ slotBuffer->handle,
+ slotBuffer->width,
+ slotBuffer->height,
+ slotBuffer->format,
+ slotBuffer->usage,
+ slotBuffer->stride,
+ slotBuffer->getGenerationNumber(),
+ mProducerId, slot);
+ if (c2Handle) {
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
+ if (err != C2_OK) {
+ return err;
}
- native_handle_close(grallocHandle);
- native_handle_delete(grallocHandle);
+ std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::make_shared<C2BufferQueueBlockPoolData>(
+ slotBuffer->getGenerationNumber(),
+ mProducerId, slot, shared_from_this());
+ *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
+ return C2_OK;
}
// Block was not created. call requestBuffer# again next time.
slotBuffer.clear();
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 7816473..eb6602c 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -1687,8 +1687,31 @@
}
// skip the first 4-byte of the offset to TIFF header
- *offset = mItemIdToExifMap[exifIndex].offset + 4;
- *size = mItemIdToExifMap[exifIndex].size - 4;
+ uint32_t tiffOffset;
+ if (!mDataSource->readAt(
+ mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) {
+ return ERROR_IO;
+ }
+
+ // We need 'Exif\0\0' before the tiff header
+ tiffOffset = ntohl(tiffOffset);
+ if (tiffOffset < 6) {
+ return ERROR_MALFORMED;
+ }
+ // The first 4-byte of the item is the offset of the tiff header within the
+ // exif data. The size of the item should be > 4 for a non-empty exif (this
+ // was already checked when the item was added). Also check that the tiff
+ // header offset is valid.
+ if (mItemIdToExifMap[exifIndex].size <= 4 ||
+ tiffOffset > mItemIdToExifMap[exifIndex].size - 4) {
+ return ERROR_MALFORMED;
+ }
+
+ // Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
+ // (first 4-byte is the tiff header offset)
+ uint32_t exifOffset = 4 + tiffOffset - 6;
+ *offset = mItemIdToExifMap[exifIndex].offset + exifOffset;
+ *size = mItemIdToExifMap[exifIndex].size - exifOffset;
return OK;
}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 50f172e..d0efddd 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -6048,6 +6048,7 @@
// Just give these file types a chance.
FOURCC("qt "), // Apple's QuickTime
FOURCC("MSNV"), // Sony's PSP
+ FOURCC("wmf "),
FOURCC("3g2a"), // 3GPP2
FOURCC("3g2b"),
diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp
index 8ee807c..003ce9e 100644
--- a/media/libeffects/lvm/tests/Android.bp
+++ b/media/libeffects/lvm/tests/Android.bp
@@ -44,3 +44,16 @@
"-Wextra",
],
}
+
+cc_test {
+ name: "snr",
+ host_supported: false,
+
+ srcs: ["snr.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
index 861ee64..41a4f04 100755
--- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
+++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh
@@ -25,16 +25,17 @@
adb shell mkdir -p $testdir
adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir
adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir
+adb push $OUT/testcases/snr/arm64/snr $testdir
flags_arr=(
"-csE"
"-eqE"
"-tE"
"-csE -tE -eqE"
- "-bE"
+ "-bE -M"
"-csE -tE"
"-csE -eqE" "-tE -eqE"
- "-csE -tE -bE -eqE"
+ "-csE -tE -bE -M -eqE"
)
fs_arr=(
@@ -79,6 +80,10 @@
then
adb shell cmp $testdir/sinesweep_2_$((fs)).raw \
$testdir/sinesweep_$((ch))_$((fs)).raw
+ elif [[ $flags == *"-bE"* ]] && [ "$ch" -gt 2 ]
+ then
+ adb shell $testdir/snr $testdir/sinesweep_2_$((fs)).raw \
+ $testdir/sinesweep_$((ch))_$((fs)).raw -thr:90.308998
fi
done
diff --git a/media/libeffects/lvm/tests/snr.cpp b/media/libeffects/lvm/tests/snr.cpp
new file mode 100644
index 0000000..88110c0
--- /dev/null
+++ b/media/libeffects/lvm/tests/snr.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+
+template <typename T, typename A = float>
+std::pair<A, A> getSignalNoise(FILE *finp, FILE *fref) {
+ constexpr size_t framesize = 256;
+ std::vector<T> in(framesize);
+ std::vector<T> ref(framesize);
+ A signal{};
+ A noise{};
+
+ for (;;) {
+ size_t read_samples_in = fread(&in[0], sizeof(T), framesize, finp);
+ const size_t read_samples_ref = fread(&ref[0], sizeof(T), framesize, fref);
+ if (read_samples_in != read_samples_ref) {
+ printf("file sizes do not match (last %zu %zu)", read_samples_in, read_samples_ref);
+ read_samples_in = std::min(read_samples_in, read_samples_ref);
+ }
+ if (read_samples_in == 0) {
+ return { signal, noise };
+ }
+ for (size_t i = 0; i < read_samples_in; ++i) {
+ const A value(ref[i]);
+ const A diff(A(in[i]) - value);
+ signal += value * value;
+ noise += diff * diff;
+ }
+ }
+}
+
+void printUsage() {
+ printf("\nUsage: ");
+ printf("\n snr <ref_file> <test_file> [options]\n");
+ printf("\nwhere, \n <ref_file> is the reference file name");
+ printf("\n on which will be taken as pure signal");
+ printf("\n <test_file> is test file for snr calculation");
+ printf("\n and options are mentioned below");
+ printf("\n");
+ printf("\n -pcm_format:<pcm format of input files>");
+ printf("\n 0 - 16 bit pcm");
+ printf("\n 1 - 32 bit float");
+ printf("\n default 0");
+ printf("\n -thr:<threshold value>");
+ printf("\n default - negative infinity\n\n");
+}
+
+int main(int argc, const char *argv[]) {
+ if (argc < 3) {
+ printUsage();
+ return -1;
+ }
+ int pcm_format = 0;
+ float thr = - std::numeric_limits<float>::infinity();
+ FILE *fref = fopen(argv[1], "rb");
+ FILE *finp = fopen(argv[2], "rb");
+ for (int i = 3; i < argc; i++) {
+ if (!strncmp(argv[i], "-pcm_format:", 12)) {
+ pcm_format = atoi(argv[i] + 12);
+ } else if (!strncmp(argv[i], "-thr:", 5)) {
+ thr = atof(argv[i] + 5);
+ }
+ }
+ if (finp == nullptr || fref == nullptr) {
+ printf("\nError: missing input/reference files\n");
+ return -1;
+ }
+ auto sn = pcm_format == 0
+ ? getSignalNoise<short>(finp, fref)
+ : getSignalNoise<float>(finp, fref);
+ if (sn.first > 0.f && sn.second > 0.f) {
+ float snr = 10.f * log(sn.first / sn.second);
+ // compare the measured snr value with threshold
+ if (snr < thr) {
+ printf("%.6f less than threshold %.6f\n", snr, thr);
+ } else {
+ printf("%.6f\n", snr);
+ }
+ }
+ fclose(finp);
+ fclose(fref);
+
+ return 0;
+}
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index e188e54..15ea578 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -1,6 +1,4 @@
-// TODO: change it back to cc_library_shared when there is a way to
-// expose media metrics as stable API.
-cc_library {
+cc_library_shared {
name: "libmediametrics",
srcs: [
@@ -32,12 +30,13 @@
cfi: true,
},
- // enumerate the stable interface
-// this would mean nobody can use the C++ interface. have to rework some things.
-// stubs: {
-// symbol_file: "libmediametrics.map.txt",
-// versions: [
-// "1" ,
-// ]
-// },
+ // enumerate stable entry points, for apex use
+ stubs: {
+ symbol_file: "libmediametrics.map.txt",
+ versions: [
+ "1" ,
+ ]
+ },
}
+
+
diff --git a/media/libmediametrics/IMediaAnalyticsService.cpp b/media/libmediametrics/IMediaAnalyticsService.cpp
index 28a7746..9114927 100644
--- a/media/libmediametrics/IMediaAnalyticsService.cpp
+++ b/media/libmediametrics/IMediaAnalyticsService.cpp
@@ -142,7 +142,7 @@
CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
bool forcenew;
- MediaAnalyticsItem *item = new MediaAnalyticsItem;
+ MediaAnalyticsItem *item = MediaAnalyticsItem::create();
data.readBool(&forcenew);
item->readFromParcel(data);
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 448e2d9..02c23b1 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -52,6 +52,17 @@
const char * const MediaAnalyticsItem::EnabledPropertyPersist = "persist.media.metrics.enabled";
const int MediaAnalyticsItem::EnabledProperty_default = 1;
+// So caller doesn't need to know size of allocated space
+MediaAnalyticsItem *MediaAnalyticsItem::create()
+{
+ return MediaAnalyticsItem::create(kKeyNone);
+}
+
+MediaAnalyticsItem *MediaAnalyticsItem::create(MediaAnalyticsItem::Key key)
+{
+ MediaAnalyticsItem *item = new MediaAnalyticsItem(key);
+ return item;
+}
// access functions for the class
MediaAnalyticsItem::MediaAnalyticsItem()
@@ -642,6 +653,19 @@
//
int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
+ int32_t version = data.readInt32();
+
+ switch(version) {
+ case 0:
+ return readFromParcel0(data);
+ break;
+ default:
+ ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version);
+ return -1;
+ }
+}
+
+int32_t MediaAnalyticsItem::readFromParcel0(const Parcel& data) {
// into 'this' object
// .. we make a copy of the string to put away.
mKey = data.readCString();
@@ -691,8 +715,23 @@
}
int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
+
if (data == NULL) return -1;
+ int32_t version = 0;
+ data->writeInt32(version);
+
+ switch(version) {
+ case 0:
+ return writeToParcel0(data);
+ break;
+ default:
+ ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version);
+ return -1;
+ }
+}
+
+int32_t MediaAnalyticsItem::writeToParcel0(Parcel *data) {
data->writeCString(mKey.c_str());
data->writeInt32(mPid);
@@ -737,7 +776,6 @@
return 0;
}
-
const char *MediaAnalyticsItem::toCString() {
return toCString(PROTO_LAST);
}
@@ -876,8 +914,6 @@
}
return true;
} else {
- std::string p = this->toString();
- ALOGW("Unable to record: %s [forcenew=%d]", p.c_str(), forcenew);
return false;
}
}
@@ -1035,5 +1071,170 @@
return true;
}
+// a byte array; contents are
+// overall length (uint32) including the length field itself
+// encoding version (uint32)
+// count of properties (uint32)
+// N copies of:
+// property name as length(int16), bytes
+// the bytes WILL include the null terminator of the name
+// type (uint8 -- 1 byte)
+// size of value field (int16 -- 2 bytes)
+// value (size based on type)
+// int32, int64, double -- little endian 4/8/8 bytes respectively
+// cstring -- N bytes of value [WITH terminator]
+
+enum { kInt32 = 0, kInt64, kDouble, kRate, kCString};
+
+bool MediaAnalyticsItem::dumpAttributes(char **pbuffer, size_t *plength) {
+
+ char *build = NULL;
+
+ if (pbuffer == NULL || plength == NULL)
+ return false;
+
+ // consistency for the caller, who owns whatever comes back in this pointer.
+ *pbuffer = NULL;
+
+ // first, let's calculate sizes
+ int32_t goal = 0;
+ int32_t version = 0;
+
+ goal += sizeof(uint32_t); // overall length, including the length field
+ goal += sizeof(uint32_t); // encoding version
+ goal += sizeof(uint32_t); // # properties
+
+ int32_t count = mPropCount;
+ for (int i = 0 ; i < count; i++ ) {
+ Prop *prop = &mProps[i];
+ goal += sizeof(uint16_t); // name length
+ goal += strlen(prop->mName) + 1; // string + null
+ goal += sizeof(uint8_t); // type
+ goal += sizeof(uint16_t); // size of value
+ switch (prop->mType) {
+ case MediaAnalyticsItem::kTypeInt32:
+ goal += sizeof(uint32_t);
+ break;
+ case MediaAnalyticsItem::kTypeInt64:
+ goal += sizeof(uint64_t);
+ break;
+ case MediaAnalyticsItem::kTypeDouble:
+ goal += sizeof(double);
+ break;
+ case MediaAnalyticsItem::kTypeRate:
+ goal += 2 * sizeof(uint64_t);
+ break;
+ case MediaAnalyticsItem::kTypeCString:
+ // length + actual string + null
+ goal += strlen(prop->u.CStringValue) + 1;
+ break;
+ default:
+ ALOGE("found bad Prop type: %d, idx %d, name %s",
+ prop->mType, i, prop->mName);
+ return false;
+ }
+ }
+
+ // now that we have a size... let's allocate and fill
+ build = (char *)malloc(goal);
+ if (build == NULL)
+ return false;
+
+ memset(build, 0, goal);
+
+ char *filling = build;
+
+#define _INSERT(val, size) \
+ { memcpy(filling, &(val), (size)); filling += (size);}
+#define _INSERTSTRING(val, size) \
+ { memcpy(filling, (val), (size)); filling += (size);}
+
+ _INSERT(goal, sizeof(int32_t));
+ _INSERT(version, sizeof(int32_t));
+ _INSERT(count, sizeof(int32_t));
+
+ for (int i = 0 ; i < count; i++ ) {
+ Prop *prop = &mProps[i];
+ int16_t attrNameLen = strlen(prop->mName) + 1;
+ _INSERT(attrNameLen, sizeof(int16_t));
+ _INSERTSTRING(prop->mName, attrNameLen); // termination included
+ int8_t elemtype;
+ int16_t elemsize;
+ switch (prop->mType) {
+ case MediaAnalyticsItem::kTypeInt32:
+ {
+ elemtype = kInt32;
+ _INSERT(elemtype, sizeof(int8_t));
+ elemsize = sizeof(int32_t);
+ _INSERT(elemsize, sizeof(int16_t));
+
+ _INSERT(prop->u.int32Value, sizeof(int32_t));
+ break;
+ }
+ case MediaAnalyticsItem::kTypeInt64:
+ {
+ elemtype = kInt64;
+ _INSERT(elemtype, sizeof(int8_t));
+ elemsize = sizeof(int64_t);
+ _INSERT(elemsize, sizeof(int16_t));
+
+ _INSERT(prop->u.int64Value, sizeof(int64_t));
+ break;
+ }
+ case MediaAnalyticsItem::kTypeDouble:
+ {
+ elemtype = kDouble;
+ _INSERT(elemtype, sizeof(int8_t));
+ elemsize = sizeof(double);
+ _INSERT(elemsize, sizeof(int16_t));
+
+ _INSERT(prop->u.doubleValue, sizeof(double));
+ break;
+ }
+ case MediaAnalyticsItem::kTypeRate:
+ {
+ elemtype = kRate;
+ _INSERT(elemtype, sizeof(int8_t));
+ elemsize = 2 * sizeof(uint64_t);
+ _INSERT(elemsize, sizeof(int16_t));
+
+ _INSERT(prop->u.rate.count, sizeof(uint64_t));
+ _INSERT(prop->u.rate.duration, sizeof(uint64_t));
+ break;
+ }
+ case MediaAnalyticsItem::kTypeCString:
+ {
+ elemtype = kCString;
+ _INSERT(elemtype, sizeof(int8_t));
+ elemsize = strlen(prop->u.CStringValue) + 1;
+ _INSERT(elemsize, sizeof(int16_t));
+
+ _INSERTSTRING(prop->u.CStringValue, elemsize);
+ break;
+ }
+ default:
+ // error if can't encode; warning if can't decode
+ ALOGE("found bad Prop type: %d, idx %d, name %s",
+ prop->mType, i, prop->mName);
+ goto badness;
+ }
+ }
+
+ if (build + goal != filling) {
+ ALOGE("problems populating; wrote=%d planned=%d",
+ (int)(filling-build), goal);
+ goto badness;
+ }
+
+ *pbuffer = build;
+ *plength = goal;
+
+ return true;
+
+ badness:
+ free(build);
+ return false;
+}
+
} // namespace android
diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp
index 9b08aa7..6109190 100644
--- a/media/libmediametrics/MediaMetrics.cpp
+++ b/media/libmediametrics/MediaMetrics.cpp
@@ -34,7 +34,7 @@
// manage the overall record
mediametrics_handle_t mediametrics_create(mediametricskey_t key) {
- android::MediaAnalyticsItem *item = new android::MediaAnalyticsItem(key);
+ android::MediaAnalyticsItem *item = android::MediaAnalyticsItem::create(key);
return (mediametrics_handle_t) item;
}
@@ -187,18 +187,9 @@
return android::MediaAnalyticsItem::isEnabled();
}
-#if 0
-// do not expose this as is.
-// need to revisit (or redefine) how the android::Parcel parameter is handled
-// so that it meets the stable-API criteria for updateable components.
-//
-int32_t mediametrics_writeToParcel(mediametrics_handle_t handle, android::Parcel *parcel) {
+bool mediametrics_getAttributes(mediametrics_handle_t handle, char **buffer, size_t *length) {
android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
- if (item == NULL) {
- return -1;
- }
- return item->writeToParcel(parcel);
+ if (item == NULL) return false;
+ return item->dumpAttributes(buffer, length);
+
}
-#endif
-
-
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index b99cd91..2f9e7c2 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
#define ANDROID_MEDIA_MEDIAANALYTICSITEM_H
-#include <cutils/properties.h>
#include <string>
#include <sys/types.h>
+
+#include <cutils/properties.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -84,6 +85,10 @@
public:
+ // so clients do not need to know size details
+ static MediaAnalyticsItem* create(Key key);
+ static MediaAnalyticsItem* create();
+
// access functions for the class
MediaAnalyticsItem();
MediaAnalyticsItem(Key);
@@ -175,6 +180,9 @@
int32_t writeToParcel(Parcel *);
int32_t readFromParcel(const Parcel&);
+ // supports the stable interface
+ bool dumpAttributes(char **pbuffer, size_t *plength);
+
std::string toString();
std::string toString(int version);
const char *toCString();
@@ -183,6 +191,11 @@
// are we collecting analytics data
static bool isEnabled();
+ private:
+ // handle Parcel version 0
+ int32_t writeToParcel0(Parcel *);
+ int32_t readFromParcel0(const Parcel&);
+
protected:
// merge fields from arg into this
diff --git a/media/libmediametrics/include/MediaMetrics.h b/media/libmediametrics/include/MediaMetrics.h
index 4d2f352..a4e1ed2 100644
--- a/media/libmediametrics/include/MediaMetrics.h
+++ b/media/libmediametrics/include/MediaMetrics.h
@@ -85,13 +85,9 @@
void mediametrics_setUid(mediametrics_handle_t handle, uid_t uid);
bool mediametrics_isEnabled();
-#if 0
-// do not expose this as is.
-// need to revisit (or redefine) how the android::Parcel parameter is handled
-// so that it meets the stable-API criteria for updateable components.
-//
-int32_t mediametrics_writeToParcel(mediametrics_handle_t handle, android::Parcel *parcel);
-#endif
+// serialized copy of the attributes/values, mostly for upstream getMetrics() calls
+// caller owns the buffer allocated as part of this call.
+bool mediametrics_getAttributes(mediametrics_handle_t handle, char **buffer, size_t *length);
__END_DECLS
diff --git a/media/libmediametrics/libmediametrics.map.txt b/media/libmediametrics/libmediametrics.map.txt
new file mode 100644
index 0000000..c46281a
--- /dev/null
+++ b/media/libmediametrics/libmediametrics.map.txt
@@ -0,0 +1,29 @@
+LIBMEDIAMETRICS_1 {
+ global:
+ mediametrics_addDouble; # apex
+ mediametrics_addInt32; # apex
+ mediametrics_addInt64; # apex
+ mediametrics_addRate; # apex
+ mediametrics_count; # apex
+ mediametrics_create; # apex
+ mediametrics_delete; # apex
+ mediametrics_freeCString; # apex
+ mediametrics_getAttributes; # apex
+ mediametrics_getCString; # apex
+ mediametrics_getDouble; # apex
+ mediametrics_getInt32; # apex
+ mediametrics_getInt64; # apex
+ mediametrics_getKey; # apex
+ mediametrics_getRate; # apex
+ mediametrics_isEnabled; # apex
+ mediametrics_readable; # apex
+ mediametrics_selfRecord; # apex
+ mediametrics_setCString; # apex
+ mediametrics_setDouble; # apex
+ mediametrics_setInt32; # apex
+ mediametrics_setInt64; # apex
+ mediametrics_setRate; # apex
+ mediametrics_setUid; # apex
+ local:
+ *;
+};
diff --git a/media/libmediaplayer2/JMedia2HTTPConnection.cpp b/media/libmediaplayer2/JMedia2HTTPConnection.cpp
index d264a7f..e1baa10 100644
--- a/media/libmediaplayer2/JMedia2HTTPConnection.cpp
+++ b/media/libmediaplayer2/JMedia2HTTPConnection.cpp
@@ -21,11 +21,10 @@
#include <mediaplayer2/JavaVMHelper.h>
#include <mediaplayer2/JMedia2HTTPConnection.h>
#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/scoped_local_ref.h>
#include "log/log.h"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
namespace android {
diff --git a/media/libmediaplayer2/JMedia2HTTPService.cpp b/media/libmediaplayer2/JMedia2HTTPService.cpp
index 264c15d..20e3573 100644
--- a/media/libmediaplayer2/JMedia2HTTPService.cpp
+++ b/media/libmediaplayer2/JMedia2HTTPService.cpp
@@ -25,8 +25,7 @@
#include <mediaplayer2/JMedia2HTTPConnection.h>
#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/scoped_local_ref.h>
namespace android {
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index 0c8d016..7804a62 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -214,6 +214,8 @@
virtual status_t setParameter(int key, const Parcel &request) = 0;
virtual status_t getParameter(int key, Parcel *reply) = 0;
+ virtual status_t getMetrics(char **buffer, size_t *length) = 0;
+
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
//
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 78865c4..2993ab1 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -102,6 +102,7 @@
status_t setAudioAttributes(const jobject attributes);
jobject getAudioAttributes();
status_t getParameter(int key, Parcel* reply);
+ status_t getMetrics(char **buffer, size_t *length);
// Modular DRM
status_t prepareDrm(int64_t srcId,
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index f75380c..53f2fb1 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -21,7 +21,6 @@
#include <android/binder_ibinder.h>
#include <media/AudioSystem.h>
#include <media/DataSourceDesc.h>
-#include <media/MediaAnalyticsItem.h>
#include <media/MemoryLeakTrackUtil.h>
#include <media/NdkWrapper.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -979,6 +978,22 @@
return status;
}
+// for mediametrics
+status_t MediaPlayer2::getMetrics(char **buffer, size_t *length) {
+ ALOGD("MediaPlayer2::getMetrics()");
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == NULL) {
+ ALOGV("getMetrics: no active player");
+ return INVALID_OPERATION;
+ }
+
+ status_t status = mPlayer->getMetrics(buffer, length);
+ if (status != OK) {
+ ALOGD("getMetrics returns %d", status);
+ }
+ return status;
+}
+
void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *obj) {
ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
(long long)srcId, msg, ext1, ext2);
diff --git a/media/libmediaplayer2/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp
index 71cd50f..0f69b2e 100644
--- a/media/libmediaplayer2/nuplayer2/Android.bp
+++ b/media/libmediaplayer2/nuplayer2/Android.bp
@@ -51,6 +51,7 @@
"libui",
"libgui",
"libmedia",
+ "libmediametrics",
"libmediandk",
"libmediandk_utils",
"libpowermanager",
diff --git a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp
index bbd22bc..89703de 100644
--- a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp
+++ b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp
@@ -22,7 +22,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/Utils.h>
-#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include "log/log.h"
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
index a5bd62d..9729d86 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
@@ -107,6 +107,8 @@
mStats->setInt64("frames-total", mNumFramesTotal);
mStats->setInt64("frames-dropped-input", mNumInputFramesDropped);
mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped);
+ mStats->setFloat("frame-rate-total", mFrameRateTotal);
+
return mStats;
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index 56e9471..1b661f2 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -92,6 +92,7 @@
static const char *kPlayerHeight = "android.media.mediaplayer.height";
static const char *kPlayerFrames = "android.media.mediaplayer.frames";
static const char *kPlayerFramesDropped = "android.media.mediaplayer.dropped";
+static const char *kPlayerFrameRate = "android.media.mediaplayer.fps";
static const char *kPlayerAMime = "android.media.mediaplayer.audio.mime";
static const char *kPlayerACodec = "android.media.mediaplayer.audio.codec";
static const char *kPlayerDuration = "android.media.mediaplayer.durationMs";
@@ -125,7 +126,7 @@
mMediaClock(new MediaClock),
mPlayer(new NuPlayer2(pid, uid, mMediaClock, context)),
mPlayerFlags(0),
- mAnalyticsItem(NULL),
+ mMetricsHandle(0),
mClientUid(uid),
mAtEOS(false),
mLooping(false),
@@ -136,9 +137,9 @@
mMediaClock->init();
- // set up an analytics record
- mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
- mAnalyticsItem->setUid(mClientUid);
+ // set up media metrics record
+ mMetricsHandle = mediametrics_create(kKeyPlayer);
+ mediametrics_setUid(mMetricsHandle, mClientUid);
mNuPlayer2Looper->start(
false, /* runOnCallingThread */
@@ -159,10 +160,7 @@
updateMetrics("destructor");
logMetrics("destructor");
- if (mAnalyticsItem != NULL) {
- delete mAnalyticsItem;
- mAnalyticsItem = NULL;
- }
+ mediametrics_delete(mMetricsHandle);
}
status_t NuPlayer2Driver::initCheck() {
@@ -453,15 +451,15 @@
if (mime.startsWith("video/")) {
int32_t width, height;
- mAnalyticsItem->setCString(kPlayerVMime, mime.c_str());
+ mediametrics_setCString(mMetricsHandle, kPlayerVMime, mime.c_str());
if (!name.empty()) {
- mAnalyticsItem->setCString(kPlayerVCodec, name.c_str());
+ mediametrics_setCString(mMetricsHandle, kPlayerVCodec, name.c_str());
}
if (stats->findInt32("width", &width)
&& stats->findInt32("height", &height)) {
- mAnalyticsItem->setInt32(kPlayerWidth, width);
- mAnalyticsItem->setInt32(kPlayerHeight, height);
+ mediametrics_setInt32(mMetricsHandle, kPlayerWidth, width);
+ mediametrics_setInt32(mMetricsHandle, kPlayerHeight, height);
}
int64_t numFramesTotal = 0;
@@ -469,14 +467,18 @@
stats->findInt64("frames-total", &numFramesTotal);
stats->findInt64("frames-dropped-output", &numFramesDropped);
- mAnalyticsItem->setInt64(kPlayerFrames, numFramesTotal);
- mAnalyticsItem->setInt64(kPlayerFramesDropped, numFramesDropped);
+ mediametrics_setInt64(mMetricsHandle, kPlayerFrames, numFramesTotal);
+ mediametrics_setInt64(mMetricsHandle, kPlayerFramesDropped, numFramesDropped);
+ float frameRate = 0;
+ if (stats->findFloat("frame-rate-output", &frameRate)) {
+ mediametrics_setInt64(mMetricsHandle, kPlayerFrameRate, frameRate);
+ }
} else if (mime.startsWith("audio/")) {
- mAnalyticsItem->setCString(kPlayerAMime, mime.c_str());
+ mediametrics_setCString(mMetricsHandle, kPlayerAMime, mime.c_str());
if (!name.empty()) {
- mAnalyticsItem->setCString(kPlayerACodec, name.c_str());
+ mediametrics_setCString(mMetricsHandle, kPlayerACodec, name.c_str());
}
}
}
@@ -487,17 +489,17 @@
// getDuration() uses mLock for mutex -- careful where we use it.
int64_t duration_ms = -1;
getDuration(&duration_ms);
- mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
+ mediametrics_setInt64(mMetricsHandle, kPlayerDuration, duration_ms);
- mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
+ mediametrics_setInt64(mMetricsHandle, kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
if (mRebufferingEvents != 0) {
- mAnalyticsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 );
- mAnalyticsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents);
- mAnalyticsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit);
+ mediametrics_setInt64(mMetricsHandle, kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 );
+ mediametrics_setInt32(mMetricsHandle, kPlayerRebufferingCount, mRebufferingEvents);
+ mediametrics_setInt32(mMetricsHandle, kPlayerRebufferingAtExit, mRebufferingAtExit);
}
- mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
+ mediametrics_setCString(mMetricsHandle, kPlayerDataSourceType, mPlayer->getDataSourceType());
}
@@ -507,7 +509,7 @@
}
ALOGV("logMetrics(%p) from %s at state %d", this, where, mState);
- if (mAnalyticsItem == NULL || mAnalyticsItem->isEnabled() == false) {
+ if (mMetricsHandle == 0 || mediametrics_isEnabled() == false) {
return;
}
@@ -516,16 +518,12 @@
// and that always injects 3 fields (duration, playing time, and
// datasource) into the record.
// So the canonical "empty" record has 3 elements in it.
- if (mAnalyticsItem->count() > 3) {
-
- mAnalyticsItem->selfrecord();
-
+ if (mediametrics_count(mMetricsHandle) > 3) {
+ mediametrics_selfRecord(mMetricsHandle);
// re-init in case we prepare() and start() again.
- delete mAnalyticsItem ;
- mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
- if (mAnalyticsItem) {
- mAnalyticsItem->setUid(mClientUid);
- }
+ mediametrics_delete(mMetricsHandle);
+ mMetricsHandle = mediametrics_create(kKeyPlayer);
+ mediametrics_setUid(mMetricsHandle, mClientUid);
} else {
ALOGV("did not have anything to record");
}
@@ -649,19 +647,18 @@
return INVALID_OPERATION;
}
-status_t NuPlayer2Driver::getParameter(int key, Parcel *reply) {
-
- if (key == FOURCC('m','t','r','X')) {
- // mtrX -- a play on 'metrics' (not matrix)
- // gather current info all together, parcel it, and send it back
- updateMetrics("api");
- mAnalyticsItem->writeToParcel(reply);
- return OK;
- }
-
+status_t NuPlayer2Driver::getParameter(int key __unused, Parcel *reply __unused) {
return INVALID_OPERATION;
}
+status_t NuPlayer2Driver::getMetrics(char **buffer, size_t *length) {
+ updateMetrics("api");
+ if (mediametrics_getAttributes(mMetricsHandle, buffer, length))
+ return OK;
+ else
+ return FAILED_TRANSACTION;
+}
+
void NuPlayer2Driver::notifyResetComplete(int64_t /* srcId */) {
ALOGD("notifyResetComplete(%p)", this);
Mutex::Autolock autoLock(mLock);
@@ -867,11 +864,11 @@
// ext1 is our primary 'error type' value. Only add ext2 when non-zero.
// [test against msg is due to fall through from previous switch value]
if (msg == MEDIA2_ERROR) {
- mAnalyticsItem->setInt32(kPlayerError, ext1);
+ mediametrics_setInt32(mMetricsHandle, kPlayerError, ext1);
if (ext2 != 0) {
- mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
+ mediametrics_setInt32(mMetricsHandle, kPlayerErrorCode, ext2);
}
- mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
+ mediametrics_setCString(mMetricsHandle, kPlayerErrorState, stateString(mState).c_str());
}
mAtEOS = true;
break;
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 0ec3a4b..3d299f3 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -16,7 +16,7 @@
#include <mediaplayer2/MediaPlayer2Interface.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetrics.h>
#include <media/stagefright/foundation/ABase.h>
#include <mediaplayer2/JObjectHolder.h>
@@ -61,6 +61,7 @@
virtual void setAudioSink(const sp<AudioSink> &audioSink) override;
virtual status_t setParameter(int key, const Parcel &request) override;
virtual status_t getParameter(int key, Parcel *reply) override;
+ virtual status_t getMetrics(char **buf, size_t *length) override;
virtual status_t dump(int fd, const Vector<String16> &args) const override;
@@ -132,7 +133,7 @@
sp<AudioSink> mAudioSink;
uint32_t mPlayerFlags;
- MediaAnalyticsItem *mAnalyticsItem;
+ mediametrics_handle_t mMetricsHandle;
uid_t mClientUid;
bool mAtEOS;
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index f45cc58..03eef48 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -160,7 +160,6 @@
"libstagefright_codecbase",
"libstagefright_foundation",
"libstagefright_omx_utils",
- "libstagefright_opus_common",
"libRScpp",
"libhidlallocatorutils",
"libhidlbase",
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 7df1a2d..c4015fb 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -85,7 +85,7 @@
static const int kTimestampDebugCount = 10;
static const int kItemIdBase = 10000;
static const char kExifHeader[] = {'E', 'x', 'i', 'f', '\0', '\0'};
-static const int32_t kTiffHeaderOffset = htonl(sizeof(kExifHeader));
+static const uint8_t kExifApp1Marker[] = {'E', 'x', 'i', 'f', 0xff, 0xe1};
static const uint8_t kMandatoryHevcNalUnitTypes[3] = {
kHevcNalUnitTypeVps,
@@ -125,7 +125,7 @@
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic; }
- bool isExifData(const MediaBufferBase *buffer) const;
+ bool isExifData(MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const;
void addChunkOffset(off64_t offset);
void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif);
void flushItemRefs();
@@ -364,7 +364,7 @@
Vector<uint16_t> mProperties;
ItemRefs mDimgRefs;
- ItemRefs mCdscRefs;
+ Vector<uint16_t> mExifList;
uint16_t mImageItemId;
int32_t mIsPrimary;
int32_t mWidth, mHeight;
@@ -1368,14 +1368,16 @@
}
off64_t MPEG4Writer::addSample_l(
- MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten) {
+ MediaBuffer *buffer, bool usePrefix,
+ uint32_t tiffHdrOffset, size_t *bytesWritten) {
off64_t old_offset = mOffset;
if (usePrefix) {
addMultipleLengthPrefixedSamples_l(buffer);
} else {
- if (isExif) {
- ::write(mFd, &kTiffHeaderOffset, 4); // exif_tiff_header_offset field
+ if (tiffHdrOffset > 0) {
+ tiffHdrOffset = htonl(tiffHdrOffset);
+ ::write(mFd, &tiffHdrOffset, 4); // exif_tiff_header_offset field
mOffset += 4;
}
@@ -1803,7 +1805,6 @@
mStartTimestampUs(-1),
mRotation(0),
mDimgRefs("dimg"),
- mCdscRefs("cdsc"),
mImageItemId(0),
mIsPrimary(0),
mWidth(0),
@@ -1984,11 +1985,34 @@
return OK;
}
-bool MPEG4Writer::Track::isExifData(const MediaBufferBase *buffer) const {
- return mIsHeic
- && (buffer->range_length() > sizeof(kExifHeader))
- && !memcmp((uint8_t *)buffer->data() + buffer->range_offset(),
- kExifHeader, sizeof(kExifHeader));
+bool MPEG4Writer::Track::isExifData(
+ MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const {
+ if (!mIsHeic) {
+ return false;
+ }
+
+ // Exif block starting with 'Exif\0\0'
+ size_t length = buffer->range_length();
+ uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset();
+ if ((length > sizeof(kExifHeader))
+ && !memcmp(data, kExifHeader, sizeof(kExifHeader))) {
+ *tiffHdrOffset = sizeof(kExifHeader);
+ return true;
+ }
+
+ // Exif block starting with fourcc 'Exif' followed by APP1 marker
+ if ((length > sizeof(kExifApp1Marker) + 2 + sizeof(kExifHeader))
+ && !memcmp(data, kExifApp1Marker, sizeof(kExifApp1Marker))
+ && !memcmp(data + sizeof(kExifApp1Marker) + 2, kExifHeader, sizeof(kExifHeader))) {
+ // skip 'Exif' fourcc
+ buffer->set_range(4, buffer->range_length() - 4);
+
+ // 2-byte APP1 + 2-byte size followed by kExifHeader
+ *tiffHdrOffset = 2 + 2 + sizeof(kExifHeader);
+ return true;
+ }
+
+ return false;
}
void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
@@ -2014,7 +2038,7 @@
}
if (isExif) {
- mCdscRefs.value.push_back(mOwner->addItem_l({
+ mExifList.push_back(mOwner->addItem_l({
.itemType = "Exif",
.isPrimary = false,
.isHidden = false,
@@ -2117,7 +2141,16 @@
if (mImageItemId > 0) {
mOwner->addRefs_l(mImageItemId, mDimgRefs);
- mOwner->addRefs_l(mImageItemId, mCdscRefs);
+
+ if (!mExifList.empty()) {
+ // The "cdsc" ref is from the metadata/exif item to the image item.
+ // So the refs all contain the image item.
+ ItemRefs cdscRefs("cdsc");
+ cdscRefs.value.push_back(mImageItemId);
+ for (uint16_t exifItem : mExifList) {
+ mOwner->addRefs_l(exifItem, cdscRefs);
+ }
+ }
}
}
@@ -2269,14 +2302,16 @@
while (!chunk->mSamples.empty()) {
List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
- int32_t isExif;
- if (!(*it)->meta_data().findInt32(kKeyIsExif, &isExif)) {
- isExif = 0;
+ uint32_t tiffHdrOffset;
+ if (!(*it)->meta_data().findInt32(
+ kKeyExifTiffOffset, (int32_t*)&tiffHdrOffset)) {
+ tiffHdrOffset = 0;
}
+ bool isExif = (tiffHdrOffset > 0);
bool usePrefix = chunk->mTrack->usePrefix() && !isExif;
size_t bytesWritten;
- off64_t offset = addSample_l(*it, usePrefix, isExif, &bytesWritten);
+ off64_t offset = addSample_l(*it, usePrefix, tiffHdrOffset, &bytesWritten);
if (chunk->mTrack->isHeic()) {
chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten, isExif);
@@ -3002,10 +3037,11 @@
}
bool isExif = false;
+ uint32_t tiffHdrOffset = 0;
int32_t isMuxerData;
if (buffer->meta_data().findInt32(kKeyIsMuxerData, &isMuxerData) && isMuxerData) {
// We only support one type of muxer data, which is Exif data block.
- isExif = isExifData(buffer);
+ isExif = isExifData(buffer, &tiffHdrOffset);
if (!isExif) {
ALOGW("Ignoring bad Exif data block");
buffer->release();
@@ -3027,7 +3063,7 @@
buffer = NULL;
if (isExif) {
- copy->meta_data().setInt32(kKeyIsExif, 1);
+ copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset);
}
bool usePrefix = this->usePrefix() && !isExif;
@@ -3300,7 +3336,8 @@
}
if (!hasMultipleTracks) {
size_t bytesWritten;
- off64_t offset = mOwner->addSample_l(copy, usePrefix, isExif, &bytesWritten);
+ off64_t offset = mOwner->addSample_l(
+ copy, usePrefix, tiffHdrOffset, &bytesWritten);
if (mIsHeic) {
addItemOffsetAndSize(offset, bytesWritten, isExif);
diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp
index ad55c56..5c13983 100644
--- a/media/libstagefright/OggWriter.cpp
+++ b/media/libstagefright/OggWriter.cpp
@@ -30,7 +30,7 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OggWriter.h>
#include <media/stagefright/foundation/ADebug.h>
-#include "OpusHeader.h"
+#include <media/stagefright/foundation/OpusHeader.h>
extern "C" {
#include <ogg/ogg.h>
@@ -114,30 +114,17 @@
}
mSampleRate = sampleRate;
-
- OpusHeader header;
- header.channels = nChannels;
- header.num_streams = nChannels;
- header.num_coupled = 0;
- header.channel_mapping = ((nChannels > 8) ? 255 : (nChannels > 2));
- header.gain_db = 0;
- header.skip_samples = 0;
-
- // headers are 21-bytes + something driven by channel count
- // expect numbers in the low 30's here. WriteOpusHeader() will tell us
- // if things are bad.
- unsigned char header_data[100];
- ogg_packet op;
- ogg_page og;
-
- const int packet_size = WriteOpusHeader(header, mSampleRate, (uint8_t*)header_data,
- sizeof(header_data));
-
- if (packet_size < 0) {
- ALOGE("opus header writing failed");
+ uint32_t type;
+ const void *header_data;
+ size_t packet_size;
+ if (!source->getFormat()->findData(kKeyOpusHeader, &type, &header_data, &packet_size)) {
+ ALOGE("opus header not found");
return UNKNOWN_ERROR;
}
- op.packet = header_data;
+
+ ogg_packet op;
+ ogg_page og;
+ op.packet = (unsigned char *)header_data;
op.bytes = packet_size;
op.b_o_s = 1;
op.e_o_s = 0;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 49e485a..2e7da01 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -37,6 +37,7 @@
#include <media/stagefright/foundation/ALookup.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/foundation/OpusHeader.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaDefs.h>
#include <media/AudioSystem.h>
@@ -1745,12 +1746,34 @@
} else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size());
} else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) {
- meta->setData(kKeyOpusHeader, 0, csd0->data(), csd0->size());
+ size_t opusHeadSize = csd0->size();
+ size_t codecDelayBufSize = 0;
+ size_t seekPreRollBufSize = 0;
+ void *opusHeadBuf = csd0->data();
+ void *codecDelayBuf = NULL;
+ void *seekPreRollBuf = NULL;
if (msg->findBuffer("csd-1", &csd1)) {
- meta->setData(kKeyOpusCodecDelay, 0, csd1->data(), csd1->size());
+ codecDelayBufSize = csd1->size();
+ codecDelayBuf = csd1->data();
}
if (msg->findBuffer("csd-2", &csd2)) {
- meta->setData(kKeyOpusSeekPreRoll, 0, csd2->data(), csd2->size());
+ seekPreRollBufSize = csd2->size();
+ seekPreRollBuf = csd2->data();
+ }
+ /* Extract codec delay and seek pre roll from csd-0,
+ * if csd-1 and csd-2 are not present */
+ if (!codecDelayBuf && !seekPreRollBuf) {
+ GetOpusHeaderBuffers(csd0->data(), csd0->size(), &opusHeadBuf,
+ &opusHeadSize, &codecDelayBuf,
+ &codecDelayBufSize, &seekPreRollBuf,
+ &seekPreRollBufSize);
+ }
+ meta->setData(kKeyOpusHeader, 0, opusHeadBuf, opusHeadSize);
+ if (codecDelayBuf) {
+ meta->setData(kKeyOpusCodecDelay, 0, codecDelayBuf, codecDelayBufSize);
+ }
+ if (seekPreRollBuf) {
+ meta->setData(kKeyOpusSeekPreRoll, 0, seekPreRollBuf, seekPreRollBufSize);
}
} else if (mime == MEDIA_MIMETYPE_AUDIO_VORBIS) {
meta->setData(kKeyVorbisInfo, 0, csd0->data(), csd0->size());
diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml
index 0b554a2..88cd08d 100644
--- a/media/libstagefright/data/media_codecs_google_c2_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml
@@ -93,5 +93,12 @@
<Limit name="complexity" range="0-8" default="5" />
<Feature name="bitrate-modes" value="CQ" />
</MediaCodec>
+ <MediaCodec name="c2.android.opus.encoder" type="audio/opus">
+ <Limit name="channel-count" max="2" />
+ <Limit name="sample-rate" ranges="8000,12000,16000,24000,48000" />
+ <Limit name="bitrate" range="500-512000" />
+ <Limit name="complexity" range="0-10" default="5" />
+ <Feature name="bitrate-modes" value="CQ" />
+ </MediaCodec>
</Encoders>
</Included>
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index adb45b3..c49789e 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -71,6 +71,15 @@
<Limit name="bitrate" range="1-40000000" />
<Feature name="adaptive-playback" />
</MediaCodec>
+ <MediaCodec name="c2.android.av1.decoder" type="video/av01">
+ <Limit name="size" min="96x96" max="1920x1080" />
+ <Limit name="alignment" value="2x2" />
+ <Limit name="block-size" value="16x16" />
+ <Limit name="blocks-per-second" min="24" max="2073600" />
+ <Limit name="bitrate" range="1-120000000" />
+ <Limit name="frame-rate" range="1-60" />
+ <Feature name="adaptive-playback" />
+ </MediaCodec>
</Decoders>
<Encoders>
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index dd1d904..533cd72 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -72,6 +72,7 @@
"MediaKeys.cpp",
"MetaData.cpp",
"MetaDataBase.cpp",
+ "OpusHeader.cpp",
"avc_utils.cpp",
"base64.cpp",
"hexdump.cpp",
diff --git a/media/libstagefright/opus/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp
similarity index 69%
rename from media/libstagefright/opus/OpusHeader.cpp
rename to media/libstagefright/foundation/OpusHeader.cpp
index e4a460c..9faede1 100644
--- a/media/libstagefright/opus/OpusHeader.cpp
+++ b/media/libstagefright/foundation/OpusHeader.cpp
@@ -16,7 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "SoftOpus"
-
+#include <algorithm>
#include <cstring>
#include <stdint.h>
@@ -43,9 +43,6 @@
{0, 6, 1, 2, 3, 4, 5, 7},
};
-// Opus always has a 48kHz output rate. This is true for all Opus, not just this
-// implementation.
-constexpr int kRate = 48000;
// Size of the Opus header excluding optional mapping information.
constexpr size_t kOpusHeaderSize = 19;
// Offset to magic string that starts Opus header.
@@ -76,15 +73,12 @@
constexpr size_t kOpusHeaderNumCoupledStreamsOffset = 20;
// Offset to the stream to channel mapping in the Opus header.
constexpr size_t kOpusHeaderStreamMapOffset = 21;
-// Maximum packet size used in Xiph's opusdec.
-constexpr int kMaxOpusOutputPacketSizeSamples = 960 * 6;
// Default audio output channel layout. Used to initialize |stream_map| in
// OpusHeader, and passed to opus_multistream_decoder_create() when the header
// does not contain mapping information. The values are valid only for mono and
// stereo output: Opus streams with more than 2 channels require a stream map.
constexpr int kMaxChannelsWithDefaultLayout = 2;
-constexpr uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = {0, 1};
static uint16_t ReadLE16(const uint8_t* data, size_t data_size, uint32_t read_offset) {
// check whether the 2nd byte is within the buffer
@@ -182,4 +176,88 @@
}
}
+int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate,
+ uint8_t* output, size_t outputSize, uint64_t codecDelay,
+ uint64_t seekPreRoll) {
+ if (outputSize < AOPUS_UNIFIED_CSD_MINSIZE) {
+ ALOGD("Buffer not large enough to hold unified OPUS CSD");
+ return -1;
+ }
+
+ int headerLen = WriteOpusHeader(header, inputSampleRate, output,
+ outputSize);
+ if (headerLen < 0) {
+ ALOGD("WriteOpusHeader failed");
+ return -1;
+ }
+ if (headerLen >= (outputSize - 2 * AOPUS_TOTAL_CSD_SIZE)) {
+ ALOGD("Buffer not large enough to hold codec delay and seek pre roll");
+ return -1;
+ }
+
+ uint64_t length = AOPUS_LENGTH;
+
+ /*
+ Following is the CSD syntax for signalling codec delay and
+ seek pre-roll which is to be appended after OpusHeader
+
+ Marker (8 bytes) | Length (8 bytes) | Samples (8 bytes)
+
+ Markers supported:
+ AOPUSDLY - Signals Codec Delay
+ AOPUSPRL - Signals seek pre roll
+
+ Length should be 8.
+ */
+
+ // Add codec delay
+ memcpy(output + headerLen, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE);
+ headerLen += AOPUS_MARKER_SIZE;
+ memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE);
+ headerLen += AOPUS_LENGTH_SIZE;
+ memcpy(output + headerLen, &codecDelay, AOPUS_CSD_SIZE);
+ headerLen += AOPUS_CSD_SIZE;
+
+ // Add skip pre roll
+ memcpy(output + headerLen, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE);
+ headerLen += AOPUS_MARKER_SIZE;
+ memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE);
+ headerLen += AOPUS_LENGTH_SIZE;
+ memcpy(output + headerLen, &seekPreRoll, AOPUS_CSD_SIZE);
+ headerLen += AOPUS_CSD_SIZE;
+
+ return headerLen;
+}
+
+void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
+ void **opusHeadBuf, size_t *opusHeadSize,
+ void **codecDelayBuf, size_t *codecDelaySize,
+ void **seekPreRollBuf, size_t *seekPreRollSize) {
+ *codecDelayBuf = NULL;
+ *codecDelaySize = 0;
+ *seekPreRollBuf = NULL;
+ *seekPreRollSize = 0;
+ *opusHeadBuf = (void *)data;
+ *opusHeadSize = data_size;
+ if (data_size >= AOPUS_UNIFIED_CSD_MINSIZE) {
+ size_t i = 0;
+ while (i < data_size - AOPUS_TOTAL_CSD_SIZE) {
+ uint8_t *csdBuf = (uint8_t *)data + i;
+ if (!memcmp(csdBuf, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE)) {
+ *opusHeadSize = std::min(*opusHeadSize, i);
+ *codecDelayBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
+ *codecDelaySize = AOPUS_CSD_SIZE;
+ i += AOPUS_TOTAL_CSD_SIZE;
+ } else if (!memcmp(csdBuf, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE)) {
+ *opusHeadSize = std::min(*opusHeadSize, i);
+ *seekPreRollBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
+ *seekPreRollSize = AOPUS_CSD_SIZE;
+ i += AOPUS_TOTAL_CSD_SIZE;
+ } else {
+ i++;
+ }
+ }
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h b/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h
new file mode 100644
index 0000000..9bffccb
--- /dev/null
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/*
+ * The Opus specification is part of IETF RFC 6716:
+ * http://tools.ietf.org/html/rfc6716
+ */
+
+#ifndef OPUS_HEADER_H_
+#define OPUS_HEADER_H_
+
+namespace android {
+
+/* Constants used for delimiting Opus CSD */
+#define AOPUS_CSD_CODEC_DELAY_MARKER "AOPUSDLY"
+#define AOPUS_CSD_SEEK_PREROLL_MARKER "AOPUSPRL"
+#define AOPUS_CSD_SIZE 8
+#define AOPUS_LENGTH 8
+#define AOPUS_MARKER_SIZE 8
+#define AOPUS_LENGTH_SIZE 8
+#define AOPUS_TOTAL_CSD_SIZE \
+ ((AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_CSD_SIZE))
+#define AOPUS_CSD0_MINSIZE 19
+#define AOPUS_UNIFIED_CSD_MINSIZE \
+ ((AOPUS_CSD0_MINSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE))
+
+/* CSD0 at max can be 22 bytes + max number of channels (255) */
+#define AOPUS_CSD0_MAXSIZE 277
+#define AOPUS_UNIFIED_CSD_MAXSIZE \
+ ((AOPUS_CSD0_MAXSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE))
+
+struct OpusHeader {
+ int channels;
+ int channel_mapping;
+ int num_streams;
+ int num_coupled;
+ int16_t gain_db;
+ int skip_samples;
+ uint8_t stream_map[8];
+};
+
+bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header);
+int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, uint8_t* output, size_t output_size);
+void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
+ void **opusHeadBuf, size_t *opusHeadSize,
+ void **codecDelayBuf, size_t *codecDelaySize,
+ void **seekPreRollBuf, size_t *seekPreRollSize);
+int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate,
+ uint8_t* output, size_t outputSize, uint64_t codecDelay,
+ uint64_t seekPreRoll);
+} // namespace android
+
+#endif // OPUS_HEADER_H_
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 1abef8c..803155d 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -257,7 +257,9 @@
void initInternal(int fd, bool isFirstSession);
// Acquire lock before calling these methods
- off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten);
+ off64_t addSample_l(
+ MediaBuffer *buffer, bool usePrefix,
+ uint32_t tiffHdrOffset, size_t *bytesWritten);
void addLengthPrefixedSample_l(MediaBuffer *buffer);
void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
uint16_t addProperty_l(const ItemProperty &);
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index b99c14c..2910bd3 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -221,7 +221,8 @@
kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track
kKeyExifOffset = 'exof', // int64_t, Exif data offset
kKeyExifSize = 'exsz', // int64_t, Exif data size
- kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block
+ kKeyExifTiffOffset = 'thdr', // int32_t, if > 0, buffer contains exif data block with
+ // tiff hdr at specified offset
kKeyPcmBigEndian = 'pcmb', // bool (int32_t)
// Key for ALAC Magic Cookie
diff --git a/media/libstagefright/opus/Android.bp b/media/libstagefright/opus/Android.bp
deleted file mode 100644
index c5086ec..0000000
--- a/media/libstagefright/opus/Android.bp
+++ /dev/null
@@ -1,21 +0,0 @@
-cc_library_shared {
- name: "libstagefright_opus_common",
- vendor_available: true,
-
- export_include_dirs: ["include"],
-
- srcs: ["OpusHeader.cpp"],
-
- shared_libs: ["liblog"],
-
- cflags: ["-Werror"],
-
- sanitize: {
- integer_overflow: true,
- cfi: true,
- diag: {
- integer_overflow: true,
- cfi: true,
- },
- },
-}
\ No newline at end of file
diff --git a/media/libstagefright/opus/include/OpusHeader.h b/media/libstagefright/opus/include/OpusHeader.h
deleted file mode 100644
index f9f79cd..0000000
--- a/media/libstagefright/opus/include/OpusHeader.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-/*
- * The Opus specification is part of IETF RFC 6716:
- * http://tools.ietf.org/html/rfc6716
- */
-
-#ifndef OPUS_HEADER_H_
-#define OPUS_HEADER_H_
-
-namespace android {
-
-struct OpusHeader {
- int channels;
- int channel_mapping;
- int num_streams;
- int num_coupled;
- int16_t gain_db;
- int skip_samples;
- uint8_t stream_map[8];
-};
-
-bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header);
-int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, uint8_t* output, size_t output_size);
-} // namespace android
-
-#endif // OPUS_HEADER_H_
diff --git a/media/libstagefright/webm/Android.bp b/media/libstagefright/webm/Android.bp
index 1f840b7..64ecc2d 100644
--- a/media/libstagefright/webm/Android.bp
+++ b/media/libstagefright/webm/Android.bp
@@ -28,7 +28,6 @@
shared_libs: [
"libstagefright_foundation",
- "libstagefright_opus_common",
"libutils",
"liblog",
],
diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp
index 7b4b23a..b0a303e 100644
--- a/media/libstagefright/webm/WebmWriter.cpp
+++ b/media/libstagefright/webm/WebmWriter.cpp
@@ -24,7 +24,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/hexdump.h>
-#include <OpusHeader.h>
+#include <media/stagefright/foundation/OpusHeader.h>
#include <utils/Errors.h>
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 1a0c3b1..010c1aa 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -308,6 +308,9 @@
ALOGE("Failed to set BufferItemConsumer buffer dataSpace");
return AMEDIA_ERROR_UNKNOWN;
}
+ if (mUsage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
+ gbConsumer->setConsumerIsProtected(true);
+ }
mSurface = new Surface(mProducer, /*controlledByApp*/true);
if (mSurface == nullptr) {
diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
index dc409a7..e0b56d4 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
@@ -121,6 +121,9 @@
bool deviceSupportsEncodedFormats(audio_devices_t device) const
{
+ if (device == AUDIO_DEVICE_NONE) {
+ return true; // required for isOffloadSupported() check
+ }
DeviceVector deviceList =
mSupportedDevices.getDevicesFromTypeMask(device);
if (!deviceList.empty()) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 55d4db4..c880e67 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -257,15 +257,21 @@
void AudioInputDescriptor::close()
{
if (mIoHandle != AUDIO_IO_HANDLE_NONE) {
+ // clean up active clients if any (can happen if close() is called to force
+ // clients to reconnect
+ for (const auto &client : getClientIterable()) {
+ if (client->active()) {
+ ALOGW("%s client with port ID %d still active on input %d",
+ __func__, client->portId(), mId);
+ setClientActive(client, false);
+ stop();
+ }
+ }
+
mClientInterface->closeInput(mIoHandle);
LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u",
__FUNCTION__, mProfile->curOpenCount);
- // do not call stop() here as stop() is supposed to be called after
- // setClientActive(client, false) and we don't know how many clients
- // are still active at this time
- if (isActive()) {
- mProfile->curActiveCount--;
- }
+
mProfile->curOpenCount--;
LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < mProfile->curActiveCount,
"%s(%d): mProfile->curOpenCount %d < mProfile->curActiveCount %d.",
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 57328f0..78b3f45 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -563,6 +563,17 @@
void SwAudioOutputDescriptor::close()
{
if (mIoHandle != AUDIO_IO_HANDLE_NONE) {
+ // clean up active clients if any (can happen if close() is called to force
+ // clients to reconnect
+ for (const auto &client : getClientIterable()) {
+ if (client->active()) {
+ ALOGW("%s client with port ID %d still active on output %d",
+ __func__, client->portId(), mId);
+ setClientActive(client, false);
+ stop();
+ }
+ }
+
AudioParameter param;
param.add(String8("closing"), String8("true"));
mClientInterface->setParameters(mIoHandle, param.toString());
@@ -571,11 +582,6 @@
LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u",
__FUNCTION__, mProfile->curOpenCount);
- // do not call stop() here as stop() is supposed to be called after setClientActive(false)
- // and we don't know how many streams are still active at this time
- if (isActive()) {
- mProfile->curActiveCount--;
- }
mProfile->curOpenCount--;
mIoHandle = AUDIO_IO_HANDLE_NONE;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 1bc4ec8..dc5b238 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -51,12 +51,6 @@
mEncodedFormats.add(AUDIO_FORMAT_AC3);
mEncodedFormats.add(AUDIO_FORMAT_IEC61937);
}
- // For backward compatibility always indicate support for SBC and AAC if no
- // supported format is listed in the configuration file
- if ((type & AUDIO_DEVICE_OUT_ALL_A2DP) != 0 && mEncodedFormats.isEmpty()) {
- mEncodedFormats.add(AUDIO_FORMAT_SBC);
- mEncodedFormats.add(AUDIO_FORMAT_AAC);
- }
}
audio_port_handle_t DeviceDescriptor::getId() const
@@ -102,11 +96,19 @@
if (!device_has_encoding_capability(type())) {
return true;
}
+ if (mEncodedFormats.isEmpty()) {
+ return true;
+ }
+
return (mCurrentEncodedFormat != AUDIO_FORMAT_DEFAULT);
}
bool DeviceDescriptor::supportsFormat(audio_format_t format)
{
+ if (mEncodedFormats.isEmpty()) {
+ return true;
+ }
+
for (const auto& devFormat : mEncodedFormats) {
if (devFormat == format) {
return true;
diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk
index 2ccb542..e4fba0f 100644
--- a/services/audiopolicy/tests/Android.mk
+++ b/services/audiopolicy/tests/Android.mk
@@ -29,6 +29,8 @@
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
include $(BUILD_NATIVE_TEST)
# system/audio.h utilities test
@@ -55,4 +57,6 @@
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
include $(BUILD_NATIVE_TEST)
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 24326bb..e9f4657 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -117,9 +117,14 @@
explicit PatchCountCheck(AudioPolicyManagerTestClient *client)
: mClient{client},
mInitialCount{mClient->getActivePatchesCount()} {}
- void assertDelta(int delta) const {
- ASSERT_EQ(mInitialCount + delta, mClient->getActivePatchesCount()); }
- void assertNoChange() const { assertDelta(0); }
+ int deltaFromSnapshot() const {
+ size_t currentCount = mClient->getActivePatchesCount();
+ if (mInitialCount <= currentCount) {
+ return currentCount - mInitialCount;
+ } else {
+ return -(static_cast<int>(mInitialCount - currentCount));
+ }
+ }
private:
const AudioPolicyManagerTestClient *mClient;
const size_t mInitialCount;
@@ -139,7 +144,7 @@
int sampleRate,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
audio_port_handle_t *portId = nullptr);
- PatchCountCheck snapPatchCount() { return PatchCountCheck(mClient.get()); }
+ PatchCountCheck snapshotPatchCount() { return PatchCountCheck(mClient.get()); }
std::unique_ptr<AudioPolicyManagerTestClient> mClient;
std::unique_ptr<AudioPolicyTestManager> mManager;
@@ -225,7 +230,7 @@
TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) {
audio_patch patch{};
audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(nullptr, &handle, 0));
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, nullptr, 0));
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, &handle, 0));
@@ -252,20 +257,20 @@
ASSERT_EQ(INVALID_OPERATION, mManager->createAudioPatch(&patch, &handle, 0));
// Verify that the handle is left unchanged.
ASSERT_EQ(AUDIO_PATCH_HANDLE_NONE, handle);
- patchCount.assertNoChange();
+ ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTest, CreateAudioPatchFromMix) {
audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
uid_t uid = 42;
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty());
PatchBuilder patchBuilder;
patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]).
addSink(mManager->getConfig().getDefaultOutputDevice());
ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid));
ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle);
- patchCount.assertDelta(1);
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
// TODO: Add patch creation tests that involve already existing patch
@@ -350,84 +355,82 @@
}
TEST_F(AudioPolicyManagerTestMsd, PatchCreationOnSetForceUse) {
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
mManager->setForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND,
AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS);
- patchCount.assertDelta(1);
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) {
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertDelta(1);
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrPcmRoutesToMsd) {
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertDelta(1);
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) {
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertDelta(1);
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertDelta(1);
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) {
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertNoChange();
+ ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrFormatSwitching) {
// Switch between formats that are supported and not supported by MSD.
{
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId, portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
&portId);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertDelta(1);
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
- patchCount.assertDelta(1); // compared to the state at the block entry
- // TODO: make PatchCountCheck asserts more obvious. It's easy to
- // miss the fact that it is immutable.
+ ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
{
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId, portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
&portId);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertDelta(-1);
+ ASSERT_EQ(-1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
- patchCount.assertNoChange();
+ ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
{
- const PatchCountCheck patchCount = snapPatchCount();
+ const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
- patchCount.assertNoChange();
+ ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
}
diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp
index 8ae3376..80d3630 100644
--- a/services/mediacodec/registrant/Android.bp
+++ b/services/mediacodec/registrant/Android.bp
@@ -38,6 +38,7 @@
"libcodec2_soft_mp3dec",
"libcodec2_soft_vorbisdec",
"libcodec2_soft_opusdec",
+ "libcodec2_soft_opusenc",
"libcodec2_soft_vp8dec",
"libcodec2_soft_vp9dec",
"libcodec2_soft_av1dec",
diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
index 966e214..6d88c84 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
@@ -18,15 +18,19 @@
openat: 1
open: 1
getuid32: 1
+getuid: 1
+getrlimit: 1
writev: 1
ioctl: 1
close: 1
mmap2: 1
+mmap: 1
fstat64: 1
stat64: 1
statfs64: 1
madvise: 1
fstatat64: 1
+newfstatat: 1
futex: 1
munmap: 1
faccessat: 1