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, &timestamp, &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;