Merge "Revert "Use start/finish app op API for mic use""
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index b012b5d..38d9406 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -22,6 +22,7 @@
"libhidlbase",
"liblog",
"libmediaextractor",
+ "libmediandk",
"libstagefright_foundation",
"libutils",
],
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 5e47b48..3b1b413 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -111,6 +111,10 @@
static status_t translateErrorCode(media_status_t err) {
if (err == AMEDIA_OK) {
return OK;
+ } else if (err == AMEDIA_ERROR_END_OF_STREAM) {
+ return ERROR_END_OF_STREAM;
+ } else if (err == AMEDIA_ERROR_IO) {
+ return ERROR_IO;
} else if (err == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
return -EAGAIN;
}
@@ -1150,6 +1154,15 @@
return AMediaExtractor_getSampleTime(mAMediaExtractor);
}
+status_t AMediaExtractorWrapper::getSampleFormat(sp<AMediaFormatWrapper> &formatWrapper) {
+ if (mAMediaExtractor == NULL) {
+ return DEAD_OBJECT;
+ }
+ AMediaFormat *format = AMediaFormat_new();
+ formatWrapper = new AMediaFormatWrapper(format);
+ return translateErrorCode(AMediaExtractor_getSampleFormat(mAMediaExtractor, format));
+}
+
int64_t AMediaExtractorWrapper::getCachedDuration() {
if (mAMediaExtractor == NULL) {
return -1;
diff --git a/media/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index b71b758..f17d2cc 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -313,6 +313,8 @@
int64_t getSampleTime();
+ status_t getSampleFormat(sp<AMediaFormatWrapper> &formatWrapper);
+
int64_t getCachedDuration();
bool advance();
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index 790581a..936fb20 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -1239,7 +1239,7 @@
}
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
+ meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSubtitleTrack.mIndex);
}
uint32_t dataType; // unused
@@ -1255,7 +1255,7 @@
if (mb->meta_data().findData(
kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
- meta->setBuffer("mpegUserData", mpegUserData);
+ meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
}
mb->release();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 5971a8b..060b698 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -2842,7 +2842,7 @@
void NuPlayer2::sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex) {
int32_t trackIndex;
int64_t timeUs, durationUs;
- CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
+ CHECK(buffer->meta()->findInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, &trackIndex));
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
index e4afd5b..e48e388 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
@@ -21,6 +21,7 @@
#include "NuPlayer2CCDecoder.h"
+#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -301,7 +302,7 @@
// returns true if a new CC track is found
bool NuPlayer2::CCDecoder::extractFromMPEGUserData(const sp<ABuffer> &accessUnit) {
sp<ABuffer> mpegUserData;
- if (!accessUnit->meta()->findBuffer("mpegUserData", &mpegUserData)
+ if (!accessUnit->meta()->findBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, &mpegUserData)
|| mpegUserData == NULL) {
return false;
}
@@ -538,7 +539,7 @@
dumpBytePair(ccBuf);
#endif
- ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
+ ccBuf->meta()->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSelectedTrack);
ccBuf->meta()->setInt64("timeUs", timeUs);
ccBuf->meta()->setInt64("durationUs", 0ll);
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index a37973b..2b5fa71 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -23,6 +23,7 @@
"libhidlmemory",
"liblog",
"libmedia",
+ "libmediandk",
"libmedia_omx",
"libmediaextractor",
"libmediadrm",
diff --git a/media/libmediaplayerservice/nuplayer/Android.bp b/media/libmediaplayerservice/nuplayer/Android.bp
index 645bb7a..58ab8fc 100644
--- a/media/libmediaplayerservice/nuplayer/Android.bp
+++ b/media/libmediaplayerservice/nuplayer/Android.bp
@@ -28,6 +28,7 @@
"frameworks/av/media/libstagefright/mpeg2ts",
"frameworks/av/media/libstagefright/rtsp",
"frameworks/av/media/libstagefright/timedtext",
+ "frameworks/av/media/ndk/include",
],
cflags: [
@@ -48,6 +49,7 @@
"libui",
"libgui",
"libmedia",
+ "libmediandk",
"libmediadrm",
"libpowermanager",
],
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index e7c3deb..d86b08d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -28,6 +28,7 @@
#include <media/MediaExtractor.h>
#include <media/MediaSource.h>
#include <media/IMediaExtractorService.h>
+#include <media/NdkMediaFormat.h>
#include <media/IMediaHTTPService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -1233,7 +1234,7 @@
}
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
+ meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSubtitleTrack.mIndex);
}
uint32_t dataType; // unused
@@ -1249,7 +1250,7 @@
if (mb->meta_data().findData(
kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
- meta->setBuffer("mpegUserData", mpegUserData);
+ meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
}
mb->release();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index dce3e0a..d252150 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -43,6 +43,7 @@
#include <media/AudioResamplerPublic.h>
#include <media/AVSyncSettings.h>
#include <media/MediaCodecBuffer.h>
+#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -2702,7 +2703,7 @@
void NuPlayer::sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex) {
int32_t trackIndex;
int64_t timeUs, durationUs;
- CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
+ CHECK(buffer->meta()->findInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, &trackIndex));
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index 0a8b97f..4ada953 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -21,6 +21,7 @@
#include "NuPlayerCCDecoder.h"
+#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -301,7 +302,7 @@
// returns true if a new CC track is found
bool NuPlayer::CCDecoder::extractFromMPEGUserData(const sp<ABuffer> &accessUnit) {
sp<ABuffer> mpegUserData;
- if (!accessUnit->meta()->findBuffer("mpegUserData", &mpegUserData)
+ if (!accessUnit->meta()->findBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, &mpegUserData)
|| mpegUserData == NULL) {
return false;
}
@@ -538,7 +539,7 @@
dumpBytePair(ccBuf);
#endif
- ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
+ ccBuf->meta()->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSelectedTrack);
ccBuf->meta()->setInt64("timeUs", timeUs);
ccBuf->meta()->setInt64("durationUs", 0ll);
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 540cf8c..f6fc813 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -446,12 +446,17 @@
ssize_t NuMediaExtractor::fetchAllTrackSamples(
int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
TrackInfo *minInfo = NULL;
- ssize_t minIndex = -1;
+ ssize_t minIndex = ERROR_END_OF_STREAM;
for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
TrackInfo *info = &mSelectedTracks.editItemAt(i);
fetchTrackSamples(info, seekTimeUs, mode);
+ status_t err = info->mFinalResult;
+ if (err != OK && err != ERROR_END_OF_STREAM) {
+ return err;
+ }
+
if (info->mSamples.empty()) {
continue;
}
@@ -721,7 +726,8 @@
ssize_t minIndex = fetchAllTrackSamples();
if (minIndex < 0) {
- return ERROR_END_OF_STREAM;
+ status_t err = minIndex;
+ return err;
}
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index f612b4f..216a897 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -433,9 +433,29 @@
mComponents.emplace("c2.google.avc.encoder", "libstagefright_soft_c2avcenc.so");
mComponents.emplace("c2.google.aac.decoder", "libstagefright_soft_c2aacdec.so");
mComponents.emplace("c2.google.aac.encoder", "libstagefright_soft_c2aacenc.so");
- mComponents.emplace("c2.google.mp3.decoder", "libstagefright_soft_c2mp3dec.so");
+ mComponents.emplace("c2.google.amrnb.decoder", "libstagefright_soft_c2amrnbdec.so");
+ mComponents.emplace("c2.google.amrnb.encoder", "libstagefright_soft_c2amrnbenc.so");
+ mComponents.emplace("c2.google.amrwb.decoder", "libstagefright_soft_c2amrwbdec.so");
+ mComponents.emplace("c2.google.amrwb.encoder", "libstagefright_soft_c2amrwbenc.so");
+ mComponents.emplace("c2.google.hevc.decoder", "libstagefright_soft_c2hevcdec.so");
mComponents.emplace("c2.google.g711.alaw.decoder", "libstagefright_soft_c2g711alawdec.so");
mComponents.emplace("c2.google.g711.mlaw.decoder", "libstagefright_soft_c2g711mlawdec.so");
+ mComponents.emplace("c2.google.mpeg2.decoder", "libstagefright_soft_c2mpeg2dec.so");
+ mComponents.emplace("c2.google.h263.decoder", "libstagefright_soft_c2h263dec.so");
+ mComponents.emplace("c2.google.h263.encoder", "libstagefright_soft_c2h263enc.so");
+ mComponents.emplace("c2.google.mpeg4.decoder", "libstagefright_soft_c2mpeg4dec.so");
+ mComponents.emplace("c2.google.mpeg4.encoder", "libstagefright_soft_c2mpeg4enc.so");
+ mComponents.emplace("c2.google.mp3.decoder", "libstagefright_soft_c2mp3dec.so");
+ mComponents.emplace("c2.google.vorbis.decoder", "libstagefright_soft_c2vorbisdec.so");
+ mComponents.emplace("c2.google.opus.decoder", "libstagefright_soft_c2opusdec.so");
+ mComponents.emplace("c2.google.vp8.decoder", "libstagefright_soft_c2vp8dec.so");
+ mComponents.emplace("c2.google.vp9.decoder", "libstagefright_soft_c2vp9dec.so");
+ mComponents.emplace("c2.google.vp8.encoder", "libstagefright_soft_c2vp8enc.so");
+ mComponents.emplace("c2.google.vp9.encoder", "libstagefright_soft_c2vp9enc.so");
+ mComponents.emplace("c2.google.raw.decoder", "libstagefright_soft_c2rawdec.so");
+ mComponents.emplace("c2.google.flac.decoder", "libstagefright_soft_c2flacdec.so");
+ mComponents.emplace("c2.google.flac.encoder", "libstagefright_soft_c2flacenc.so");
+ mComponents.emplace("c2.google.gsm.decoder", "libstagefright_soft_c2gsmdec.so");
}
c2_status_t C2PlatformComponentStore::copyBuffer(
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index f844459..4001d46 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -122,6 +122,50 @@
//###############################################################################
+cc_library_shared {
+ name: "libstagefright_soft_c2amrnbenc",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftAmrNbEnc.cpp"],
+
+ local_include_dirs: ["src"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ static_libs: [
+ "libstagefright_amrnbenc",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ "libstagefright_amrnb_common",
+ ],
+}
+
+//###############################################################################
+
cc_test {
name: "libstagefright_amrnbenc_test",
gtest: false,
diff --git a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
new file mode 100644
index 0000000..4dd0309
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
@@ -0,0 +1,252 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAmrNbEnc"
+#include <utils/Log.h>
+
+#include "gsmamr_enc.h"
+
+#include "C2SoftAmrNbEnc.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.amrnb.encoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatAudio)
+ .outputFormat(C2FormatCompressed)
+ .inputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_AMR_NB)
+ .build();
+}
+
+C2SoftAmrNbEnc::C2SoftAmrNbEnc(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
+ mEncState(nullptr),
+ mSidState(nullptr) {
+}
+
+C2SoftAmrNbEnc::~C2SoftAmrNbEnc() {
+ onRelease();
+}
+
+c2_status_t C2SoftAmrNbEnc::onInit() {
+ bool dtx_enable = false;
+
+ if (AMREncodeInit(&mEncState, &mSidState, dtx_enable) != 0)
+ return C2_CORRUPTED;
+ mMode = MR795;
+ mIsFirst = true;
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ mAnchorTimeStamp = 0;
+ mProcessedSamples = 0;
+ mFilledLen = 0;
+
+ return C2_OK;
+}
+
+void C2SoftAmrNbEnc::onRelease() {
+ if (mEncState) {
+ AMREncodeExit(&mEncState, &mSidState);
+ mEncState = mSidState = nullptr;
+ }
+}
+
+c2_status_t C2SoftAmrNbEnc::onStop() {
+ if (AMREncodeReset(mEncState, mSidState) != 0)
+ return C2_CORRUPTED;
+ mIsFirst = true;
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ mAnchorTimeStamp = 0;
+ mProcessedSamples = 0;
+ mFilledLen = 0;
+
+ return C2_OK;
+}
+
+void C2SoftAmrNbEnc::onReset() {
+ (void) onStop();
+}
+
+c2_status_t C2SoftAmrNbEnc::onFlush_sm() {
+ return onStop();
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+void C2SoftAmrNbEnc::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ 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);
+
+ size_t outCapacity = kNumBytesPerInputFrame;
+ outCapacity += mFilledLen + inSize;
+ std::shared_ptr<C2LinearBlock> outputBlock;
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &outputBlock);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock for Output failed with status %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ C2WriteView wView = outputBlock->map().get();
+ if (wView.error()) {
+ ALOGE("write view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ uint64_t outTimeStamp = mProcessedSamples * 1000000ll / kSampleRate;
+ const uint8_t *inPtr = rView.data() + inOffset;
+ size_t inPos = 0;
+ size_t outPos = 0;
+ while (inPos < inSize) {
+ int validSamples = mFilledLen / sizeof(int16_t);
+ if ((inPos + (kNumBytesPerInputFrame - mFilledLen)) <= inSize) {
+ memcpy(mInputFrame + validSamples, inPtr + inPos,
+ (kNumBytesPerInputFrame - mFilledLen));
+ inPos += (kNumBytesPerInputFrame - mFilledLen);
+ } else {
+ memcpy(mInputFrame + validSamples, inPtr + inPos, (inSize - inPos));
+ mFilledLen += (inSize - inPos);
+ inPos += (inSize - inPos);
+ if (eos) {
+ validSamples = mFilledLen / sizeof(int16_t);
+ memset(mInputFrame + validSamples, 0, (kNumBytesPerInputFrame - mFilledLen));
+ } else break;
+ }
+ Frame_Type_3GPP frameType;
+ int numEncBytes = AMREncode(mEncState, mSidState, mMode, mInputFrame,
+ wView.data() + outPos, &frameType,
+ AMR_TX_WMF);
+ if (numEncBytes < 0 || numEncBytes > ((int)outCapacity - (int)outPos)) {
+ ALOGE("encodeFrame call failed, state [%d %zu %zu]", numEncBytes, outPos, outCapacity);
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ // Convert header byte from WMF to IETF format.
+ if (numEncBytes > 0)
+ wView.data()[outPos] = ((wView.data()[outPos] << 3) | 4) & 0x7c;
+ outPos += numEncBytes;
+ mProcessedSamples += kNumSamplesPerFrame;
+ mFilledLen = 0;
+ }
+ ALOGV("causal sample size %d", mFilledLen);
+ if (mIsFirst) {
+ mIsFirst = false;
+ mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+ }
+ fillEmptyWork(work);
+ if (outPos != 0) {
+ work->worklets.front()->output.buffers.push_back(
+ createLinearBuffer(std::move(outputBlock), 0, outPos));
+ work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+ }
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ if (mFilledLen) ALOGV("Discarding trailing %d bytes", mFilledLen);
+ }
+}
+
+c2_status_t C2SoftAmrNbEnc::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ (void) 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;
+ }
+
+ onFlush_sm();
+ return C2_OK;
+}
+
+class C2SoftAmrNbEncFactory : public C2ComponentFactory {
+public:
+ 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 C2SoftAmrNbEnc(kComponentName, id), 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 = BuildIntf(kComponentName, id, deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftAmrNbEncFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftAmrNbEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.h b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.h
new file mode 100644
index 0000000..9ced921
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_SOFT_AMRNB_ENC_H_
+#define C2_SOFT_AMRNB_ENC_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+class C2SoftAmrNbEnc : public SimpleC2Component {
+public:
+ C2SoftAmrNbEnc(const char *name, c2_node_id_t id);
+ virtual ~C2SoftAmrNbEnc();
+
+ // 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:
+ static const int32_t kNumSamplesPerFrame = L_FRAME;
+ static const int32_t kNumBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
+ static const int32_t kSampleRate = 8000;
+
+ void *mEncState;
+ void *mSidState;
+ Mode mMode;
+ bool mIsFirst;
+ bool mSignalledError;
+ bool mSignalledOutputEos;
+ uint64_t mAnchorTimeStamp;
+ uint64_t mProcessedSamples;
+ int32_t mFilledLen;
+ int16_t mInputFrame[kNumSamplesPerFrame];
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftAmrNbEnc);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_AMRNB_ENC_H_
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
index 9af086b..68fdd15 100644
--- a/media/libstagefright/codecs/flac/dec/Android.bp
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -1,4 +1,48 @@
cc_library_shared {
+ name: "libstagefright_soft_c2flacdec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: [
+ "C2SoftFlacDecoder.cpp",
+ ],
+
+ include_dirs: [
+ "external/flac/include",
+ "frameworks/av/media/libstagefright/flac/dec",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_flacdec",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ ],
+}
+
+
+cc_library_shared {
name: "libstagefright_soft_flacdec",
vendor_available: true,
vndk: {
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
new file mode 100644
index 0000000..ce40d6b
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftFlacDecoder"
+#include <utils/Log.h>
+
+#include "C2SoftFlacDecoder.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.flac.decoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatAudio)
+ .inputMediaType(MEDIA_MIMETYPE_AUDIO_FLAC)
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .build();
+}
+
+C2SoftFlacDecoder::C2SoftFlacDecoder(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
+ mFLACDecoder(nullptr) {
+}
+
+C2SoftFlacDecoder::~C2SoftFlacDecoder() {
+}
+
+c2_status_t C2SoftFlacDecoder::onInit() {
+ status_t err = initDecoder();
+ return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftFlacDecoder::onStop() {
+ if (mFLACDecoder) mFLACDecoder->flush();
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+ mHasStreamInfo = false;
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ mInputBufferCount = 0;
+ return C2_OK;
+}
+
+void C2SoftFlacDecoder::onReset() {
+ (void)onStop();
+}
+
+void C2SoftFlacDecoder::onRelease() {
+}
+
+c2_status_t C2SoftFlacDecoder::onFlush_sm() {
+ return onStop();
+}
+
+status_t C2SoftFlacDecoder::initDecoder() {
+ mFLACDecoder = FLACDecoder::Create();
+ if (!mFLACDecoder) {
+ ALOGE("initDecoder: failed to create FLACDecoder");
+ mSignalledError = true;
+ return NO_MEMORY;
+ }
+
+ memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+ mHasStreamInfo = false;
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ mInputBufferCount = 0;
+
+ return OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+// (TODO) add multiframe support, in plugin and FLACDecoder.cpp
+void C2SoftFlacDecoder::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+ bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+
+ ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+ (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+ if (inSize == 0) {
+ fillEmptyWork(work);
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+ return;
+ }
+
+ if (mInputBufferCount == 0 && !codecConfig) {
+ ALOGV("First frame has to include configuration, forcing config");
+ codecConfig = true;
+ }
+
+ uint8_t *input = const_cast<uint8_t *>(rView.data() + inOffset);
+ if (codecConfig) {
+ status_t decoderErr = mFLACDecoder->parseMetadata(input, inSize);
+ if (decoderErr != OK && decoderErr != WOULD_BLOCK) {
+ ALOGE("process: FLACDecoder parseMetaData returns error %d", decoderErr);
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ mInputBufferCount++;
+ fillEmptyWork(work);
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+
+ if (decoderErr == WOULD_BLOCK) {
+ ALOGV("process: parseMetadata is Blocking, Continue %d", decoderErr);
+ } else {
+ mStreamInfo = mFLACDecoder->getStreamInfo();
+ if (mStreamInfo.max_blocksize && mStreamInfo.channels)
+ mHasStreamInfo = true;
+ ALOGD("process: decoder configuration : %d Hz, %d channels, %d samples,"
+ " %d block size", mStreamInfo.sample_rate, mStreamInfo.channels,
+ (int)mStreamInfo.total_samples, mStreamInfo.max_blocksize);
+ }
+ return;
+ }
+
+ size_t outSize;
+ if (mHasStreamInfo)
+ outSize = mStreamInfo.max_blocksize * mStreamInfo.channels * sizeof(short);
+ else
+ outSize = kMaxBlockSize * FLACDecoder::kMaxChannels * sizeof(short);
+
+ std::shared_ptr<C2LinearBlock> block;
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchLinearBlock(outSize, usage, &block);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock for Output failed with status %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ C2WriteView wView = block->map().get();
+ if (wView.error()) {
+ ALOGE("write view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ short *output = reinterpret_cast<short *>(wView.data());
+ status_t decoderErr = mFLACDecoder->decodeOneFrame(
+ input, inSize, output, &outSize);
+ if (decoderErr != OK) {
+ ALOGE("process: FLACDecoder decodeOneFrame returns error %d", decoderErr);
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ mInputBufferCount++;
+ ALOGV("out buffer attr. size %zu", outSize);
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, 0, outSize));
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+}
+
+c2_status_t C2SoftFlacDecoder::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ (void) 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;
+ }
+
+ if (mFLACDecoder) mFLACDecoder->flush();
+
+ return C2_OK;
+}
+
+class C2SoftFlacDecFactory : public C2ComponentFactory {
+public:
+ 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 C2SoftFlacDecoder(kComponentName, id), 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 = BuildIntf(kComponentName, id, deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftFlacDecFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftFlacDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
new file mode 100644
index 0000000..a5c01a9
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_SOFT_FLAC_DECODER_H_
+#define C2_SOFT_FLAC_DECODER_H_
+
+#include <SimpleC2Component.h>
+
+#include "FLACDecoder.h"
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct C2SoftFlacDecoder : public SimpleC2Component {
+ C2SoftFlacDecoder(const char *name, c2_node_id_t id);
+ virtual ~C2SoftFlacDecoder();
+
+ // 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:
+ enum {
+ kMaxBlockSize = 4096
+ };
+
+ sp<FLACDecoder> mFLACDecoder;
+ FLAC__StreamMetadata_StreamInfo mStreamInfo;
+ bool mSignalledError;
+ bool mSignalledOutputEos;
+ bool mHasStreamInfo;
+ size_t mInputBufferCount;
+
+ status_t initDecoder();
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftFlacDecoder);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_FLAC_DECODER_H_
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index 46b974d..bb705dd 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -1,4 +1,43 @@
cc_library_shared {
+ name: "libstagefright_soft_c2flacenc",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftFlacEnc.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ static_libs: [
+ "libFLAC",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ "libutils",
+ ],
+}
+
+cc_library_shared {
srcs: ["SoftFlacEncoder.cpp"],
diff --git a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
new file mode 100644
index 0000000..6c147ad
--- /dev/null
+++ b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
@@ -0,0 +1,389 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftFlacEnc"
+#include <utils/Log.h>
+
+#include "C2SoftFlacEnc.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.flac.encoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatAudio)
+ .outputFormat(C2FormatCompressed)
+ .inputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_FLAC)
+ .build();
+}
+
+C2SoftFlacEnc::C2SoftFlacEnc(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
+ mFlacStreamEncoder(nullptr),
+ mInputBufferPcm32(nullptr) {
+}
+
+C2SoftFlacEnc::~C2SoftFlacEnc() {
+ onRelease();
+}
+
+c2_status_t C2SoftFlacEnc::onInit() {
+ mFlacStreamEncoder = FLAC__stream_encoder_new();
+ if (!mFlacStreamEncoder) return C2_CORRUPTED;
+
+ mInputBufferPcm32 = (FLAC__int32*) malloc(
+ kInBlockSize * kMaxNumChannels * sizeof(FLAC__int32));
+ if (!mInputBufferPcm32) return C2_NO_MEMORY;
+
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ mNumChannels = 1;
+ mSampleRate = 44100;
+ mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT;
+ mIsFirstFrame = true;
+ mAnchorTimeStamp = 0ull;
+ mProcessedSamples = 0u;
+ mEncoderWriteData = false;
+ mEncoderReturnedNbBytes = 0;
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+ mHeaderOffset = 0;
+ mWroteHeader = false;
+#endif
+
+ status_t err = configureEncoder();
+ return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+void C2SoftFlacEnc::onRelease() {
+ if (mFlacStreamEncoder) {
+ FLAC__stream_encoder_delete(mFlacStreamEncoder);
+ mFlacStreamEncoder = nullptr;
+ }
+
+ if (mInputBufferPcm32) {
+ free(mInputBufferPcm32);
+ mInputBufferPcm32 = nullptr;
+ }
+}
+
+void C2SoftFlacEnc::onReset() {
+ mNumChannels = 1;
+ mSampleRate = 44100;
+ mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT;
+ (void) onStop();
+}
+
+c2_status_t C2SoftFlacEnc::onStop() {
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ mIsFirstFrame = true;
+ mAnchorTimeStamp = 0ull;
+ mProcessedSamples = 0u;
+ mEncoderWriteData = false;
+ mEncoderReturnedNbBytes = 0;
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+ mHeaderOffset = 0;
+ mWroteHeader = false;
+#endif
+
+ c2_status_t status = drain(DRAIN_COMPONENT_NO_EOS, nullptr);
+ if (C2_OK != status) return status;
+
+ status_t err = configureEncoder();
+ if (err != OK) mSignalledError = true;
+ return C2_OK;
+}
+
+c2_status_t C2SoftFlacEnc::onFlush_sm() {
+ return onStop();
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+void C2SoftFlacEnc::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ 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 (mIsFirstFrame && inSize) {
+ mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+ mIsFirstFrame = false;
+ }
+ uint64_t outTimeStamp = mProcessedSamples * 1000000ll / mSampleRate;
+
+ size_t outCapacity = inSize;
+ outCapacity += mBlockSize * mNumChannels * sizeof(int16_t);
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+ outCapacity += FLAC_HEADER_SIZE;
+#endif
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchLinearBlock(outCapacity, 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;
+ return;
+ }
+
+ mEncoderWriteData = true;
+ mEncoderReturnedNbBytes = 0;
+ while (inOffset < inSize) {
+ size_t processSize = MIN(kInBlockSize * mNumChannels * sizeof(int16_t), (inSize - inOffset));
+ const unsigned nbInputFrames = processSize / (mNumChannels * sizeof(int16_t));
+ const unsigned nbInputSamples = processSize / sizeof(int16_t);
+ const int16_t *pcm16 = reinterpret_cast<const int16_t *>(rView.data() + inOffset);
+ ALOGV("about to encode %zu bytes", processSize);
+
+ for (unsigned i = 0; i < nbInputSamples; i++) {
+ mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
+ }
+
+ FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
+ mFlacStreamEncoder, mInputBufferPcm32, nbInputFrames);
+ if (!ok) {
+ ALOGE("error encountered during encoding");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ mOutputBlock.reset();
+ return;
+ }
+ inOffset += processSize;
+ }
+ if (eos && !drain(DRAIN_COMPONENT_WITH_EOS, pool)) {
+ ALOGE("error encountered during encoding");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ mOutputBlock.reset();
+ return;
+ }
+ fillEmptyWork(work);
+ if (mEncoderReturnedNbBytes != 0) {
+ std::shared_ptr<C2Buffer> buffer = createLinearBuffer(std::move(mOutputBlock), 0, mEncoderReturnedNbBytes);
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+ } else {
+ ALOGV("encoder process_interleaved returned without data to write");
+ }
+ mOutputBlock = nullptr;
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+ mEncoderWriteData = false;
+ mEncoderReturnedNbBytes = 0;
+}
+
+FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::onEncodedFlacAvailable(
+ const FLAC__byte buffer[], size_t bytes, unsigned samples,
+ unsigned current_frame) {
+ (void) current_frame;
+ ALOGV("%s (bytes=%zu, samples=%u, curr_frame=%u)", __func__, bytes, samples,
+ current_frame);
+
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+ if (samples == 0) {
+ ALOGI("saving %zu bytes of header", bytes);
+ memcpy(mHeader + mHeaderOffset, buffer, bytes);
+ mHeaderOffset += bytes;// will contain header size when finished receiving header
+ return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+ }
+#endif
+
+ if ((samples == 0) || !mEncoderWriteData) {
+ // called by the encoder because there's header data to save, but it's not the role
+ // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined)
+ ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples);
+ return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+ }
+
+ // write encoded data
+ C2WriteView wView = mOutputBlock->map().get();
+ uint8_t* outData = wView.data();
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+ if (!mWroteHeader) {
+ ALOGI("writing %d bytes of header on output", mHeaderOffset);
+ memcpy(outData + mEncoderReturnedNbBytes, mHeader, mHeaderOffset);
+ mEncoderReturnedNbBytes += mHeaderOffset;
+ mWroteHeader = true;
+ }
+#endif
+ ALOGV("writing %zu bytes of encoded data on output", bytes);
+ // increment mProcessedSamples to maintain audio synchronization during
+ // play back
+ mProcessedSamples += samples;
+ if (bytes + mEncoderReturnedNbBytes > mOutputBlock->capacity()) {
+ ALOGE("not enough space left to write encoded data, dropping %zu bytes", bytes);
+ // a fatal error would stop the encoding
+ return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+ }
+ memcpy(outData + mEncoderReturnedNbBytes, buffer, bytes);
+ mEncoderReturnedNbBytes += bytes;
+ return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+}
+
+
+status_t C2SoftFlacEnc::configureEncoder() {
+ ALOGV("%s numChannel=%d, sampleRate=%d", __func__, mNumChannels, mSampleRate);
+
+ if (mSignalledError || !mFlacStreamEncoder) {
+ ALOGE("can't configure encoder: no encoder or invalid state");
+ return UNKNOWN_ERROR;
+ }
+
+ FLAC__bool ok = true;
+ ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels);
+ ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate);
+ ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16);
+ ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, mCompressionLevel);
+ ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false);
+ if (!ok) {
+ ALOGE("unknown error when configuring encoder");
+ return UNKNOWN_ERROR;
+ }
+
+ ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK ==
+ FLAC__stream_encoder_init_stream(mFlacStreamEncoder,
+ flacEncoderWriteCallback /*write_callback*/,
+ nullptr /*seek_callback*/,
+ nullptr /*tell_callback*/,
+ nullptr /*metadata_callback*/,
+ (void *) this /*client_data*/);
+
+ if (!ok) {
+ ALOGE("unknown error when configuring encoder");
+ return UNKNOWN_ERROR;
+ }
+
+ mBlockSize = FLAC__stream_encoder_get_blocksize(mFlacStreamEncoder);
+
+ ALOGV("encoder successfully configured");
+ return OK;
+}
+
+FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::flacEncoderWriteCallback(
+ const FLAC__StreamEncoder *,
+ const FLAC__byte buffer[],
+ size_t bytes,
+ unsigned samples,
+ unsigned current_frame,
+ void *client_data) {
+ return ((C2SoftFlacEnc*) client_data)->onEncodedFlacAvailable(
+ buffer, bytes, samples, current_frame);
+}
+
+c2_status_t C2SoftFlacEnc::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ (void) pool;
+ switch (drainMode) {
+ case NO_DRAIN:
+ ALOGW("drain with NO_DRAIN: no-op");
+ return C2_OK;
+ case DRAIN_CHAIN:
+ ALOGW("DRAIN_CHAIN not supported");
+ return C2_OMITTED;
+ case DRAIN_COMPONENT_WITH_EOS:
+ // TODO: This flag is not being sent back to the client
+ // because there are no items in PendingWork queue as all the
+ // inputs are being sent back with emptywork or valid encoded data
+ // mSignalledOutputEos = true;
+ case DRAIN_COMPONENT_NO_EOS:
+ break;
+ default:
+ return C2_BAD_VALUE;
+ }
+ FLAC__bool ok = FLAC__stream_encoder_finish(mFlacStreamEncoder);
+ if (!ok) return C2_CORRUPTED;
+ mIsFirstFrame = true;
+ mAnchorTimeStamp = 0ull;
+ mProcessedSamples = 0u;
+
+ return C2_OK;
+}
+
+class C2SoftFlacEncFactory : public C2ComponentFactory {
+public:
+ 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 C2SoftFlacEnc(kComponentName, id), 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 = BuildIntf(kComponentName, id, deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftFlacEncFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftFlacEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.h b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.h
new file mode 100644
index 0000000..cc8cb86
--- /dev/null
+++ b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_SOFT_FLAC_ENC_H_
+#define C2_SOFT_FLAC_ENC_H_
+
+#include <SimpleC2Component.h>
+
+#include "FLAC/stream_encoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+// use this symbol to have the first output buffer start with FLAC frame header so a dump of
+// all the output buffers can be opened as a .flac file
+//#define WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+
+#define FLAC_COMPRESSION_LEVEL_MIN 0
+#define FLAC_COMPRESSION_LEVEL_DEFAULT 5
+#define FLAC_COMPRESSION_LEVEL_MAX 8
+
+#define FLAC_HEADER_SIZE 128
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+namespace android {
+
+class C2SoftFlacEnc : public SimpleC2Component {
+public:
+ C2SoftFlacEnc(const char *name, c2_node_id_t id);
+ virtual ~C2SoftFlacEnc();
+
+ // 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:
+ status_t configureEncoder();
+ static FLAC__StreamEncoderWriteStatus flacEncoderWriteCallback(
+ const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[],
+ size_t bytes, unsigned samples, unsigned current_frame,
+ void *client_data);
+ FLAC__StreamEncoderWriteStatus onEncodedFlacAvailable(
+ const FLAC__byte buffer[], size_t bytes, unsigned samples,
+ unsigned current_frame);
+
+ const unsigned int kInBlockSize = 1152;
+ const unsigned int kMaxNumChannels = 2;
+ FLAC__StreamEncoder* mFlacStreamEncoder;
+ FLAC__int32* mInputBufferPcm32;
+ std::shared_ptr<C2LinearBlock> mOutputBlock;
+ bool mSignalledError;
+ bool mSignalledOutputEos;
+ uint32_t mNumChannels;
+ uint32_t mSampleRate;
+ uint32_t mCompressionLevel;
+ uint32_t mBlockSize;
+ bool mIsFirstFrame;
+ uint64_t mAnchorTimeStamp;
+ uint64_t mProcessedSamples;
+ // should the data received by the callback be written to the output port
+ bool mEncoderWriteData;
+ size_t mEncoderReturnedNbBytes;
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+ unsigned mHeaderOffset;
+ bool mWroteHeader;
+ char mHeader[FLAC_HEADER_SIZE];
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftFlacEnc);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_FLAC_ENC_H_
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index ca70cc2..d24a116 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -112,3 +112,91 @@
},
compile_multilib: "32",
}
+
+//###############################################################################
+
+cc_library_shared {
+ name: "libstagefright_soft_c2mpeg4dec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftMpeg4Dec.cpp"],
+
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ "-DMPEG4",
+ "-Wall",
+ "-Werror",
+ ],
+
+ local_include_dirs: ["src"],
+ export_include_dirs: ["include"],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ static_libs: ["libstagefright_m4vh263dec"],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ ],
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
+
+//###############################################################################
+cc_library_shared {
+ name: "libstagefright_soft_c2h263dec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftMpeg4Dec.cpp"],
+
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ "-Wall",
+ "-Werror",
+ ],
+
+ local_include_dirs: ["src"],
+ export_include_dirs: ["include"],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ static_libs: ["libstagefright_m4vh263dec"],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ ],
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
new file mode 100644
index 0000000..2a3239f
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
@@ -0,0 +1,502 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftMpeg4Dec"
+#include <utils/Log.h>
+
+#include "C2SoftMpeg4Dec.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include "mp4dec_api.h"
+
+namespace android {
+
+#ifdef MPEG4
+constexpr char kComponentName[] = "c2.google.mpeg4.decoder";
+#else
+constexpr char kComponentName[] = "c2.google.h263.decoder";
+#endif
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatVideo)
+ .inputMediaType(
+#ifdef MPEG4
+ MEDIA_MIMETYPE_VIDEO_MPEG4
+#else
+ MEDIA_MIMETYPE_VIDEO_H263
+#endif
+ )
+ .outputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+ .build();
+}
+
+C2SoftMpeg4Dec::C2SoftMpeg4Dec(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
+ mDecHandle(nullptr) {
+}
+
+C2SoftMpeg4Dec::~C2SoftMpeg4Dec() {
+ onRelease();
+}
+
+c2_status_t C2SoftMpeg4Dec::onInit() {
+ status_t err = initDecoder();
+ return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftMpeg4Dec::onStop() {
+ if (mInitialized) {
+ PVCleanUpVideoDecoder(mDecHandle);
+ mInitialized = false;
+ }
+ for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (mOutputBuffer[i]) {
+ free(mOutputBuffer[i]);
+ mOutputBuffer[i] = nullptr;
+ }
+ }
+ mNumSamplesOutput = 0;
+ mFramesConfigured = false;
+ mSignalledOutputEos = false;
+ mSignalledError = false;
+
+ return C2_OK;
+}
+
+void C2SoftMpeg4Dec::onReset() {
+ (void) onStop();
+}
+
+void C2SoftMpeg4Dec::onRelease() {
+ if (mInitialized) {
+ PVCleanUpVideoDecoder(mDecHandle);
+ }
+ if (mOutBlock) {
+ mOutBlock.reset();
+ }
+ for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (mOutputBuffer[i]) {
+ free(mOutputBuffer[i]);
+ mOutputBuffer[i] = nullptr;
+ }
+ }
+
+ delete mDecHandle;
+ mDecHandle = nullptr;
+}
+
+c2_status_t C2SoftMpeg4Dec::onFlush_sm() {
+ if (mInitialized) {
+ if (PV_TRUE != PVResetVideoDecoder(mDecHandle)) return C2_CORRUPTED;
+ }
+ mSignalledOutputEos = false;
+ mSignalledError = false;
+ return C2_OK;
+}
+
+status_t C2SoftMpeg4Dec::initDecoder() {
+#ifdef MPEG4
+ mIsMpeg4 = true;
+#else
+ mIsMpeg4 = false;
+#endif
+ if (!mDecHandle) {
+ mDecHandle = new tagvideoDecControls;
+ }
+ memset(mDecHandle, 0, sizeof(tagvideoDecControls));
+
+ for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+ mOutputBuffer[i] = nullptr;
+ }
+
+ /* TODO: bring these values to 352 and 288. It cannot be done as of now
+ * because, h263 doesn't seem to allow port reconfiguration. In OMX, the
+ * problem of larger width and height than default width and height is
+ * overcome by adaptivePlayBack() api call. This call gets width and height
+ * information from extractor. Such a thing is not possible here.
+ * So we are configuring to larger values.*/
+ mWidth = 1408;
+ mHeight = 1152;
+ mNumSamplesOutput = 0;
+ mInitialized = false;
+ mFramesConfigured = false;
+ mSignalledOutputEos = false;
+ mSignalledError = false;
+
+ return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling eos");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+void C2SoftMpeg4Dec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+ std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+ C2Rect(mWidth, mHeight));
+ mOutBlock = nullptr;
+ auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+ (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling eos");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ };
+ if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+ fillWork(work);
+ } else {
+ finish(index, fillWork);
+ }
+}
+
+c2_status_t C2SoftMpeg4Dec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+ if (!mDecHandle) {
+ ALOGE("not supposed to be here, invalid decoder context");
+ return C2_CORRUPTED;
+ }
+
+ uint32_t outSize = align(mWidth, 16) * align(mHeight, 16) * 3 / 2;
+ for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (!mOutputBuffer[i]) {
+ mOutputBuffer[i] = (uint8_t *)malloc(outSize * sizeof(uint8_t));
+ if (!mOutputBuffer[i]) return C2_NO_MEMORY;
+ }
+ }
+ if (mOutBlock &&
+ (mOutBlock->width() != align(mWidth, 16) || mOutBlock->height() != mHeight)) {
+ mOutBlock.reset();
+ }
+ if (!mOutBlock) {
+ uint32_t format = HAL_PIXEL_FORMAT_YV12;
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, usage, &mOutBlock);
+ if (err != C2_OK) {
+ ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+ return err;
+ }
+ ALOGV("provided (%dx%d) required (%dx%d)",
+ mOutBlock->width(), mOutBlock->height(), mWidth, mHeight);
+ }
+ return C2_OK;
+}
+
+bool C2SoftMpeg4Dec::handleResChange(const std::unique_ptr<C2Work> &work) {
+ uint32_t disp_width, disp_height;
+ PVGetVideoDimensions(mDecHandle, (int32 *)&disp_width, (int32 *)&disp_height);
+
+ uint32_t buf_width, buf_height;
+ PVGetBufferDimensions(mDecHandle, (int32 *)&buf_width, (int32 *)&buf_height);
+
+ CHECK_LE(disp_width, buf_width);
+ CHECK_LE(disp_height, buf_height);
+
+ ALOGV("display size (%dx%d), buffer size (%dx%d)",
+ disp_width, disp_height, buf_width, buf_height);
+
+ bool resChanged = false;
+ if (disp_width != mWidth || disp_height != mHeight) {
+ mWidth = disp_width;
+ mHeight = disp_height;
+ resChanged = true;
+ for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (mOutputBuffer[i]) {
+ free(mOutputBuffer[i]);
+ mOutputBuffer[i] = nullptr;
+ }
+ }
+
+ if (!mIsMpeg4) {
+ PVCleanUpVideoDecoder(mDecHandle);
+
+ uint8_t *vol_data[1]{};
+ int32_t vol_size = 0;
+
+ if (!PVInitVideoDecoder(
+ mDecHandle, vol_data, &vol_size, 1, mWidth, mHeight, H263_MODE)) {
+ ALOGE("Error in PVInitVideoDecoder H263_MODE while resChanged was set to true");
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return true;
+ }
+ }
+ mFramesConfigured = false;
+ }
+ return resChanged;
+}
+
+/* TODO: can remove temporary copy after library supports writing to display
+ * buffer Y, U and V plane pointers using stride info. */
+static void copyOutputBufferToYV12Frame(uint8_t *dst, uint8_t *src, size_t dstYStride,
+ size_t srcYStride, uint32_t width, uint32_t height) {
+ size_t dstUVStride = align(dstYStride / 2, 16);
+ size_t srcUVStride = srcYStride / 2;
+ uint8_t *srcStart = src;
+ uint8_t *dstStart = dst;
+ size_t vStride = align(height, 16);
+ for (size_t i = 0; i < height; ++i) {
+ memcpy(dst, src, width);
+ src += srcYStride;
+ dst += dstYStride;
+ }
+ /* U buffer */
+ src = srcStart + vStride * srcYStride;
+ dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+ for (size_t i = 0; i < height / 2; ++i) {
+ memcpy(dst, src, width / 2);
+ src += srcUVStride;
+ dst += dstUVStride;
+ }
+ /* V buffer */
+ src = srcStart + vStride * srcYStride * 5 / 4;
+ dst = dstStart + (dstYStride * height);
+ for (size_t i = 0; i < height / 2; ++i) {
+ memcpy(dst, src, width / 2);
+ src += srcUVStride;
+ dst += dstUVStride;
+ }
+}
+
+void C2SoftMpeg4Dec::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+ C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ 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);
+
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ if (inSize == 0) {
+ fillEmptyWork(work);
+ if (eos) {
+ mSignalledOutputEos = true;
+ }
+ return;
+ }
+
+ uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
+ uint32_t *start_code = (uint32_t *)bitstream;
+ bool volHeader = *start_code == 0xB0010000;
+ if (volHeader) {
+ PVCleanUpVideoDecoder(mDecHandle);
+ mInitialized = false;
+ }
+
+ if (!mInitialized) {
+ uint8_t *vol_data[1]{};
+ int32_t vol_size = 0;
+
+ bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+ if (codecConfig || volHeader) {
+ vol_data[0] = bitstream;
+ vol_size = inSize;
+ }
+ MP4DecodingMode mode = (mIsMpeg4) ? MPEG4_MODE : H263_MODE;
+
+ if (!PVInitVideoDecoder(
+ mDecHandle, vol_data, &vol_size, 1,
+ mWidth, mHeight, mode)) {
+ ALOGE("PVInitVideoDecoder failed. Unsupported content?");
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return;
+ }
+ mInitialized = true;
+ MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle);
+ if (mode != actualMode) {
+ ALOGE("Decoded mode not same as actual mode of the decoder");
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return;
+ }
+
+ PVSetPostProcType(mDecHandle, 0);
+ (void) handleResChange(work);
+ if (codecConfig) {
+ fillEmptyWork(work);
+ return;
+ }
+ }
+
+ while (inOffset < inSize) {
+ c2_status_t err = ensureDecoderState(pool);
+ if (C2_OK != err) {
+ mSignalledError = true;
+ work->result = err;
+ return;
+ }
+ C2GraphicView wView = mOutBlock->map().get();
+ if (wView.error()) {
+ ALOGE("graphic view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ uint32_t outSize = align(mWidth, 16) * align(mHeight, 16) * 3 / 2;
+ uint32_t yFrameSize = sizeof(uint8) * mDecHandle->size;
+ if (outSize < yFrameSize * 3 / 2){
+ ALOGE("Too small output buffer: %d bytes", outSize);
+ work->result = C2_NO_MEMORY;
+ mSignalledError = true;
+ return;
+ }
+
+ if (!mFramesConfigured) {
+ PVSetReferenceYUV(mDecHandle,mOutputBuffer[1]);
+ mFramesConfigured = true;
+ }
+
+ // Need to check if header contains new info, e.g., width/height, etc.
+ VopHeaderInfo header_info;
+ uint32_t useExtTimestamp = (inOffset == 0);
+ int32_t tmpInSize = (int32_t)inSize;
+ uint8_t *bitstreamTmp = bitstream;
+ uint32_t timestamp = workIndex;
+ if (PVDecodeVopHeader(
+ mDecHandle, &bitstreamTmp, ×tamp, &tmpInSize,
+ &header_info, &useExtTimestamp,
+ mOutputBuffer[mNumSamplesOutput & 1]) != PV_TRUE) {
+ ALOGE("failed to decode vop header.");
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return;
+ }
+
+ // H263 doesn't have VOL header, the frame size information is in short header, i.e. the
+ // decoder may detect size change after PVDecodeVopHeader.
+ bool resChange = handleResChange(work);
+ if (mIsMpeg4 && resChange) {
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return;
+ } else if (resChange) {
+ continue;
+ }
+
+ if (PVDecodeVopBody(mDecHandle, &tmpInSize) != PV_TRUE) {
+ ALOGE("failed to decode video frame.");
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return;
+ }
+ if (handleResChange(work)) {
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return;
+ }
+
+ uint8_t *outputBufferY = wView.data()[C2PlanarLayout::PLANE_Y];
+ (void)copyOutputBufferToYV12Frame(outputBufferY, mOutputBuffer[mNumSamplesOutput & 1],
+ wView.width(), align(mWidth, 16), mWidth, mHeight);
+
+ inOffset += inSize - (size_t)tmpInSize;
+ finishWork(workIndex, work);
+ ++mNumSamplesOutput;
+ if (inSize - inOffset) {
+ ALOGD("decoded frame, ignoring further trailing bytes %zu",
+ inSize - (size_t)tmpInSize);
+ break;
+ }
+ }
+}
+
+c2_status_t C2SoftMpeg4Dec::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ (void) 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;
+ }
+ return C2_OK;
+}
+
+class C2SoftMpeg4DecFactory : public C2ComponentFactory {
+public:
+ 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 C2SoftMpeg4Dec(kComponentName, id), 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 = BuildIntf(kComponentName, id, deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftMpeg4DecFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftMpeg4DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.h b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.h
new file mode 100644
index 0000000..8eb316e
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_SOFT_MPEG4_DEC_H_
+#define C2_SOFT_MPEG4_DEC_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+struct tagvideoDecControls;
+
+namespace android {
+
+struct C2SoftMpeg4Dec : public SimpleC2Component {
+ C2SoftMpeg4Dec(const char *name, c2_node_id_t id);
+ virtual ~C2SoftMpeg4Dec();
+
+ // 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:
+ enum {
+ kNumOutputBuffers = 2,
+ };
+
+ status_t initDecoder();
+ c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+ void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+ bool handleResChange(const std::unique_ptr<C2Work> &work);
+
+ tagvideoDecControls *mDecHandle;
+ std::shared_ptr<C2GraphicBlock> mOutBlock;
+ uint8_t *mOutputBuffer[kNumOutputBuffers];
+
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mNumSamplesOutput;
+
+ bool mIsMpeg4;
+ bool mInitialized;
+ bool mFramesConfigured;
+ bool mSignalledOutputEos;
+ bool mSignalledError;
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftMpeg4Dec);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_MPEG4_DEC_H_
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp
index fb0db8f..3123376 100644
--- a/media/libstagefright/codecs/mpeg2dec/Android.bp
+++ b/media/libstagefright/codecs/mpeg2dec/Android.bp
@@ -1,4 +1,47 @@
cc_library_shared {
+ name: "libstagefright_soft_c2mpeg2dec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftMpeg2Dec.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ include_dirs: [
+ "external/libmpeg2/decoder",
+ "external/libmpeg2/common",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ ],
+ cfi: false, // true,
+ diag: {
+ cfi: false, // true,
+ },
+ },
+
+ static_libs: ["libmpeg2dec"],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ ],
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
+
+cc_library_shared {
name: "libstagefright_soft_mpeg2dec",
vendor_available: true,
vndk: {
diff --git a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
new file mode 100644
index 0000000..0ebe7d6
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
@@ -0,0 +1,795 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftMpeg2Dec"
+#include <utils/Log.h>
+
+#include "iv_datatypedef.h"
+#include "iv.h"
+#include "ivd.h"
+#include "impeg2d.h"
+#include "C2SoftMpeg2Dec.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.mpeg2.decoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatVideo)
+ .inputMediaType(MEDIA_MIMETYPE_VIDEO_MPEG2)
+ .outputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+ .build();
+}
+
+static size_t getCpuCoreCount() {
+ long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+ return (size_t)cpuCoreCount;
+}
+
+static void *ivd_aligned_malloc(WORD32 alignment, WORD32 size) {
+ return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *mem) {
+ free(mem);
+}
+
+C2SoftMpeg2Dec::C2SoftMpeg2Dec(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
+ mDecHandle(nullptr),
+ mMemRecords(nullptr),
+ mOutBufferDrain(nullptr),
+ mIvColorformat(IV_YUV_420P),
+ mWidth(320),
+ mHeight(240) {
+ // If input dump is enabled, then open create an empty file
+ GENERATE_FILE_NAMES();
+ CREATE_DUMP_FILE(mInFile);
+}
+
+C2SoftMpeg2Dec::~C2SoftMpeg2Dec() {
+ onRelease();
+}
+
+c2_status_t C2SoftMpeg2Dec::onInit() {
+ status_t err = initDecoder();
+ return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftMpeg2Dec::onStop() {
+ if (OK != resetDecoder()) return C2_CORRUPTED;
+ resetPlugin();
+ return C2_OK;
+}
+
+void C2SoftMpeg2Dec::onReset() {
+ (void) onStop();
+}
+
+void C2SoftMpeg2Dec::onRelease() {
+ (void) deleteDecoder();
+ if (mOutBufferDrain) {
+ ivd_aligned_free(mOutBufferDrain);
+ mOutBufferDrain = nullptr;
+ }
+ if (mOutBlock) {
+ mOutBlock.reset();
+ }
+ if (mMemRecords) {
+ ivd_aligned_free(mMemRecords);
+ mMemRecords = nullptr;
+ }
+}
+
+c2_status_t C2SoftMpeg2Dec::onFlush_sm() {
+ if (OK != setFlushMode()) return C2_CORRUPTED;
+
+ uint32_t displayStride = mStride;
+ uint32_t displayHeight = mHeight;
+ uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
+ mOutBufferDrain = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
+ if (!mOutBufferDrain) {
+ ALOGE("could not allocate tmp output buffer (for flush) of size %u ", bufferSize);
+ return C2_NO_MEMORY;
+ }
+
+ while (true) {
+ ivd_video_decode_ip_t s_decode_ip;
+ ivd_video_decode_op_t s_decode_op;
+
+ setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, nullptr, 0, 0, 0);
+ (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+ if (0 == s_decode_op.u4_output_present) {
+ resetPlugin();
+ break;
+ }
+ }
+
+ ivd_aligned_free(mOutBufferDrain);
+ mOutBufferDrain = nullptr;
+
+ return C2_OK;
+}
+
+status_t C2SoftMpeg2Dec::getNumMemRecords() {
+ iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+ iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+ s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+ s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+ s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_num_mem_rec_ip,
+ &s_num_mem_rec_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in getting mem records: 0x%x", s_num_mem_rec_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::fillMemRecords() {
+ iv_mem_rec_t *ps_mem_rec = (iv_mem_rec_t *) ivd_aligned_malloc(
+ 128, mNumMemRecords * sizeof(iv_mem_rec_t));
+ if (!ps_mem_rec) {
+ ALOGE("Allocation failure");
+ return NO_MEMORY;
+ }
+ memset(ps_mem_rec, 0, mNumMemRecords * sizeof(iv_mem_rec_t));
+ for (size_t i = 0; i < mNumMemRecords; i++)
+ ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+ mMemRecords = ps_mem_rec;
+
+ ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+ ivdext_fill_mem_rec_op_t s_fill_mem_op;
+
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = sizeof(ivdext_fill_mem_rec_ip_t);
+ s_fill_mem_ip.u4_share_disp_buf = 0;
+ s_fill_mem_ip.e_output_format = mIvColorformat;
+ s_fill_mem_ip.u4_deinterlace = 1;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = mWidth;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = mHeight;
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size = sizeof(ivdext_fill_mem_rec_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_fill_mem_ip,
+ &s_fill_mem_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in filling mem records: 0x%x",
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ CHECK_EQ(mNumMemRecords, s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled);
+ for (size_t i = 0; i < mNumMemRecords; i++, ps_mem_rec++) {
+ ps_mem_rec->pv_base = ivd_aligned_malloc(
+ ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+ if (!ps_mem_rec->pv_base) {
+ ALOGE("Allocation failure for memory record #%zu of size %u",
+ i, ps_mem_rec->u4_mem_size);
+ return NO_MEMORY;
+ }
+ }
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::createDecoder() {
+ ivdext_init_ip_t s_init_ip;
+ ivdext_init_op_t s_init_op;
+
+ s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+ s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+ s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = mWidth;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight;
+ s_init_ip.u4_share_disp_buf = 0;
+ s_init_ip.u4_deinterlace = 1;
+ s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+ s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorformat;
+ s_init_op.s_ivd_init_op_t.u4_size = sizeof(ivdext_init_op_t);
+
+ mDecHandle = (iv_obj_t *)mMemRecords[0].pv_base;
+ mDecHandle->pv_fxns = (void *)ivdec_api_function;
+ mDecHandle->u4_size = sizeof(iv_obj_t);
+
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_init_ip,
+ &s_init_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__,
+ s_init_op.s_ivd_init_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::setNumCores() {
+ ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip;
+ ivdext_ctl_set_num_cores_op_t s_set_num_cores_op;
+
+ s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+ s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+ s_set_num_cores_ip.u4_num_cores = mNumCores;
+ s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_set_num_cores_ip,
+ &s_set_num_cores_op);
+ if (status != IV_SUCCESS) {
+ ALOGD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::setParams(size_t stride) {
+ ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
+ ivd_ctl_set_config_op_t s_set_dyn_params_op;
+
+ s_set_dyn_params_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+ s_set_dyn_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_dyn_params_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
+ s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+ s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ s_set_dyn_params_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+ s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_set_dyn_params_ip,
+ &s_set_dyn_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__, s_set_dyn_params_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::getVersion() {
+ ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip;
+ ivd_ctl_getversioninfo_op_t s_get_versioninfo_op;
+ UWORD8 au1_buf[512];
+
+ s_get_versioninfo_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+ s_get_versioninfo_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_get_versioninfo_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+ s_get_versioninfo_ip.pv_version_buffer = au1_buf;
+ s_get_versioninfo_ip.u4_version_buffer_size = sizeof(au1_buf);
+ s_get_versioninfo_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_get_versioninfo_ip,
+ &s_get_versioninfo_op);
+ if (status != IV_SUCCESS) {
+ ALOGD("error in %s: 0x%x", __func__,
+ s_get_versioninfo_op.u4_error_code);
+ } else {
+ ALOGV("ittiam decoder version number: %s",
+ (char *) s_get_versioninfo_ip.pv_version_buffer);
+ }
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::initDecoder() {
+ status_t ret = getNumMemRecords();
+ if (OK != ret) return ret;
+
+ ret = fillMemRecords();
+ if (OK != ret) return ret;
+
+ if (OK != createDecoder()) return UNKNOWN_ERROR;
+
+ mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
+ mStride = ALIGN64(mWidth);
+ mSignalledError = false;
+ mPreference = kPreferBitstream;
+ memset(&mDefaultColorAspects, 0, sizeof(ColorAspects));
+ memset(&mBitstreamColorAspects, 0, sizeof(ColorAspects));
+ memset(&mFinalColorAspects, 0, sizeof(ColorAspects));
+ mUpdateColorAspects = false;
+ resetPlugin();
+ (void) setNumCores();
+ if (OK != setParams(mStride)) return UNKNOWN_ERROR;
+ (void) getVersion();
+
+ return OK;
+}
+
+bool C2SoftMpeg2Dec::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+ ivd_video_decode_op_t *ps_decode_op,
+ C2ReadView *inBuffer,
+ C2GraphicView *outBuffer,
+ size_t inOffset,
+ size_t inSize,
+ uint32_t tsMarker) {
+ uint32_t displayStride = mStride;
+ uint32_t displayHeight = mHeight;
+ size_t lumaSize = displayStride * displayHeight;
+ size_t chromaSize = lumaSize >> 2;
+
+ ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+ ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+ if (inBuffer) {
+ ps_decode_ip->u4_ts = tsMarker;
+ ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer->data() + inOffset);
+ ps_decode_ip->u4_num_Bytes = inSize - inOffset;
+ } else {
+ ps_decode_ip->u4_ts = 0;
+ ps_decode_ip->pv_stream_buffer = nullptr;
+ ps_decode_ip->u4_num_Bytes = 0;
+ }
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+ if (outBuffer) {
+ if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+ ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
+ outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
+ return false;
+ }
+ ps_decode_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[C2PlanarLayout::PLANE_Y];
+ ps_decode_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[C2PlanarLayout::PLANE_U];
+ ps_decode_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+ } else {
+ ps_decode_ip->s_out_buffer.pu1_bufs[0] = mOutBufferDrain;
+ ps_decode_ip->s_out_buffer.pu1_bufs[1] = mOutBufferDrain + lumaSize;
+ ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferDrain + lumaSize + chromaSize;
+ }
+ ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+ ps_decode_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+ return true;
+}
+
+bool C2SoftMpeg2Dec::colorAspectsDiffer(
+ const ColorAspects &a, const ColorAspects &b) {
+ if (a.mRange != b.mRange
+ || a.mPrimaries != b.mPrimaries
+ || a.mTransfer != b.mTransfer
+ || a.mMatrixCoeffs != b.mMatrixCoeffs) {
+ return true;
+ }
+ return false;
+}
+
+void C2SoftMpeg2Dec::updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects) {
+ Mutex::Autolock autoLock(mColorAspectsLock);
+ ColorAspects newAspects;
+ newAspects.mRange = preferredAspects.mRange != ColorAspects::RangeUnspecified ?
+ preferredAspects.mRange : otherAspects.mRange;
+ newAspects.mPrimaries = preferredAspects.mPrimaries != ColorAspects::PrimariesUnspecified ?
+ preferredAspects.mPrimaries : otherAspects.mPrimaries;
+ newAspects.mTransfer = preferredAspects.mTransfer != ColorAspects::TransferUnspecified ?
+ preferredAspects.mTransfer : otherAspects.mTransfer;
+ newAspects.mMatrixCoeffs = preferredAspects.mMatrixCoeffs != ColorAspects::MatrixUnspecified ?
+ preferredAspects.mMatrixCoeffs : otherAspects.mMatrixCoeffs;
+
+ // Check to see if need update mFinalColorAspects.
+ if (colorAspectsDiffer(mFinalColorAspects, newAspects)) {
+ mFinalColorAspects = newAspects;
+ mUpdateColorAspects = true;
+ }
+}
+
+status_t C2SoftMpeg2Dec::handleColorAspectsChange() {
+ if (mPreference == kPreferBitstream) {
+ updateFinalColorAspects(mDefaultColorAspects, mBitstreamColorAspects);
+ } else if (mPreference == kPreferContainer) {
+ updateFinalColorAspects(mBitstreamColorAspects, mDefaultColorAspects);
+ } else {
+ return C2_CORRUPTED;
+ }
+ return C2_OK;
+}
+
+bool C2SoftMpeg2Dec::getSeqInfo() {
+ ivdext_ctl_get_seq_info_ip_t s_ctl_get_seq_info_ip;
+ ivdext_ctl_get_seq_info_op_t s_ctl_get_seq_info_op;
+
+ s_ctl_get_seq_info_ip.u4_size = sizeof(ivdext_ctl_get_seq_info_ip_t);
+ s_ctl_get_seq_info_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_get_seq_info_ip.e_sub_cmd =
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_GET_SEQ_INFO;
+ s_ctl_get_seq_info_op.u4_size = sizeof(ivdext_ctl_get_seq_info_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_ctl_get_seq_info_ip,
+ &s_ctl_get_seq_info_op);
+ if (status != IV_SUCCESS) {
+ ALOGW("Error in getting Sequence info: 0x%x", s_ctl_get_seq_info_op.u4_error_code);
+ return false;
+ }
+
+ int32_t primaries = s_ctl_get_seq_info_op.u1_colour_primaries;
+ int32_t transfer = s_ctl_get_seq_info_op.u1_transfer_characteristics;
+ int32_t coeffs = s_ctl_get_seq_info_op.u1_matrix_coefficients;
+ bool full_range = false; // mpeg2 video has limited range.
+
+ ColorAspects colorAspects;
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ primaries, transfer, coeffs, full_range, colorAspects);
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+ mBitstreamColorAspects = colorAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+
+ return true;
+}
+
+status_t C2SoftMpeg2Dec::setFlushMode() {
+ ivd_ctl_flush_ip_t s_set_flush_ip;
+ ivd_ctl_flush_op_t s_set_flush_op;
+
+ s_set_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+ s_set_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+ s_set_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_set_flush_ip,
+ &s_set_flush_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__, s_set_flush_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::resetDecoder() {
+ ivd_ctl_reset_ip_t s_reset_ip;
+ ivd_ctl_reset_op_t s_reset_op;
+
+ s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+ s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+ s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_reset_ip,
+ &s_reset_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ (void) setNumCores();
+ mStride = 0;
+ mSignalledError = false;
+
+ return OK;
+}
+
+void C2SoftMpeg2Dec::resetPlugin() {
+ mSignalledOutputEos = false;
+ gettimeofday(&mTimeStart, nullptr);
+ gettimeofday(&mTimeEnd, nullptr);
+}
+
+status_t C2SoftMpeg2Dec::deleteDecoder() {
+ if (mMemRecords) {
+ iv_mem_rec_t *ps_mem_rec = mMemRecords;
+
+ for (size_t i = 0; i < mNumMemRecords; i++, ps_mem_rec++) {
+ if (ps_mem_rec->pv_base) {
+ ivd_aligned_free(ps_mem_rec->pv_base);
+ }
+ }
+ ivd_aligned_free(mMemRecords);
+ mMemRecords = nullptr;
+ }
+ mDecHandle = nullptr;
+
+ return OK;
+}
+
+status_t C2SoftMpeg2Dec::reInitDecoder() {
+ deleteDecoder();
+
+ status_t ret = initDecoder();
+ if (OK != ret) {
+ ALOGE("Failed to initialize decoder");
+ deleteDecoder();
+ return ret;
+ }
+ return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling eos");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+void C2SoftMpeg2Dec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+ std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+ C2Rect(mWidth, mHeight));
+ mOutBlock = nullptr;
+ auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+ (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling eos");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ };
+ if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+ fillWork(work);
+ } else {
+ finish(index, fillWork);
+ }
+}
+
+c2_status_t C2SoftMpeg2Dec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+ if (!mDecHandle) {
+ ALOGE("not supposed to be here, invalid decoder context");
+ return C2_CORRUPTED;
+ }
+ if (mStride != ALIGN64(mWidth)) {
+ mStride = ALIGN64(mWidth);
+ if (OK != setParams(mStride)) return C2_CORRUPTED;
+ }
+ if (mOutBlock &&
+ (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ mOutBlock.reset();
+ }
+ if (!mOutBlock) {
+ uint32_t format = HAL_PIXEL_FORMAT_YV12;
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ if (err != C2_OK) {
+ ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+ return err;
+ }
+ ALOGV("provided (%dx%d) required (%dx%d)",
+ mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ }
+
+ return C2_OK;
+}
+
+// TODO: can overall error checking be improved?
+// TODO: allow configuration of color format and usage for graphic buffers instead
+// of hard coding them to HAL_PIXEL_FORMAT_YV12
+// TODO: pass coloraspects information to surface
+// TODO: test support for dynamic change in resolution
+// TODO: verify if the decoder sent back all frames
+void C2SoftMpeg2Dec::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+ C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ bool hasPicture = false;
+
+ 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);
+ while (inOffset < inSize) {
+ if (C2_OK != ensureDecoderState(pool)) {
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ C2GraphicView wView = mOutBlock->map().get();
+ if (wView.error()) {
+ ALOGE("graphic view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ ivd_video_decode_ip_t s_decode_ip;
+ ivd_video_decode_op_t s_decode_op;
+ if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
+ inOffset, inSize, workIndex)) {
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ // If input dump is enabled, then write to file
+ DUMP_TO_FILE(mInFile, s_decode_ip.pv_stream_buffer, s_decode_ip.u4_num_Bytes);
+ WORD32 delay;
+ GETTIME(&mTimeStart, NULL);
+ TIME_DIFF(mTimeEnd, mTimeStart, delay);
+ (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+ WORD32 decodeTime;
+ GETTIME(&mTimeEnd, nullptr);
+ TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+ ALOGV("decodeTime=%6d delay=%6d numBytes=%6d ", decodeTime, delay,
+ s_decode_op.u4_num_bytes_consumed);
+ if (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_decode_op.u4_error_code) {
+ ALOGV("unsupported resolution : %dx%d", s_decode_op.u4_pic_wd, s_decode_op.u4_pic_ht);
+ drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+ resetPlugin();
+ mWidth = s_decode_op.u4_pic_wd;
+ mHeight = s_decode_op.u4_pic_ht;
+ if (OK != reInitDecoder()) {
+ ALOGE("Failed to reinitialize decoder");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ continue;
+ } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & 0xFF)) {
+ ALOGV("resolution changed");
+ drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+ resetDecoder();
+ resetPlugin();
+ mWidth = s_decode_op.u4_pic_wd;
+ mHeight = s_decode_op.u4_pic_ht;
+ continue;
+ }
+
+ (void) getSeqInfo();
+ if (mUpdateColorAspects) {
+ mUpdateColorAspects = false;
+ }
+ hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
+ if (s_decode_op.u4_output_present) {
+ finishWork(s_decode_op.u4_ts, work);
+ }
+ inOffset += s_decode_op.u4_num_bytes_consumed;
+ if (hasPicture && (inSize - inOffset)) {
+ ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
+ (int)inSize - (int)inOffset);
+ break;
+ }
+ }
+
+ if (eos) {
+ drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+ mSignalledOutputEos = true;
+ } else if (!hasPicture) {
+ fillEmptyWork(work);
+ }
+}
+
+c2_status_t C2SoftMpeg2Dec::drainInternal(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work) {
+ 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;
+ }
+
+ if (OK != setFlushMode()) return C2_CORRUPTED;
+ while (true) {
+ if (C2_OK != ensureDecoderState(pool)) {
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return C2_CORRUPTED;
+ }
+ C2GraphicView wView = mOutBlock->map().get();
+ if (wView.error()) {
+ ALOGE("graphic view map failed %d", wView.error());
+ return C2_CORRUPTED;
+ }
+ ivd_video_decode_ip_t s_decode_ip;
+ ivd_video_decode_op_t s_decode_op;
+ if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
+ mSignalledError = true;
+ return C2_CORRUPTED;
+ }
+ (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+ if (s_decode_op.u4_output_present) {
+ finishWork(s_decode_op.u4_ts, work);
+ } else {
+ break;
+ }
+ }
+ if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
+ work && work->workletsProcessed == 0u) {
+ fillEmptyWork(work);
+ }
+
+ return C2_OK;
+}
+
+c2_status_t C2SoftMpeg2Dec::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftMpeg2DecFactory : public C2ComponentFactory {
+public:
+ 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 C2SoftMpeg2Dec(kComponentName, id), 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 = BuildIntf(kComponentName, id, deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftMpeg2DecFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftMpeg2DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.h b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.h
new file mode 100644
index 0000000..64e5b05
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_SOFT_MPEG2_DEC_H_
+#define C2_SOFT_MPEG2_DEC_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/ColorUtils.h>
+
+namespace android {
+
+#define ivdec_api_function impeg2d_api_function
+#define ivdext_init_ip_t impeg2d_init_ip_t
+#define ivdext_init_op_t impeg2d_init_op_t
+#define ivdext_fill_mem_rec_ip_t impeg2d_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t impeg2d_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t impeg2d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t impeg2d_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_seq_info_ip_t impeg2d_ctl_get_seq_info_ip_t
+#define ivdext_ctl_get_seq_info_op_t impeg2d_ctl_get_seq_info_op_t
+#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
+#define MAX_NUM_CORES 4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES \
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define GETTIME(a, b) gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff) \
+ diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+ ((end).tv_usec - (start).tv_usec);
+
+#ifdef FILE_DUMP_ENABLE
+ #define INPUT_DUMP_PATH "/sdcard/clips/mpeg2d_input"
+ #define INPUT_DUMP_EXT "m2v"
+ #define GENERATE_FILE_NAMES() { \
+ GETTIME(&mTimeStart, NULL); \
+ strcpy(mInFile, ""); \
+ sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \
+ mTimeStart.tv_sec, mTimeStart.tv_usec, \
+ INPUT_DUMP_EXT); \
+ }
+ #define CREATE_DUMP_FILE(m_filename) { \
+ FILE *fp = fopen(m_filename, "wb"); \
+ if (fp != NULL) { \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not open file %s", m_filename); \
+ } \
+ }
+ #define DUMP_TO_FILE(m_filename, m_buf, m_size) \
+ { \
+ FILE *fp = fopen(m_filename, "ab"); \
+ if (fp != NULL && m_buf != NULL) { \
+ uint32_t i; \
+ i = fwrite(m_buf, 1, m_size, fp); \
+ ALOGD("fwrite ret %d to write %d", i, m_size); \
+ if (i != (uint32_t)m_size) { \
+ ALOGD("Error in fwrite, returned %d", i); \
+ perror("Error in write to file"); \
+ } \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not write to file %s", m_filename);\
+ } \
+ }
+#else /* FILE_DUMP_ENABLE */
+ #define INPUT_DUMP_PATH
+ #define INPUT_DUMP_EXT
+ #define OUTPUT_DUMP_PATH
+ #define OUTPUT_DUMP_EXT
+ #define GENERATE_FILE_NAMES()
+ #define CREATE_DUMP_FILE(m_filename)
+ #define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+struct C2SoftMpeg2Dec : public SimpleC2Component {
+ C2SoftMpeg2Dec(const char *name, c2_node_id_t id);
+ virtual ~C2SoftMpeg2Dec();
+
+ // 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:
+ status_t getNumMemRecords();
+ status_t fillMemRecords();
+ status_t createDecoder();
+ status_t setNumCores();
+ status_t setParams(size_t stride);
+ status_t getVersion();
+ status_t initDecoder();
+ bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+ ivd_video_decode_op_t *ps_decode_op,
+ C2ReadView *inBuffer,
+ C2GraphicView *outBuffer,
+ size_t inOffset,
+ size_t inSize,
+ uint32_t tsMarker);
+ bool getSeqInfo();
+ // TODO:This is not the right place for colorAspects functions. These should
+ // be part of c2-vndk so that they can be accessed by all video plugins
+ // until then, make them feel at home
+ bool colorAspectsDiffer(const ColorAspects &a, const ColorAspects &b);
+ void updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects);
+ status_t handleColorAspectsChange();
+ c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+ void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+ status_t setFlushMode();
+ c2_status_t drainInternal(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work);
+ status_t resetDecoder();
+ void resetPlugin();
+ status_t deleteDecoder();
+ status_t reInitDecoder();
+
+ // TODO:This is not the right place for this enum. These should
+ // be part of c2-vndk so that they can be accessed by all video plugins
+ // until then, make them feel at home
+ enum {
+ kNotSupported,
+ kPreferBitstream,
+ kPreferContainer,
+ };
+
+ iv_obj_t *mDecHandle;
+ iv_mem_rec_t *mMemRecords;
+ size_t mNumMemRecords;
+ std::shared_ptr<C2GraphicBlock> mOutBlock;
+ uint8_t *mOutBufferDrain;
+
+ size_t mNumCores;
+ IV_COLOR_FORMAT_T mIvColorformat;
+
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mStride;
+ bool mSignalledOutputEos;
+ bool mSignalledError;
+
+ // ColorAspects
+ Mutex mColorAspectsLock;
+ int mPreference;
+ ColorAspects mDefaultColorAspects;
+ ColorAspects mBitstreamColorAspects;
+ ColorAspects mFinalColorAspects;
+ bool mUpdateColorAspects;
+
+ // profile
+ struct timeval mTimeStart;
+ struct timeval mTimeEnd;
+#ifdef FILE_DUMP_ENABLE
+ char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftMpeg2Dec);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_MPEG2_DEC_H_
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index 8a9399a..03f0c05 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -37,3 +37,82 @@
},
compile_multilib: "32",
}
+
+cc_library_shared {
+ name: "libstagefright_soft_c2vp9dec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftVpx.cpp"],
+
+ static_libs: ["libvpx"],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ ],
+
+ cflags: [
+ "-DVP9",
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
+
+cc_library_shared {
+ name: "libstagefright_soft_c2vp8dec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftVpx.cpp"],
+
+ static_libs: ["libvpx"],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
diff --git a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
new file mode 100644
index 0000000..96b303c
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
@@ -0,0 +1,411 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftVpx"
+#include <utils/Log.h>
+
+#include "C2SoftVpx.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+#ifdef VP9
+constexpr char kComponentName[] = "c2.google.vp9.decoder";
+#else
+constexpr char kComponentName[] = "c2.google.vp8.decoder";
+#endif
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatVideo)
+ .inputMediaType(
+#ifdef VP9
+ MEDIA_MIMETYPE_VIDEO_VP9
+#else
+ MEDIA_MIMETYPE_VIDEO_VP8
+#endif
+ )
+ .outputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+ .build();
+}
+
+C2SoftVpx::C2SoftVpx(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
+ mCodecCtx(nullptr) {
+}
+
+C2SoftVpx::~C2SoftVpx() {
+ onRelease();
+}
+
+c2_status_t C2SoftVpx::onInit() {
+ status_t err = initDecoder();
+ return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftVpx::onStop() {
+ (void) onFlush_sm();
+ destroyDecoder();
+
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+
+ return C2_OK;
+}
+
+void C2SoftVpx::onReset() {
+ (void) onStop();
+ (void) initDecoder();
+}
+
+void C2SoftVpx::onRelease() {
+ destroyDecoder();
+}
+
+c2_status_t C2SoftVpx::onFlush_sm() {
+ if (mFrameParallelMode) {
+ // Flush decoder by passing nullptr data ptr and 0 size.
+ // Ideally, this should never fail.
+ if (vpx_codec_decode(mCodecCtx, nullptr, 0, nullptr, 0)) {
+ ALOGE("Failed to flush on2 decoder.");
+ return C2_CORRUPTED;
+ }
+ }
+
+ // Drop all the decoded frames in decoder.
+ vpx_codec_iter_t iter = nullptr;
+ while (vpx_codec_get_frame(mCodecCtx, &iter)) {
+ }
+
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+ return C2_OK;
+}
+
+static int GetCPUCoreCount() {
+ int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGV("Number of CPU cores: %d", cpuCoreCount);
+ return cpuCoreCount;
+}
+
+status_t C2SoftVpx::initDecoder() {
+#ifdef VP9
+ mMode = MODE_VP9;
+#else
+ mMode = MODE_VP8;
+#endif
+
+ mWidth = 320;
+ mHeight = 240;
+ mFrameParallelMode = false;
+ mSignalledOutputEos = false;
+ mSignalledError = false;
+
+ mCodecCtx = new vpx_codec_ctx_t;
+
+ vpx_codec_dec_cfg_t cfg;
+ memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
+ cfg.threads = GetCPUCoreCount();
+
+ vpx_codec_flags_t flags;
+ memset(&flags, 0, sizeof(vpx_codec_flags_t));
+ if (mFrameParallelMode) flags |= VPX_CODEC_USE_FRAME_THREADING;
+
+ vpx_codec_err_t vpx_err;
+ if ((vpx_err = vpx_codec_dec_init(
+ mCodecCtx, mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
+ &cfg, flags))) {
+ ALOGE("on2 decoder failed to initialize. (%d)", vpx_err);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t C2SoftVpx::destroyDecoder() {
+ if (mCodecCtx) {
+ vpx_codec_destroy(mCodecCtx);
+ delete mCodecCtx;
+ mCodecCtx = nullptr;
+ }
+
+ return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling eos");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+void C2SoftVpx::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2GraphicBlock> &block) {
+ std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(block,
+ C2Rect(mWidth, mHeight));
+ auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+ (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ ALOGV("signalling eos");
+ }
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ };
+ if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+ fillWork(work);
+ } else {
+ finish(index, fillWork);
+ }
+}
+
+void C2SoftVpx::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ bool codecConfig = ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) !=0);
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+
+ 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);
+
+ // Software VP9 Decoder does not need the Codec Specific Data (CSD)
+ // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if
+ // it was passed.
+ if (codecConfig) {
+ // Ignore CSD buffer for VP9.
+ if (mMode == MODE_VP9) {
+ fillEmptyWork(work);
+ return;
+ } else {
+ // Tolerate the CSD buffer for VP8. This is a workaround
+ // for b/28689536. continue
+ ALOGW("WARNING: Got CSD buffer for VP8. Continue");
+ }
+ }
+
+ uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
+ int64_t frameIndex = work->input.ordinal.frameIndex.peekll();
+
+ if (inSize) {
+ vpx_codec_err_t err = vpx_codec_decode(
+ mCodecCtx, bitstream, inSize, &frameIndex, 0);
+ if (err != VPX_CODEC_OK) {
+ ALOGE("on2 decoder failed to decode frame. err: %d", err);
+ work->result = C2_CORRUPTED;
+ mSignalledError = true;
+ return;
+ }
+ }
+
+ (void)outputBuffer(pool, work);
+
+ if (eos) {
+ drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+ mSignalledOutputEos = true;
+ } else if (!inSize) {
+ fillEmptyWork(work);
+ }
+}
+
+static void copyOutputBufferToYV12Frame(uint8_t *dst,
+ const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+ size_t srcYStride, size_t srcUStride, size_t srcVStride,
+ uint32_t width, uint32_t height, int32_t bpp) {
+ size_t dstYStride = align(width, 16) * bpp ;
+ size_t dstUVStride = align(dstYStride / 2, 16);
+ uint8_t *dstStart = dst;
+
+ for (size_t i = 0; i < height; ++i) {
+ memcpy(dst, srcY, width * bpp);
+ srcY += srcYStride;
+ dst += dstYStride;
+ }
+
+ dst = dstStart + dstYStride * height;
+ for (size_t i = 0; i < height / 2; ++i) {
+ memcpy(dst, srcV, width / 2 * bpp);
+ srcV += srcVStride;
+ dst += dstUVStride;
+ }
+
+ dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+ for (size_t i = 0; i < height / 2; ++i) {
+ memcpy(dst, srcU, width / 2 * bpp);
+ srcU += srcUStride;
+ dst += dstUVStride;
+ }
+}
+
+bool C2SoftVpx::outputBuffer(
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work)
+{
+ if (!(work && pool)) return false;
+
+ vpx_codec_iter_t iter = nullptr;
+ vpx_image_t *img = vpx_codec_get_frame(mCodecCtx, &iter);
+
+ if (!img) return false;
+
+ mWidth = img->d_w;
+ mHeight = img->d_h;
+
+ CHECK(img->fmt == VPX_IMG_FMT_I420 || img->fmt == VPX_IMG_FMT_I42016);
+ int32_t bpp = 1;
+ if (img->fmt == VPX_IMG_FMT_I42016) {
+ bpp = 2;
+ }
+
+ std::shared_ptr<C2GraphicBlock> block;
+ uint32_t format = HAL_PIXEL_FORMAT_YV12;
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16) * bpp, mHeight, format, usage, &block);
+ if (err != C2_OK) {
+ ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+ work->result = err;
+ return false;
+ }
+
+ C2GraphicView wView = block->map().get();
+ if (wView.error()) {
+ ALOGE("graphic view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return false;
+ }
+
+ ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d",
+ block->width(), block->height(), mWidth, mHeight, (int)*(int64_t *)img->user_priv);
+
+ uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ size_t srcYStride = img->stride[VPX_PLANE_Y];
+ size_t srcUStride = img->stride[VPX_PLANE_U];
+ size_t srcVStride = img->stride[VPX_PLANE_V];
+ const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y];
+ const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U];
+ const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V];
+ copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV,
+ srcYStride, srcUStride, srcVStride, mWidth, mHeight, bpp);
+
+ finishWork(*(int64_t *)img->user_priv, work, std::move(block));
+ return true;
+}
+
+c2_status_t C2SoftVpx::drainInternal(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work) {
+ 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;
+ }
+
+ while ((outputBuffer(pool, work))) {
+ }
+
+ if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
+ work && work->workletsProcessed == 0u) {
+ fillEmptyWork(work);
+ }
+
+ return C2_OK;
+}
+c2_status_t C2SoftVpx::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftVpxFactory : public C2ComponentFactory {
+public:
+ 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 C2SoftVpx(kComponentName, id), 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 = BuildIntf(kComponentName, id, deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftVpxFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftVpxFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/libstagefright/codecs/on2/dec/C2SoftVpx.h b/media/libstagefright/codecs/on2/dec/C2SoftVpx.h
new file mode 100644
index 0000000..b5d4e21
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/C2SoftVpx.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_SOFT_VPX_H_
+#define C2_SOFT_VPX_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+#include "vpx/vpx_decoder.h"
+#include "vpx/vp8dx.h"
+
+namespace android {
+
+struct C2SoftVpx : public SimpleC2Component {
+ C2SoftVpx(const char *name, c2_node_id_t id);
+ virtual ~C2SoftVpx();
+
+ // 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:
+ enum {
+ MODE_VP8,
+ MODE_VP9,
+ } mMode;
+
+ vpx_codec_ctx_t *mCodecCtx;
+ bool mFrameParallelMode; // Frame parallel is only supported by VP9 decoder.
+
+ uint32_t mWidth;
+ uint32_t mHeight;
+ bool mSignalledOutputEos;
+ bool mSignalledError;
+
+ status_t initDecoder();
+ status_t destroyDecoder();
+ void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2GraphicBlock> &block);
+ bool outputBuffer(
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work);
+ c2_status_t drainInternal(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work);
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftVpx);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_VPX_H_
diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp
index 43318f2..38d72e6 100644
--- a/media/libstagefright/codecs/opus/dec/Android.bp
+++ b/media/libstagefright/codecs/opus/dec/Android.bp
@@ -1,4 +1,40 @@
cc_library_shared {
+ name: "libstagefright_soft_c2opusdec",
+// vendor_available: true,
+// vndk: {
+// enabled: true,
+// },
+
+ srcs: ["C2SoftOpus.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_foundation",
+ "libstagefright_simple_c2component",
+ "libopus",
+ ],
+}
+
+cc_library_shared {
name: "libstagefright_soft_opusdec",
vendor_available: true,
vndk: {
diff --git a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
new file mode 100644
index 0000000..4eec362
--- /dev/null
+++ b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
@@ -0,0 +1,448 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftOpus"
+#include <utils/Log.h>
+
+#include "C2SoftOpus.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+extern "C" {
+ #include <opus.h>
+ #include <opus_multistream.h>
+}
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.opus.decoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatAudio)
+ .inputMediaType(MEDIA_MIMETYPE_AUDIO_OPUS)
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .build();
+}
+
+C2SoftOpus::C2SoftOpus(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
+ mDecoder(nullptr) {
+}
+
+C2SoftOpus::~C2SoftOpus() {
+ onRelease();
+}
+
+c2_status_t C2SoftOpus::onInit() {
+ status_t err = initDecoder();
+ return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftOpus::onStop() {
+ if (mDecoder) {
+ opus_multistream_decoder_destroy(mDecoder);
+ mDecoder = nullptr;
+ }
+ memset(&mHeader, 0, sizeof(mHeader));
+ mCodecDelay = 0;
+ mSeekPreRoll = 0;
+ mSamplesToDiscard = 0;
+ mInputBufferCount = 0;
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+
+ return C2_OK;
+}
+
+void C2SoftOpus::onReset() {
+ (void)onStop();
+}
+
+void C2SoftOpus::onRelease() {
+ if (mDecoder) {
+ opus_multistream_decoder_destroy(mDecoder);
+ mDecoder = nullptr;
+ }
+}
+
+status_t C2SoftOpus::initDecoder() {
+ memset(&mHeader, 0, sizeof(mHeader));
+ mCodecDelay = 0;
+ mSeekPreRoll = 0;
+ mSamplesToDiscard = 0;
+ mInputBufferCount = 0;
+ mSignalledError = false;
+ mSignalledOutputEos = false;
+
+ return OK;
+}
+
+c2_status_t C2SoftOpus::onFlush_sm() {
+ if (mDecoder) {
+ opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE);
+ mSamplesToDiscard = mSeekPreRoll;
+ mSignalledOutputEos = false;
+ }
+ return C2_OK;
+}
+
+c2_status_t C2SoftOpus::drain(
+ uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ (void) 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;
+ }
+
+ return C2_OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ 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
+// mappings for up to 8 channels. This information is part of the Vorbis I
+// Specification:
+// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
+static const int kMaxChannels = 8;
+
+// Maximum packet size used in Xiph's opusdec.
+static const 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.
+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) {
+ return static_cast<double>(ns) * rate / 1000000000;
+}
+
+void C2SoftOpus::process(
+ const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ size_t inOffset = inBuffer.offset();
+ size_t inSize = inBuffer.size();
+ C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ if (inSize == 0) {
+ fillEmptyWork(work);
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+ return;
+ }
+
+ ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+ (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+ const uint8_t *data = rView.data() + inOffset;
+ if (mInputBufferCount < 3) {
+ if (mInputBufferCount == 0) {
+ if (!ParseOpusHeader(data, inSize, &mHeader)) {
+ ALOGE("Encountered error while Parsing Opus Header.");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ uint8_t channel_mapping[kMaxChannels] = {0};
+ if (mHeader.channels <= kMaxChannelsWithDefaultLayout) {
+ memcpy(&channel_mapping,
+ kDefaultOpusChannelLayout,
+ kMaxChannelsWithDefaultLayout);
+ } else {
+ memcpy(&channel_mapping,
+ mHeader.stream_map,
+ mHeader.channels);
+ }
+ int status = OPUS_INVALID_STATE;
+ mDecoder = opus_multistream_decoder_create(kRate,
+ mHeader.channels,
+ mHeader.num_streams,
+ mHeader.num_coupled,
+ channel_mapping,
+ &status);
+ if (!mDecoder || status != OPUS_OK) {
+ ALOGE("opus_multistream_decoder_create failed status = %s",
+ opus_strerror(status));
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ status = opus_multistream_decoder_ctl(mDecoder,
+ OPUS_SET_GAIN(mHeader.gain_db));
+ if (status != OPUS_OK) {
+ ALOGE("Failed to set OPUS header gain; status = %s",
+ opus_strerror(status));
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ } else {
+ if (inSize < 8) {
+ ALOGE("Input sample size is too small.");
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ int64_t samples = ns_to_samples( *(reinterpret_cast<int64_t*>
+ (const_cast<uint8_t *> (data))), kRate);
+ if (mInputBufferCount == 1) {
+ mCodecDelay = samples;
+ mSamplesToDiscard = mCodecDelay;
+ }
+ else {
+ mSeekPreRoll = samples;
+ }
+ }
+
+ ++mInputBufferCount;
+ fillEmptyWork(work);
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+ return;
+ }
+
+ // Ignore CSD re-submissions.
+ if ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG)) {
+ fillEmptyWork(work);
+ return;
+ }
+
+ // When seeking to zero, |mCodecDelay| samples has to be discarded
+ // instead of |mSeekPreRoll| samples (as we would when seeking to any
+ // other timestamp).
+ if (work->input.ordinal.timestamp.peeku() == 0) mSamplesToDiscard = mCodecDelay;
+
+ std::shared_ptr<C2LinearBlock> block;
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchLinearBlock(
+ kMaxNumSamplesPerBuffer * kMaxChannels * sizeof(int16_t),
+ usage, &block);
+ if (err != C2_OK) {
+ ALOGE("fetchLinearBlock for Output failed with status %d", err);
+ work->result = C2_NO_MEMORY;
+ return;
+ }
+ C2WriteView wView = block->map().get();
+ if (wView.error()) {
+ ALOGE("write view map failed %d", wView.error());
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ int numSamples = opus_multistream_decode(mDecoder,
+ data,
+ inSize,
+ reinterpret_cast<int16_t *> (wView.data()),
+ kMaxOpusOutputPacketSizeSamples,
+ 0);
+ if (numSamples < 0) {
+ ALOGE("opus_multistream_decode returned numSamples %d", numSamples);
+ numSamples = 0;
+ mSignalledError = true;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ int outOffset = 0;
+ if (mSamplesToDiscard > 0) {
+ if (mSamplesToDiscard > numSamples) {
+ mSamplesToDiscard -= numSamples;
+ numSamples = 0;
+ } else {
+ numSamples -= mSamplesToDiscard;
+ outOffset = mSamplesToDiscard * sizeof(int16_t) * mHeader.channels;
+ mSamplesToDiscard = 0;
+ }
+ }
+
+ if (numSamples) {
+ int outSize = numSamples * sizeof(int16_t) * mHeader.channels;
+ ALOGV("out buffer attr. offset %d size %d ", outOffset, outSize);
+
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, outOffset, outSize));
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ } else {
+ fillEmptyWork(work);
+ block.reset();
+ }
+ if (eos) {
+ mSignalledOutputEos = true;
+ ALOGV("signalled EOS");
+ }
+}
+
+class C2SoftOpusDecFactory : public C2ComponentFactory {
+public:
+ 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 C2SoftOpus(kComponentName, id), 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 = BuildIntf(kComponentName, id, deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2SoftOpusDecFactory() override = default;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+ ALOGV("in %s", __func__);
+ return new ::android::C2SoftOpusDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+ ALOGV("in %s", __func__);
+ delete factory;
+}
diff --git a/media/libstagefright/codecs/opus/dec/C2SoftOpus.h b/media/libstagefright/codecs/opus/dec/C2SoftOpus.h
new file mode 100644
index 0000000..70ad2de
--- /dev/null
+++ b/media/libstagefright/codecs/opus/dec/C2SoftOpus.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_SOFT_OPUS_H_
+#define C2_SOFT_OPUS_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+struct OpusMSDecoder;
+
+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 C2SoftOpus : public SimpleC2Component {
+ C2SoftOpus(const char *name, c2_node_id_t id);
+ virtual ~C2SoftOpus();
+
+ // 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:
+ enum {
+ kMaxNumSamplesPerBuffer = 960 * 6
+ };
+
+ OpusMSDecoder *mDecoder;
+ OpusHeader mHeader;
+
+ int64_t mCodecDelay;
+ int64_t mSeekPreRoll;
+ int64_t mSamplesToDiscard;
+ size_t mInputBufferCount;
+ bool mSignalledError;
+ bool mSignalledOutputEos;
+
+ status_t initDecoder();
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftOpus);
+};
+
+} // namespace android
+
+#endif // C2_SOFT_OPUS_H_
diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp
index 8a77401..b6b0829 100644
--- a/media/libstagefright/httplive/Android.bp
+++ b/media/libstagefright/httplive/Android.bp
@@ -37,6 +37,7 @@
"libcutils",
"libmedia",
"libmediaextractor",
+ "libmediandk",
"libstagefright",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index fbf1496..52a7afc 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -11,6 +11,7 @@
include_dirs: [
"frameworks/av/media/libstagefright",
+ "frameworks/av/media/ndk/include",
"frameworks/native/include/media/openmax",
],
@@ -33,6 +34,7 @@
shared_libs: [
"libcrypto",
"libmedia",
+ "libmediandk",
"libhidlallocatorutils",
"android.hardware.cas.native@1.0",
"android.hidl.memory@1.0",
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 8488d10..972babd 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -19,6 +19,7 @@
#include "AnotherPacketSource.h"
+#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -218,7 +219,7 @@
}
sp<ABuffer> mpegUserData;
- if (buffer->meta()->findBuffer("mpegUserData", &mpegUserData) && mpegUserData != NULL) {
+ if (buffer->meta()->findBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, &mpegUserData) && mpegUserData != NULL) {
bufmeta.setData(
kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size());
}
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 59fba1a..1147555 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -20,6 +20,7 @@
#include "ESQueue.h"
+#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -865,6 +866,7 @@
}
bits.skipBits(2); // original_copy, home
+ mFormat = new MetaData;
MakeAACCodecSpecificData(*mFormat,
profile, sampling_freq_index, channel_configuration);
@@ -1474,7 +1476,7 @@
mpegUserData->data() + i * sizeof(size_t),
&userDataPositions[i], sizeof(size_t));
}
- accessUnit->meta()->setBuffer("mpegUserData", mpegUserData);
+ accessUnit->meta()->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
}
}
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index ea43d2e..ac837a3 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -43,7 +43,12 @@
static media_status_t translate_error(status_t err) {
if (err == OK) {
return AMEDIA_OK;
+ } else if (err == ERROR_END_OF_STREAM) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ } else if (err == ERROR_IO) {
+ return AMEDIA_ERROR_IO;
}
+
ALOGE("sf error code: %d", err);
return AMEDIA_ERROR_UNKNOWN;
}
@@ -411,5 +416,64 @@
return -1;
}
+EXPORT
+media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt) {
+ if (fmt == NULL) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ sp<MetaData> sampleMeta;
+ status_t err = ex->mImpl->getSampleMeta(&sampleMeta);
+ if (err != OK) {
+ return translate_error(err);
+ }
+
+ sp<AMessage> meta;
+ AMediaFormat_getFormat(fmt, &meta);
+ meta->clear();
+
+ int32_t layerId;
+ if (sampleMeta->findInt32(kKeyTemporalLayerId, &layerId)) {
+ meta->setInt32(AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId);
+ }
+
+ size_t trackIndex;
+ err = ex->mImpl->getSampleTrackIndex(&trackIndex);
+ if (err == OK) {
+ meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, trackIndex);
+ sp<AMessage> trackFormat;
+ AString mime;
+ err = ex->mImpl->getTrackFormat(trackIndex, &trackFormat);
+ if (err == OK
+ && trackFormat != NULL
+ && trackFormat->findString(AMEDIAFORMAT_KEY_MIME, &mime)) {
+ meta->setString(AMEDIAFORMAT_KEY_MIME, mime);
+ }
+ }
+
+ int64_t durationUs;
+ if (sampleMeta->findInt64(kKeyDuration, &durationUs)) {
+ meta->setInt64(AMEDIAFORMAT_KEY_DURATION, durationUs);
+ }
+
+ uint32_t dataType; // unused
+ const void *seiData;
+ size_t seiLength;
+ if (sampleMeta->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
+ sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
+ meta->setBuffer(AMEDIAFORMAT_KEY_SEI, sei);
+ }
+
+ const void *mpegUserDataPointer;
+ size_t mpegUserDataLength;
+ if (sampleMeta->findData(
+ kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
+ sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
+ meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
+ }
+
+ return AMEDIA_OK;
+}
+
} // extern "C"
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index b86876b..abcaa0a 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -309,6 +309,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
+EXPORT const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA = "mpeg-user-data";
EXPORT const char* AMEDIAFORMAT_KEY_OPERATING_RATE = "operating-rate";
EXPORT const char* AMEDIAFORMAT_KEY_PCM_ENCODING = "pcm-encoding";
EXPORT const char* AMEDIAFORMAT_KEY_PRIORITY = "priority";
@@ -317,10 +318,13 @@
EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
EXPORT const char* AMEDIAFORMAT_KEY_ROTATION = "rotation-degrees";
EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height";
EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
+EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema";
EXPORT const char* AMEDIAFORMAT_KEY_TRACK_ID = "track-id";
+EXPORT const char* AMEDIAFORMAT_KEY_TRACK_INDEX = "track-index";
EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
diff --git a/media/ndk/include/media/NdkMediaError.h b/media/ndk/include/media/NdkMediaError.h
index e48fcbe..13aacc9 100644
--- a/media/ndk/include/media/NdkMediaError.h
+++ b/media/ndk/include/media/NdkMediaError.h
@@ -53,6 +53,8 @@
AMEDIA_ERROR_INVALID_OBJECT = AMEDIA_ERROR_BASE - 3,
AMEDIA_ERROR_INVALID_PARAMETER = AMEDIA_ERROR_BASE - 4,
AMEDIA_ERROR_INVALID_OPERATION = AMEDIA_ERROR_BASE - 5,
+ AMEDIA_ERROR_END_OF_STREAM = AMEDIA_ERROR_BASE - 6,
+ AMEDIA_ERROR_IO = AMEDIA_ERROR_BASE - 7,
AMEDIA_DRM_ERROR_BASE = -20000,
AMEDIA_DRM_NOT_PROVISIONED = AMEDIA_DRM_ERROR_BASE - 1,
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index 3c9e23d..f7b9cfd 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -203,6 +203,17 @@
*/
int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *);
+/**
+ * Read the current sample's metadata format into |fmt|. Examples of sample metadata are
+ * SEI (supplemental enhancement information) and MPEG user data, both of which can embed
+ * closed-caption data.
+ *
+ * Returns AMEDIA_OK on success or AMEDIA_ERROR_* to indicate failure reason.
+ * Existing key-value pairs in |fmt| would be removed if this API returns AMEDIA_OK.
+ * The contents of |fmt| is undefined if this API returns AMEDIA_ERROR_*.
+ */
+media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt);
+
#endif /* __ANDROID_API__ >= 28 */
#endif /* __ANDROID_API__ >= 21 */
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index b6489c7..464c127 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -123,6 +123,7 @@
extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE;
extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH;
extern const char* AMEDIAFORMAT_KEY_MIME;
+extern const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA;
extern const char* AMEDIAFORMAT_KEY_OPERATING_RATE;
extern const char* AMEDIAFORMAT_KEY_PCM_ENCODING;
extern const char* AMEDIAFORMAT_KEY_PRIORITY;
@@ -131,10 +132,13 @@
extern const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER;
extern const char* AMEDIAFORMAT_KEY_ROTATION;
extern const char* AMEDIAFORMAT_KEY_SAMPLE_RATE;
+extern const char* AMEDIAFORMAT_KEY_SEI;
extern const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT;
extern const char* AMEDIAFORMAT_KEY_STRIDE;
+extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID;
extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING;
extern const char* AMEDIAFORMAT_KEY_TRACK_ID;
+extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX;
extern const char* AMEDIAFORMAT_KEY_WIDTH;
#endif /* __ANDROID_API__ >= 21 */
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 17c1a0d..d8c41d2 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -159,6 +159,7 @@
AMediaExtractor_getPsshInfo;
AMediaExtractor_getSampleCryptoInfo;
AMediaExtractor_getSampleFlags;
+ AMediaExtractor_getSampleFormat; # introduced=28
AMediaExtractor_getSampleSize; # introduced=28
AMediaExtractor_getSampleTime;
AMediaExtractor_getSampleTrackIndex;