Merge "Bug fix : enqueue one next subtitle for each subtitle." into jb-dev
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 6a5b45f..3656fe3 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -610,6 +610,53 @@
     fprintf(stderr, "       -d(ump) filename (raw stream data to a file)\n");
 }
 
+static void dumpCodecProfiles(const sp<IOMX>& omx, bool queryDecoders) {
+    const char *kMimeTypes[] = {
+        MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
+        MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC,
+        MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB,
+        MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
+        MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
+        MEDIA_MIMETYPE_VIDEO_VPX
+    };
+
+    if (queryDecoders) {
+        printf("decoder profiles:\n");
+    } else {
+        printf("encoder profiles:\n");
+    }
+
+    for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) {
+        printf("type '%s':\n", kMimeTypes[k]);
+
+        Vector<CodecCapabilities> results;
+        // will retrieve hardware and software codecs
+        CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
+                             queryDecoders,
+                             &results), (status_t)OK);
+
+        for (size_t i = 0; i < results.size(); ++i) {
+            printf("  decoder '%s' supports ",
+                       results[i].mComponentName.string());
+
+            if (results[i].mProfileLevels.size() == 0) {
+                    printf("NOTHING.\n");
+                    continue;
+            }
+
+            for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) {
+                const CodecProfileLevel &profileLevel =
+                     results[i].mProfileLevels[j];
+
+                printf("%s%ld/%ld", j > 0 ? ", " : "",
+                    profileLevel.mProfile, profileLevel.mLevel);
+            }
+
+            printf("\n");
+        }
+    }
+}
+
 int main(int argc, char **argv) {
     android::ProcessState::self()->startThreadPool();
 
@@ -830,46 +877,8 @@
 
         sp<IOMX> omx = service->getOMX();
         CHECK(omx.get() != NULL);
-
-        const char *kMimeTypes[] = {
-            MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
-            MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC,
-            MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB,
-            MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
-            MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
-            MEDIA_MIMETYPE_VIDEO_VPX
-        };
-
-        for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]);
-             ++k) {
-            printf("type '%s':\n", kMimeTypes[k]);
-
-            Vector<CodecCapabilities> results;
-            // will retrieve hardware and software codecs
-            CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
-                                 true, // queryDecoders
-                                 &results), (status_t)OK);
-
-            for (size_t i = 0; i < results.size(); ++i) {
-                printf("  decoder '%s' supports ",
-                       results[i].mComponentName.string());
-
-                if (results[i].mProfileLevels.size() == 0) {
-                    printf("NOTHING.\n");
-                    continue;
-                }
-
-                for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) {
-                    const CodecProfileLevel &profileLevel =
-                        results[i].mProfileLevels[j];
-
-                    printf("%s%ld/%ld", j > 0 ? ", " : "",
-                           profileLevel.mProfile, profileLevel.mLevel);
-                }
-
-                printf("\n");
-            }
-        }
+        dumpCodecProfiles(omx, true /* queryDecoders */);
+        dumpCodecProfiles(omx, false /* queryDecoders */);
     }
 
     if (listComponents) {
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 30db642..3891809 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -77,6 +77,7 @@
     AUDIO_ENCODER_AAC = 3,
     AUDIO_ENCODER_AAC_PLUS = 4,
     AUDIO_ENCODER_EAAC_PLUS = 5,
+    AUDIO_ENCODER_AAC_ELD = 6,
 
     AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type
 };
diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h
index 49397ee..df1b053 100644
--- a/include/media/stagefright/AACWriter.h
+++ b/include/media/stagefright/AACWriter.h
@@ -59,6 +59,7 @@
     int64_t mEstimatedDurationUs;
     int32_t mChannelCount;
     int32_t mSampleRate;
+    int32_t mAACProfile;
     int32_t mFrameDurationUs;
 
     static void *ThreadWrapper(void *);
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 7d7af63..b8d925e 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -182,7 +182,7 @@
     status_t setupAACCodec(
             bool encoder,
             int32_t numChannels, int32_t sampleRate, int32_t bitRate,
-            bool isADTS);
+            int32_t aacProfile, bool isADTS);
 
     status_t selectAudioPortFormat(
             OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 8a87d83..3c25a14 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -49,6 +49,7 @@
     kKeyFrameRate         = 'frmR',  // int32_t (video frame rate fps)
     kKeyBitRate           = 'brte',  // int32_t (bps)
     kKeyESDS              = 'esds',  // raw data
+    kKeyAACProfile        = 'aacp',  // int32_t
     kKeyAVCC              = 'avcc',  // raw data
     kKeyD263              = 'd263',  // raw data
     kKeyVorbisInfo        = 'vinf',  // raw data
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index e197134..c9c709c 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -18,6 +18,7 @@
 #define NU_MEDIA_EXTRACTOR_H_
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaSource.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -53,8 +54,12 @@
     status_t getTrackFormat(size_t index, sp<AMessage> *format) const;
 
     status_t selectTrack(size_t index);
+    status_t unselectTrack(size_t index);
 
-    status_t seekTo(int64_t timeUs);
+    status_t seekTo(
+            int64_t timeUs,
+            MediaSource::ReadOptions::SeekMode mode =
+                MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
 
     status_t advance();
     status_t readSampleData(const sp<ABuffer> &buffer);
@@ -93,7 +98,11 @@
     int64_t mTotalBitrate;  // in bits/sec
     int64_t mDurationUs;
 
-    ssize_t fetchTrackSamples(int64_t seekTimeUs = -1ll);
+    ssize_t fetchTrackSamples(
+            int64_t seekTimeUs = -1ll,
+            MediaSource::ReadOptions::SeekMode mode =
+                MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
+
     void releaseTrackSamples();
 
     bool getTotalBitrate(int64_t *bitRate) const;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 055da5d..887ce5d 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -243,7 +243,7 @@
 
     status_t setAACFormat(
             int32_t numChannels, int32_t sampleRate, int32_t bitRate,
-            bool isADTS);
+            int32_t aacProfile, bool isADTS);
 
     void setG711Format(int32_t numChannels);
 
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index b1be8b1..cd419bd 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -54,6 +54,12 @@
         audio_stream_type_t streamType,
         uint32_t sampleRate)
 {
+    // FIXME merge with similar code in createTrack_l(), except we're missing
+    //       some information here that is available in createTrack_l():
+    //          audio_io_handle_t output
+    //          audio_format_t format
+    //          audio_channel_mask_t channelMask
+    //          audio_output_flags_t flags
     int afSampleRate;
     if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
         return NO_INIT;
@@ -201,11 +207,11 @@
         streamType = AUDIO_STREAM_MUSIC;
     }
 
-    int afSampleRate;
-    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
     if (sampleRate == 0) {
+        int afSampleRate;
+        if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+            return NO_INIT;
+        }
         sampleRate = afSampleRate;
     }
 
@@ -223,6 +229,12 @@
         return BAD_VALUE;
     }
 
+    // AudioFlinger does not currently support 8-bit data in shared memory
+    if (format == AUDIO_FORMAT_PCM_8_BIT && sharedBuffer != 0) {
+        ALOGE("8-bit data in shared memory is not supported");
+        return BAD_VALUE;
+    }
+
     // force direct flag if format is not linear PCM
     if (!audio_is_linear_pcm(format)) {
         flags = (audio_output_flags_t)
@@ -744,14 +756,6 @@
         return NO_INIT;
     }
 
-    int afSampleRate;
-    if (AudioSystem::getSamplingRate(output, streamType, &afSampleRate) != NO_ERROR) {
-        return NO_INIT;
-    }
-    int afFrameCount;
-    if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) {
-        return NO_INIT;
-    }
     uint32_t afLatency;
     if (AudioSystem::getLatency(output, streamType, &afLatency) != NO_ERROR) {
         return NO_INIT;
@@ -768,14 +772,57 @@
         ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client");
         flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST);
     }
-    ALOGV("createTrack_l() output %d afFrameCount %d afLatency %d", output, afFrameCount, afLatency);
+    ALOGV("createTrack_l() output %d afLatency %d", output, afLatency);
 
     mNotificationFramesAct = mNotificationFramesReq;
+
     if (!audio_is_linear_pcm(format)) {
+
         if (sharedBuffer != 0) {
+            // Same comment as below about ignoring frameCount parameter for set()
             frameCount = sharedBuffer->size();
+        } else if (frameCount == 0) {
+            int afFrameCount;
+            if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) {
+                return NO_INIT;
+            }
+            frameCount = afFrameCount;
         }
-    } else {
+
+    } else if (sharedBuffer != 0) {
+
+        // Ensure that buffer alignment matches channelCount
+        int channelCount = popcount(channelMask);
+        // 8-bit data in shared memory is not currently supported by AudioFlinger
+        size_t alignment = /* format == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2;
+        if (channelCount > 1) {
+            // More than 2 channels does not require stronger alignment than stereo
+            alignment <<= 1;
+        }
+        if (((uint32_t)sharedBuffer->pointer() & (alignment - 1)) != 0) {
+            ALOGE("Invalid buffer alignment: address %p, channelCount %d",
+                    sharedBuffer->pointer(), channelCount);
+            return BAD_VALUE;
+        }
+
+        // When initializing a shared buffer AudioTrack via constructors,
+        // there's no frameCount parameter.
+        // But when initializing a shared buffer AudioTrack via set(),
+        // there _is_ a frameCount parameter.  We silently ignore it.
+        frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t);
+
+    } else if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
+
+        // FIXME move these calculations and associated checks to server
+        int afSampleRate;
+        if (AudioSystem::getSamplingRate(output, streamType, &afSampleRate) != NO_ERROR) {
+            return NO_INIT;
+        }
+        int afFrameCount;
+        if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) {
+            return NO_INIT;
+        }
+
         // Ensure that buffer depth covers at least audio hardware latency
         uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
         if (minBufCount < 2) minBufCount = 2;
@@ -784,38 +831,27 @@
         ALOGV("minFrameCount: %d, afFrameCount=%d, minBufCount=%d, sampleRate=%d, afSampleRate=%d"
                 ", afLatency=%d",
                 minFrameCount, afFrameCount, minBufCount, sampleRate, afSampleRate, afLatency);
-#define MIN_FRAME_COUNT_FAST 128    // FIXME hard-coded
-        if ((flags & AUDIO_OUTPUT_FLAG_FAST) && (minFrameCount > MIN_FRAME_COUNT_FAST)) {
-            minFrameCount = MIN_FRAME_COUNT_FAST;
+
+        if (frameCount == 0) {
+            frameCount = minFrameCount;
+        }
+        if (mNotificationFramesAct == 0) {
+            mNotificationFramesAct = frameCount/2;
+        }
+        // Make sure that application is notified with sufficient margin
+        // before underrun
+        if (mNotificationFramesAct > (uint32_t)frameCount/2) {
+            mNotificationFramesAct = frameCount/2;
+        }
+        if (frameCount < minFrameCount) {
+            // not ALOGW because it happens all the time when playing key clicks over A2DP
+            ALOGV("Minimum buffer size corrected from %d to %d",
+                     frameCount, minFrameCount);
+            frameCount = minFrameCount;
         }
 
-        if (sharedBuffer == 0) {
-            if (frameCount == 0) {
-                frameCount = minFrameCount;
-            }
-            if (mNotificationFramesAct == 0) {
-                mNotificationFramesAct = frameCount/2;
-            }
-            // Make sure that application is notified with sufficient margin
-            // before underrun
-            if (mNotificationFramesAct > (uint32_t)frameCount/2) {
-                mNotificationFramesAct = frameCount/2;
-            }
-            if (frameCount < minFrameCount) {
-                // not ALOGW because it happens all the time when playing key clicks over A2DP
-                ALOGV("Minimum buffer size corrected from %d to %d",
-                         frameCount, minFrameCount);
-                frameCount = minFrameCount;
-            }
-        } else {
-            // Ensure that buffer alignment matches channelCount
-            int channelCount = popcount(channelMask);
-            if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) {
-                ALOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount);
-                return BAD_VALUE;
-            }
-            frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t);
-        }
+    } else {
+        // For fast tracks, the frame count calculations and checks are done by server
     }
 
     IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
@@ -864,6 +900,9 @@
         } else {
             ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", mCblk->frameCount);
         }
+        if (sharedBuffer == 0) {
+            mNotificationFramesAct = mCblk->frameCount/2;
+        }
     }
     if (sharedBuffer == 0) {
         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
@@ -879,6 +918,7 @@
     mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
     mCblk->waitTimeMs = 0;
     mRemainingFrames = mNotificationFramesAct;
+    // FIXME don't believe this lie
     mLatency = afLatency + (1000*mCblk->frameCount) / sampleRate;
     return NO_ERROR;
 }
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index c224f06..c08f033 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -41,9 +41,10 @@
 };
 
 const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = {
-    {"amrnb", AUDIO_ENCODER_AMR_NB},
-    {"amrwb", AUDIO_ENCODER_AMR_WB},
-    {"aac",   AUDIO_ENCODER_AAC},
+    {"amrnb",  AUDIO_ENCODER_AMR_NB},
+    {"amrwb",  AUDIO_ENCODER_AMR_WB},
+    {"aac",    AUDIO_ENCODER_AAC},
+    {"aaceld", AUDIO_ENCODER_AAC_ELD},
 };
 
 const MediaProfiles::NameToTagMap MediaProfiles::sFileFormatMap[] = {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 2c5644f..b676cc7 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -24,6 +24,7 @@
 #include <binder/IServiceManager.h>
 
 #include <media/IMediaPlayerService.h>
+#include <media/openmax/OMX_Audio.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/AMRWriter.h>
@@ -817,6 +818,11 @@
             break;
         case AUDIO_ENCODER_AAC:
             mime = MEDIA_MIMETYPE_AUDIO_AAC;
+            encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectLC);
+            break;
+        case AUDIO_ENCODER_AAC_ELD:
+            mime = MEDIA_MIMETYPE_AUDIO_AAC;
+            encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectELD);
             break;
         default:
             ALOGE("Unknown audio encoder: %d", mAudioEncoder);
@@ -852,7 +858,8 @@
     // Add support for OUTPUT_FORMAT_AAC_ADIF
     CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS);
 
-    CHECK_EQ(mAudioEncoder, AUDIO_ENCODER_AAC);
+    CHECK(mAudioEncoder == AUDIO_ENCODER_AAC ||
+          mAudioEncoder == AUDIO_ENCODER_AAC_ELD);
     CHECK(mAudioSource != AUDIO_SOURCE_CNT);
 
     mWriter = new AACWriter(mOutputFd);
@@ -1435,6 +1442,7 @@
         case AUDIO_ENCODER_AMR_NB:
         case AUDIO_ENCODER_AMR_WB:
         case AUDIO_ENCODER_AAC:
+        case AUDIO_ENCODER_AAC_ELD:
             break;
 
         default:
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index 9cdb463..21c5428 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "AACWriter"
 #include <utils/Log.h>
 
+#include <media/openmax/OMX_Audio.h>
 #include <media/stagefright/AACWriter.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -38,7 +39,8 @@
       mPaused(false),
       mResumed(false),
       mChannelCount(-1),
-      mSampleRate(-1) {
+      mSampleRate(-1),
+      mAACProfile(OMX_AUDIO_AACObjectLC) {
 
     ALOGV("AACWriter Constructor");
 
@@ -96,6 +98,7 @@
     CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC));
     CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount));
     CHECK(meta->findInt32(kKeySampleRate, &mSampleRate));
+    CHECK(meta->findInt32(kKeyAACProfile, &mAACProfile));
     CHECK(mChannelCount >= 1 && mChannelCount <= 2);
 
     mSource = source;
@@ -254,7 +257,7 @@
     data |= kProtectionAbsense;
     write(mFd, &data, 1);
 
-    const uint8_t kProfileCode = 1;  // AAC-LC
+    const uint8_t kProfileCode = mAACProfile - 1;
     uint8_t kSampleFreqIndex;
     CHECK(getSampleRateTableIndex(mSampleRate, &kSampleFreqIndex));
     const uint8_t kPrivateStream = 0;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 5ac34c9..c303146 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -852,13 +852,16 @@
                 || !msg->findInt32("sample-rate", &sampleRate)) {
             err = INVALID_OPERATION;
         } else {
-            int32_t isADTS;
+            int32_t isADTS, aacProfile;
             if (!msg->findInt32("is-adts", &isADTS)) {
                 isADTS = 0;
             }
+            if (!msg->findInt32("aac-profile", &aacProfile)) {
+                aacProfile = OMX_AUDIO_AACObjectNull;
+            }
 
             err = setupAACCodec(
-                    encoder, numChannels, sampleRate, bitRate, isADTS != 0);
+                    encoder, numChannels, sampleRate, bitRate, aacProfile, isADTS != 0);
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
         err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
@@ -960,8 +963,8 @@
 }
 
 status_t ACodec::setupAACCodec(
-        bool encoder,
-        int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
+        bool encoder, int32_t numChannels, int32_t sampleRate,
+        int32_t bitRate, int32_t aacProfile, bool isADTS) {
     if (encoder && isADTS) {
         return -EINVAL;
     }
@@ -1026,7 +1029,7 @@
         profile.nFrameLength = 0;
         profile.nAACtools = OMX_AUDIO_AACToolAll;
         profile.nAACERtools = OMX_AUDIO_AACERNone;
-        profile.eAACProfile = OMX_AUDIO_AACObjectLC;
+        profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile;
         profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
 
         err = mOMX->setParameter(
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index b15cb67..1387e74 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1345,7 +1345,7 @@
 }
 
 void AwesomePlayer::addTextSource(size_t trackIndex, const sp<MediaSource>& source) {
-    Mutex::Autolock autoLock(mTimedTextLock);
+    Mutex::Autolock autoLock(mLock);
     CHECK(source != NULL);
 
     if (mTextDriver == NULL) {
@@ -1395,7 +1395,6 @@
     if (mAudioSource != NULL) {
         Mutex::Autolock autoLock(mStatsLock);
         TrackStat *stat = &mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
-
         const char *component;
         if (!mAudioSource->getFormat()
                 ->findCString(kKeyDecoderComponent, &component)) {
@@ -2268,13 +2267,13 @@
 }
 
 status_t AwesomePlayer::getTrackInfo(Parcel *reply) const {
-    Mutex::Autolock autoLock(mTimedTextLock);
-    if (mTextDriver == NULL) {
-        return INVALID_OPERATION;
+    Mutex::Autolock autoLock(mLock);
+    size_t trackCount = mExtractor->countTracks();
+    if (mTextDriver != NULL) {
+        trackCount += mTextDriver->countExternalTracks();
     }
 
-    reply->writeInt32(mTextDriver->countExternalTracks() +
-                mExtractor->countTracks());
+    reply->writeInt32(trackCount);
     for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
         sp<MetaData> meta = mExtractor->getTrackMetaData(i);
 
@@ -2296,28 +2295,31 @@
         }
 
         const char *lang;
-        if (meta->findCString(kKeyMediaLanguage, &lang)) {
-            reply->writeString16(String16(lang));
-        } else {
-            reply->writeString16(String16(""));
+        if (!meta->findCString(kKeyMediaLanguage, &lang)) {
+            lang = "und";
         }
+        reply->writeString16(String16(lang));
     }
 
-    mTextDriver->getExternalTrackInfo(reply);
+    if (mTextDriver != NULL) {
+        mTextDriver->getExternalTrackInfo(reply);
+    }
     return OK;
 }
 
 // FIXME:
 // At present, only timed text track is able to be selected or unselected.
 status_t AwesomePlayer::selectTrack(size_t trackIndex, bool select) {
-    Mutex::Autolock autoLock(mTimedTextLock);
-    if (mTextDriver == NULL) {
-        return INVALID_OPERATION;
+    ALOGV("selectTrack: trackIndex = %d and select=%d", trackIndex, select);
+    Mutex::Autolock autoLock(mLock);
+    size_t trackCount = mExtractor->countTracks();
+    if (mTextDriver != NULL) {
+        trackCount += mTextDriver->countExternalTracks();
     }
 
-    if (trackIndex >= mExtractor->countTracks()
-                + mTextDriver->countExternalTracks()) {
-        return BAD_VALUE;
+    if (trackIndex >= trackCount) {
+        ALOGE("Track index (%d) is out of range [0, %d)", trackIndex, trackCount);
+        return ERROR_OUT_OF_RANGE;
     }
 
     if (trackIndex < mExtractor->countTracks()) {
@@ -2331,6 +2333,11 @@
         }
     }
 
+    // Timed text track handling
+    if (mTextDriver == NULL) {
+        return INVALID_OPERATION;
+    }
+
     status_t err = OK;
     if (select) {
         err = mTextDriver->selectTrack(trackIndex);
@@ -2371,7 +2378,7 @@
         }
         case INVOKE_ID_ADD_EXTERNAL_SOURCE:
         {
-            Mutex::Autolock autoLock(mTimedTextLock);
+            Mutex::Autolock autoLock(mLock);
             if (mTextDriver == NULL) {
                 mTextDriver = new TimedTextDriver(mListener);
             }
@@ -2383,7 +2390,7 @@
         }
         case INVOKE_ID_ADD_EXTERNAL_SOURCE_FD:
         {
-            Mutex::Autolock autoLock(mTimedTextLock);
+            Mutex::Autolock autoLock(mLock);
             if (mTextDriver == NULL) {
                 mTextDriver = new TimedTextDriver(mListener);
             }
@@ -2398,12 +2405,12 @@
         case INVOKE_ID_SELECT_TRACK:
         {
             int trackIndex = request.readInt32();
-            return selectTrack(trackIndex, true);
+            return selectTrack(trackIndex, true /* select */);
         }
         case INVOKE_ID_UNSELECT_TRACK:
         {
             int trackIndex = request.readInt32();
-            return selectTrack(trackIndex, false);
+            return selectTrack(trackIndex, false /* select */);
         }
         default:
         {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 9385b8a..a572541 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
@@ -1824,26 +1825,23 @@
         return ERROR_MALFORMED;
     }
 
-    uint32_t objectType = csd[0] >> 3;
+    ABitReader br(csd, csd_size);
+    uint32_t objectType = br.getBits(5);
 
-    if (objectType == 31) {
-        return ERROR_UNSUPPORTED;
+    if (objectType == 31) {  // AAC-ELD => additional 6 bits
+        objectType = 32 + br.getBits(6);
     }
 
-    uint32_t freqIndex = (csd[0] & 7) << 1 | (csd[1] >> 7);
+    uint32_t freqIndex = br.getBits(4);
+
     int32_t sampleRate = 0;
     int32_t numChannels = 0;
     if (freqIndex == 15) {
         if (csd_size < 5) {
             return ERROR_MALFORMED;
         }
-
-        sampleRate = (csd[1] & 0x7f) << 17
-                        | csd[2] << 9
-                        | csd[3] << 1
-                        | (csd[4] >> 7);
-
-        numChannels = (csd[4] >> 3) & 15;
+        sampleRate = br.getBits(24);
+        numChannels = br.getBits(4);
     } else {
         static uint32_t kSamplingRate[] = {
             96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
@@ -1855,7 +1853,7 @@
         }
 
         sampleRate = kSamplingRate[freqIndex];
-        numChannels = (csd[1] >> 3) & 15;
+        numChannels = br.getBits(4);
     }
 
     if (numChannels == 0) {
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 7cfb8ea..5f3f63f 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -384,6 +384,47 @@
     return OK;
 }
 
+status_t NuMediaExtractor::unselectTrack(size_t index) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mImpl == NULL) {
+        return -EINVAL;
+    }
+
+    if (index >= mImpl->countTracks()) {
+        return -ERANGE;
+    }
+
+    size_t i;
+    for (i = 0; i < mSelectedTracks.size(); ++i) {
+        TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+        if (info->mTrackIndex == index) {
+            break;
+        }
+    }
+
+    if (i == mSelectedTracks.size()) {
+        // Not selected.
+        return OK;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+    if (info->mSample != NULL) {
+        info->mSample->release();
+        info->mSample = NULL;
+
+        info->mSampleTimeUs = -1ll;
+    }
+
+    CHECK_EQ((status_t)OK, info->mSource->stop());
+
+    mSelectedTracks.removeAt(i);
+
+    return OK;
+}
+
 void NuMediaExtractor::releaseTrackSamples() {
     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
         TrackInfo *info = &mSelectedTracks.editItemAt(i);
@@ -397,7 +438,8 @@
     }
 }
 
-ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
+ssize_t NuMediaExtractor::fetchTrackSamples(
+        int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
     TrackInfo *minInfo = NULL;
     ssize_t minIndex = -1;
 
@@ -419,7 +461,7 @@
         if (info->mSample == NULL) {
             MediaSource::ReadOptions options;
             if (seekTimeUs >= 0ll) {
-                options.setSeekTo(seekTimeUs);
+                options.setSeekTo(seekTimeUs, mode);
             }
             status_t err = info->mSource->read(&info->mSample, &options);
 
@@ -445,10 +487,11 @@
     return minIndex;
 }
 
-status_t NuMediaExtractor::seekTo(int64_t timeUs) {
+status_t NuMediaExtractor::seekTo(
+        int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
     Mutex::Autolock autoLock(mLock);
 
-    ssize_t minIndex = fetchTrackSamples(timeUs);
+    ssize_t minIndex = fetchTrackSamples(timeUs, mode);
 
     if (minIndex < 0) {
         return ERROR_END_OF_STREAM;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 1d6f927..245d941 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -511,16 +511,20 @@
     } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) {
         setAMRFormat(true /* isWAMR */, bitRate);
     } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) {
-        int32_t numChannels, sampleRate;
+        int32_t numChannels, sampleRate, aacProfile;
         CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
         CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
 
+        if (!meta->findInt32(kKeyAACProfile, &aacProfile)) {
+            aacProfile = OMX_AUDIO_AACObjectNull;
+        }
+
         int32_t isADTS;
         if (!meta->findInt32(kKeyIsADTS, &isADTS)) {
             isADTS = false;
         }
 
-        status_t err = setAACFormat(numChannels, sampleRate, bitRate, isADTS);
+        status_t err = setAACFormat(numChannels, sampleRate, bitRate, aacProfile, isADTS);
         if (err != OK) {
             CODEC_LOGE("setAACFormat() failed (err = %d)", err);
             return err;
@@ -3395,7 +3399,7 @@
 }
 
 status_t OMXCodec::setAACFormat(
-        int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
+        int32_t numChannels, int32_t sampleRate, int32_t bitRate, int32_t aacProfile, bool isADTS) {
     if (numChannels > 2) {
         ALOGW("Number of channels: (%d) \n", numChannels);
     }
@@ -3453,7 +3457,7 @@
         profile.nFrameLength = 0;
         profile.nAACtools = OMX_AUDIO_AACToolAll;
         profile.nAACERtools = OMX_AUDIO_AACERNone;
-        profile.eAACProfile = OMX_AUDIO_AACObjectLC;
+        profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile;
         profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
         err = mOMX->setParameter(mNode, OMX_IndexParamAudioAac,
                 &profile, sizeof(profile));
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index aa65a0b..547a554 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -108,6 +108,7 @@
             status = OK;
         }
     }
+    mIsFirst = true;
     return status;
 }
 
@@ -298,7 +299,16 @@
             inInfo->mOwnedByUs = false;
             notifyEmptyBufferDone(inHeader);
 
-            outHeader->nFilledLen = 0;
+            // flush out the decoder's delayed data by calling DecodeFrame one more time, with
+            // the AACDEC_FLUSH flag set
+            INT_PCM *outBuffer =
+                    reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
+            decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
+                                                outBuffer,
+                                                outHeader->nAllocLen,
+                                                AACDEC_FLUSH);
+            outHeader->nFilledLen =
+                    mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
             outHeader->nFlags = OMX_BUFFERFLAG_EOS;
 
             outQueue.erase(outQueue.begin());
@@ -375,7 +385,7 @@
          * Thus, we could not say for sure whether a stream is
          * AAC+/eAAC+ until the first data frame is decoded.
          */
-        if (decoderErr == AAC_DEC_OK && mInputBufferCount <= 2) {
+        if (mInputBufferCount <= 2) {
             if (mStreamInfo->sampleRate != prevSampleRate) {
                 // We're going to want to revisit this input buffer, but
                 // may have already advanced the offset. Undo that if
@@ -412,6 +422,12 @@
             // We'll only output data if we successfully decoded it or
             // we've previously decoded valid data, in the latter case
             // (decode failed) we'll output a silent frame.
+            if (mIsFirst) {
+                mIsFirst = false;
+                // the first decoded frame should be discarded to account for decoder delay
+                numOutBytes = 0;
+            }
+
             outHeader->nFilledLen = numOutBytes;
             outHeader->nFlags = 0;
 
@@ -447,6 +463,7 @@
         // Make sure that the next buffer output does not still
         // depend on fragments from the last one decoded.
         mInputDiscontinuity = true;
+        mIsFirst = true;
     }
 }
 
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index d93685c..e5a1e3e 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -50,6 +50,7 @@
     HANDLE_AACDECODER mAACDecoder;
     CStreamInfo *mStreamInfo;
     bool mIsADTS;
+    bool mIsFirst;
     size_t mInputBufferCount;
     bool mSignalledError;
     bool mInputDiscontinuity;
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index 0ad3f6c..98e702e 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 include frameworks/av/media/libstagefright/codecs/common/Config.mk
 
-
+AAC_LIBRARY = fraunhofer
 
 LOCAL_SRC_FILES := basic_op/basicop2.c basic_op/oper_32b.c
 
@@ -90,24 +90,57 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-        SoftAACEncoder.cpp
+ifeq ($(AAC_LIBRARY), fraunhofer)
 
-LOCAL_C_INCLUDES := \
-	frameworks/av/media/libstagefright/include \
-	frameworks/av/media/libstagefright/codecs/common/include \
-	frameworks/native/include/media/openmax
+  include $(CLEAR_VARS)
 
-LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+  LOCAL_SRC_FILES := \
+          SoftAACEncoder2.cpp
 
-LOCAL_STATIC_LIBRARIES := \
-        libstagefright_aacenc
+  LOCAL_C_INCLUDES := \
+          frameworks/av/media/libstagefright/include \
+          frameworks/native/include/media/openmax \
+          external/aac/libAACenc/include \
+          external/aac/libFDK/include \
+          external/aac/libMpegTPEnc/include \
+          external/aac/libSBRenc/include \
+          external/aac/libSYS/include
 
-LOCAL_SHARED_LIBRARIES := \
-        libstagefright_omx libstagefright_foundation libutils \
-        libstagefright_enc_common
+  LOCAL_CFLAGS :=
 
-LOCAL_MODULE := libstagefright_soft_aacenc
-LOCAL_MODULE_TAGS := optional
+  LOCAL_STATIC_LIBRARIES := \
+          libAACenc libMpegTPEnc libSBRenc libFDK libSYS
 
-include $(BUILD_SHARED_LIBRARY)
+  LOCAL_SHARED_LIBRARIES := \
+          libstagefright_omx libstagefright_foundation libutils
+
+  LOCAL_MODULE := libstagefright_soft_aacenc
+  LOCAL_MODULE_TAGS := optional
+
+  include $(BUILD_SHARED_LIBRARY)
+
+else # visualon
+
+  LOCAL_SRC_FILES := \
+          SoftAACEncoder.cpp
+
+  LOCAL_C_INCLUDES := \
+          frameworks/av/media/libstagefright/include \
+          frameworks/av/media/libstagefright/codecs/common/include \
+          frameworks/native/include/media/openmax
+
+  LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+  LOCAL_STATIC_LIBRARIES := \
+          libstagefright_aacenc
+
+  LOCAL_SHARED_LIBRARIES := \
+          libstagefright_omx libstagefright_foundation libutils \
+          libstagefright_enc_common
+
+  LOCAL_MODULE := libstagefright_soft_aacenc
+  LOCAL_MODULE_TAGS := optional
+
+  include $(BUILD_SHARED_LIBRARY)
+
+endif # $(AAC_LIBRARY)
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
new file mode 100644
index 0000000..4947fb2
--- /dev/null
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2012 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 "SoftAACEncoder2"
+#include <utils/Log.h>
+
+#include "SoftAACEncoder2.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftAACEncoder2::SoftAACEncoder2(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mAACEncoder(NULL),
+      mNumChannels(1),
+      mSampleRate(44100),
+      mBitRate(0),
+      mAACProfile(OMX_AUDIO_AACObjectLC),
+      mSentCodecSpecificData(false),
+      mInputSize(0),
+      mInputFrame(NULL),
+      mInputTimeUs(-1ll),
+      mSawInputEOS(false),
+      mSignalledError(false) {
+    initPorts();
+    CHECK_EQ(initEncoder(), (status_t)OK);
+    setAudioParams();
+}
+
+SoftAACEncoder2::~SoftAACEncoder2() {
+    aacEncClose(&mAACEncoder);
+
+    delete[] mInputFrame;
+    mInputFrame = NULL;
+}
+
+void SoftAACEncoder2::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t) * 2;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/aac");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+    addPort(def);
+}
+
+status_t SoftAACEncoder2::initEncoder() {
+    if (AACENC_OK != aacEncOpen(&mAACEncoder, 0, 0)) {
+        ALOGE("Failed to init AAC encoder");
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+OMX_ERRORTYPE SoftAACEncoder2::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamAudioPortFormat:
+        {
+            OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex > 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            formatParams->eEncoding =
+                (formatParams->nPortIndex == 0)
+                    ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAAC;
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioAac:
+        {
+            OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+                (OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+            if (aacParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            aacParams->nBitRate = mBitRate;
+            aacParams->nAudioBandWidth = 0;
+            aacParams->nAACtools = 0;
+            aacParams->nAACERtools = 0;
+            aacParams->eAACProfile = (OMX_AUDIO_AACPROFILETYPE) mAACProfile;
+            aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+            aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
+
+            aacParams->nChannels = mNumChannels;
+            aacParams->nSampleRate = mSampleRate;
+            aacParams->nFrameLength = 0;
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            pcmParams->eNumData = OMX_NumericalDataSigned;
+            pcmParams->eEndian = OMX_EndianBig;
+            pcmParams->bInterleaved = OMX_TRUE;
+            pcmParams->nBitPerSample = 16;
+            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+            pcmParams->nChannels = mNumChannels;
+            pcmParams->nSamplingRate = mSampleRate;
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftAACEncoder2::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (strncmp((const char *)roleParams->cRole,
+                        "audio_encoder.aac",
+                        OMX_MAX_STRINGNAME_SIZE - 1)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPortFormat:
+        {
+            const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+                (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex > 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            if ((formatParams->nPortIndex == 0
+                        && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
+                || (formatParams->nPortIndex == 1
+                        && formatParams->eEncoding != OMX_AUDIO_CodingAAC)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioAac:
+        {
+            OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+                (OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+            if (aacParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            mBitRate = aacParams->nBitRate;
+            mNumChannels = aacParams->nChannels;
+            mSampleRate = aacParams->nSampleRate;
+
+            if (aacParams->eAACProfile != OMX_AUDIO_AACObjectNull) {
+                mAACProfile = aacParams->eAACProfile;
+            }
+
+            if (setAudioParams() != OK) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            mNumChannels = pcmParams->nChannels;
+            mSampleRate = pcmParams->nSamplingRate;
+
+            if (setAudioParams() != OK) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+CHANNEL_MODE getChannelMode(OMX_U32 nChannels) {
+    CHANNEL_MODE chMode = MODE_INVALID;
+    switch (nChannels) {
+        case 1: chMode = MODE_1; break;
+        case 2: chMode = MODE_2; break;
+        case 3: chMode = MODE_1_2; break;
+        case 4: chMode = MODE_1_2_1; break;
+        case 5: chMode = MODE_1_2_2; break;
+        case 6: chMode = MODE_1_2_2_1; break;
+        default: chMode = MODE_INVALID;
+    }
+    return chMode;
+}
+
+status_t SoftAACEncoder2::setAudioParams() {
+    // We call this whenever sample rate, number of channels or bitrate change
+    // in reponse to setParameter calls.
+
+    ALOGV("setAudioParams: %lu Hz, %lu channels, %lu bps",
+         mSampleRate, mNumChannels, mBitRate);
+
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT,
+            mAACProfile == OMX_AUDIO_AACObjectELD ? AOT_ER_AAC_ELD : AOT_AAC_LC)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE,
+            getChannelMode(mNumChannels))) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void SoftAACEncoder2::onQueueFilled(OMX_U32 portIndex) {
+    if (mSignalledError) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    if (!mSentCodecSpecificData) {
+        // The very first thing we want to output is the codec specific
+        // data. It does not require any input data but we will need an
+        // output buffer to store it in.
+
+        if (outQueue.empty()) {
+            return;
+        }
+
+        if (AACENC_OK != aacEncEncode(mAACEncoder, NULL, NULL, NULL, NULL)) {
+            ALOGE("Failed to initialize AAC encoder");
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+
+        AACENC_InfoStruct encInfo;
+        if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) {
+            ALOGE("Failed to get AAC encoder info");
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+        outHeader->nFilledLen = encInfo.confSize;
+        outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG;
+
+        uint8_t *out = outHeader->pBuffer + outHeader->nOffset;
+        memcpy(out, encInfo.confBuf, encInfo.confSize);
+
+        outQueue.erase(outQueue.begin());
+        outInfo->mOwnedByUs = false;
+        notifyFillBufferDone(outHeader);
+
+        mSentCodecSpecificData = true;
+    }
+
+    size_t numBytesPerInputFrame =
+        mNumChannels * kNumSamplesPerFrame * sizeof(int16_t);
+
+    // BUGBUG: Fraunhofer's decoder chokes on large chunks of AAC-ELD
+    if (mAACProfile == OMX_AUDIO_AACObjectELD && numBytesPerInputFrame > 512) {
+        numBytesPerInputFrame = 512;
+    }
+
+    for (;;) {
+        // We do the following until we run out of buffers.
+
+        while (mInputSize < numBytesPerInputFrame) {
+            // As long as there's still input data to be read we
+            // will drain "kNumSamplesPerFrame * mNumChannels" samples
+            // into the "mInputFrame" buffer and then encode those
+            // as a unit into an output buffer.
+
+            if (mSawInputEOS || inQueue.empty()) {
+                return;
+            }
+
+            BufferInfo *inInfo = *inQueue.begin();
+            OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+            const void *inData = inHeader->pBuffer + inHeader->nOffset;
+
+            size_t copy = numBytesPerInputFrame - mInputSize;
+            if (copy > inHeader->nFilledLen) {
+                copy = inHeader->nFilledLen;
+            }
+
+            if (mInputFrame == NULL) {
+                mInputFrame = new int16_t[kNumSamplesPerFrame * mNumChannels];
+            }
+
+            if (mInputSize == 0) {
+                mInputTimeUs = inHeader->nTimeStamp;
+            }
+
+            memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy);
+            mInputSize += copy;
+
+            inHeader->nOffset += copy;
+            inHeader->nFilledLen -= copy;
+
+            // "Time" on the input buffer has in effect advanced by the
+            // number of audio frames we just advanced nOffset by.
+            inHeader->nTimeStamp +=
+                (copy * 1000000ll / mSampleRate)
+                    / (mNumChannels * sizeof(int16_t));
+
+            if (inHeader->nFilledLen == 0) {
+                if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+                    mSawInputEOS = true;
+
+                    // Pad any remaining data with zeroes.
+                    memset((uint8_t *)mInputFrame + mInputSize,
+                           0,
+                           numBytesPerInputFrame - mInputSize);
+
+                    mInputSize = numBytesPerInputFrame;
+                }
+
+                inQueue.erase(inQueue.begin());
+                inInfo->mOwnedByUs = false;
+                notifyEmptyBufferDone(inHeader);
+
+                inData = NULL;
+                inHeader = NULL;
+                inInfo = NULL;
+            }
+        }
+
+        // At this  point we have all the input data necessary to encode
+        // a single frame, all we need is an output buffer to store the result
+        // in.
+
+        if (outQueue.empty()) {
+            return;
+        }
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        uint8_t *outPtr = (uint8_t *)outHeader->pBuffer + outHeader->nOffset;
+        size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
+
+        AACENC_InArgs inargs;
+        AACENC_OutArgs outargs;
+        memset(&inargs, 0, sizeof(inargs));
+        memset(&outargs, 0, sizeof(outargs));
+        inargs.numInSamples = numBytesPerInputFrame / sizeof(int16_t);
+
+        void* inBuffer[]        = { (unsigned char *)mInputFrame };
+        INT   inBufferIds[]     = { IN_AUDIO_DATA };
+        INT   inBufferSize[]    = { numBytesPerInputFrame };
+        INT   inBufferElSize[]  = { sizeof(int16_t) };
+
+        AACENC_BufDesc inBufDesc;
+        inBufDesc.numBufs           = sizeof(inBuffer) / sizeof(void*);
+        inBufDesc.bufs              = (void**)&inBuffer;
+        inBufDesc.bufferIdentifiers = inBufferIds;
+        inBufDesc.bufSizes          = inBufferSize;
+        inBufDesc.bufElSizes        = inBufferElSize;
+
+        void* outBuffer[]       = { outPtr };
+        INT   outBufferIds[]    = { OUT_BITSTREAM_DATA };
+        INT   outBufferSize[]   = { 0 };
+        INT   outBufferElSize[] = { sizeof(UCHAR) };
+
+        AACENC_BufDesc outBufDesc;
+        outBufDesc.numBufs           = sizeof(outBuffer) / sizeof(void*);
+        outBufDesc.bufs              = (void**)&outBuffer;
+        outBufDesc.bufferIdentifiers = outBufferIds;
+        outBufDesc.bufSizes          = outBufferSize;
+        outBufDesc.bufElSizes        = outBufferElSize;
+
+        // Encode the mInputFrame, which is treated as a modulo buffer
+        AACENC_ERROR encoderErr = AACENC_OK;
+        size_t nOutputBytes = 0;
+        do {
+            memset(&outargs, 0, sizeof(outargs));
+
+            outBuffer[0] = outPtr;
+            outBufferSize[0] = outAvailable - nOutputBytes;
+
+            encoderErr = aacEncEncode(mAACEncoder,
+                                      &inBufDesc,
+                                      &outBufDesc,
+                                      &inargs,
+                                      &outargs);
+
+            if (encoderErr == AACENC_OK) {
+                outPtr += outargs.numOutBytes;
+                nOutputBytes += outargs.numOutBytes;
+
+                if (outargs.numInSamples > 0) {
+                    int numRemainingSamples = inargs.numInSamples - outargs.numInSamples;
+                    if (numRemainingSamples > 0) {
+                        memmove(mInputFrame,
+                                &mInputFrame[outargs.numInSamples],
+                                sizeof(int16_t) * numRemainingSamples);
+                    }
+                    inargs.numInSamples -= outargs.numInSamples;
+                }
+            }
+        } while (encoderErr == AACENC_OK && inargs.numInSamples > 0);
+
+        outHeader->nFilledLen = nOutputBytes;
+
+        outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
+
+        if (mSawInputEOS) {
+            // We also tag this output buffer with EOS if it corresponds
+            // to the final input buffer.
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+        }
+
+        outHeader->nTimeStamp = mInputTimeUs;
+
+#if 0
+        ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
+              nOutputBytes, mInputTimeUs, outHeader->nFlags);
+
+        hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
+#endif
+
+        outQueue.erase(outQueue.begin());
+        outInfo->mOwnedByUs = false;
+        notifyFillBufferDone(outHeader);
+
+        outHeader = NULL;
+        outInfo = NULL;
+
+        mInputSize = 0;
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftAACEncoder2(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h
new file mode 100644
index 0000000..2603f4f
--- /dev/null
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 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 SOFT_AAC_ENCODER_2_H_
+
+#define SOFT_AAC_ENCODER_2_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+#include "aacenc_lib.h"
+
+namespace android {
+
+struct SoftAACEncoder2 : public SimpleSoftOMXComponent {
+    SoftAACEncoder2(
+            const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftAACEncoder2();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+
+private:
+    enum {
+        kNumBuffers             = 4,
+        kNumSamplesPerFrame     = 1024
+    };
+
+    HANDLE_AACENCODER mAACEncoder;
+
+    OMX_U32 mNumChannels;
+    OMX_U32 mSampleRate;
+    OMX_U32 mBitRate;
+    OMX_U32 mAACProfile;
+
+    bool mSentCodecSpecificData;
+    size_t mInputSize;
+    int16_t *mInputFrame;
+    int64_t mInputTimeUs;
+
+    bool mSawInputEOS;
+
+    bool mSignalledError;
+
+    void initPorts();
+    status_t initEncoder();
+
+    status_t setAudioParams();
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftAACEncoder2);
+};
+
+}  // namespace android
+
+#endif  // SOFT_AAC_ENCODER_2_H_
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
index 7cbb38f..e6aa563 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
@@ -3,6 +3,7 @@
 
 LOCAL_SRC_FILES := \
     M4vH263Encoder.cpp \
+    SoftMPEG4Encoder.cpp \
     src/bitstream_io.cpp \
     src/combined_encode.cpp \
     src/datapart_encode.cpp \
@@ -35,3 +36,39 @@
     $(TOP)/frameworks/native/include/media/openmax
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftMPEG4Encoder.cpp
+
+LOCAL_C_INCLUDES := \
+        frameworks/av/media/libstagefright/include \
+        frameworks/native/include/media/openmax \
+        $(LOCAL_PATH)/src \
+        $(LOCAL_PATH)/include \
+        $(LOCAL_PATH)/../common/include \
+        $(LOCAL_PATH)/../common
+
+LOCAL_CFLAGS := \
+    -DBX_RC \
+    -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF=
+
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_m4vh263enc
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright \
+        libstagefright_enc_common \
+        libstagefright_foundation \
+        libstagefright_omx \
+        libutils \
+
+
+LOCAL_MODULE := libstagefright_soft_mpeg4enc
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
new file mode 100644
index 0000000..a5a2332
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2012 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 "SoftMPEG4Encoder"
+#include <utils/Log.h>
+
+#include "mp4enc_api.h"
+#include "OMX_Video.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include "SoftMPEG4Encoder.h"
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+inline static void ConvertYUV420SemiPlanarToYUV420Planar(
+        uint8_t *inyuv, uint8_t* outyuv,
+        int32_t width, int32_t height) {
+
+    int32_t outYsize = width * height;
+    uint32_t *outy =  (uint32_t *) outyuv;
+    uint16_t *outcb = (uint16_t *) (outyuv + outYsize);
+    uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2));
+
+    /* Y copying */
+    memcpy(outy, inyuv, outYsize);
+
+    /* U & V copying */
+    uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
+    for (int32_t i = height >> 1; i > 0; --i) {
+        for (int32_t j = width >> 2; j > 0; --j) {
+            uint32_t temp = *inyuv_4++;
+            uint32_t tempU = temp & 0xFF;
+            tempU = tempU | ((temp >> 8) & 0xFF00);
+
+            uint32_t tempV = (temp >> 8) & 0xFF;
+            tempV = tempV | ((temp >> 16) & 0xFF00);
+
+            // Flip U and V
+            *outcb++ = tempV;
+            *outcr++ = tempU;
+        }
+    }
+}
+
+SoftMPEG4Encoder::SoftMPEG4Encoder(
+            const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mEncodeMode(COMBINE_MODE_WITH_ERR_RES),
+      mVideoWidth(176),
+      mVideoHeight(144),
+      mVideoFrameRate(30),
+      mVideoBitRate(192000),
+      mVideoColorFormat(OMX_COLOR_FormatYUV420Planar),
+      mIDRFrameRefreshIntervalInSec(1),
+      mNumInputFrames(-1),
+      mStarted(false),
+      mSawInputEOS(false),
+      mSignalledError(false),
+      mHandle(new tagvideoEncControls),
+      mEncParams(new tagvideoEncOptions),
+      mInputFrameData(NULL) {
+
+   if (!strcmp(name, "OMX.google.h263.encoder")) {
+        mEncodeMode = H263_MODE;
+    } else {
+        CHECK(!strcmp(name, "OMX.google.mpeg4.encoder"));
+    }
+
+    initPorts();
+    ALOGI("Construct SoftMPEG4Encoder");
+}
+
+SoftMPEG4Encoder::~SoftMPEG4Encoder() {
+    ALOGV("Destruct SoftMPEG4Encoder");
+    releaseEncoder();
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    CHECK(outQueue.empty());
+    CHECK(inQueue.empty());
+}
+
+OMX_ERRORTYPE SoftMPEG4Encoder::initEncParams() {
+    CHECK(mHandle != NULL);
+    memset(mHandle, 0, sizeof(tagvideoEncControls));
+
+    CHECK(mEncParams != NULL);
+    memset(mEncParams, 0, sizeof(tagvideoEncOptions));
+    if (!PVGetDefaultEncOption(mEncParams, 0)) {
+        ALOGE("Failed to get default encoding parameters");
+        return OMX_ErrorUndefined;
+    }
+    mEncParams->encMode = mEncodeMode;
+    mEncParams->encWidth[0] = mVideoWidth;
+    mEncParams->encHeight[0] = mVideoHeight;
+    mEncParams->encFrameRate[0] = mVideoFrameRate;
+    mEncParams->rcType = VBR_1;
+    mEncParams->vbvDelay = 5.0f;
+
+    // FIXME:
+    // Add more profile and level support for MPEG4 encoder
+    mEncParams->profile_level = CORE_PROFILE_LEVEL2;
+    mEncParams->packetSize = 32;
+    mEncParams->rvlcEnable = PV_OFF;
+    mEncParams->numLayers = 1;
+    mEncParams->timeIncRes = 1000;
+    mEncParams->tickPerSrc = mEncParams->timeIncRes / mVideoFrameRate;
+
+    mEncParams->bitRate[0] = mVideoBitRate;
+    mEncParams->iQuant[0] = 15;
+    mEncParams->pQuant[0] = 12;
+    mEncParams->quantType[0] = 0;
+    mEncParams->noFrameSkipped = PV_OFF;
+
+    if (mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
+        // Color conversion is needed.
+        CHECK(mInputFrameData == NULL);
+        mInputFrameData =
+            (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
+        CHECK(mInputFrameData != NULL);
+    }
+
+    // PV's MPEG4 encoder requires the video dimension of multiple
+    if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) {
+        ALOGE("Video frame size %dx%d must be a multiple of 16",
+            mVideoWidth, mVideoHeight);
+        return OMX_ErrorBadParameter;
+    }
+
+    // Set IDR frame refresh interval
+    if (mIDRFrameRefreshIntervalInSec < 0) {
+        mEncParams->intraPeriod = -1;
+    } else if (mIDRFrameRefreshIntervalInSec == 0) {
+        mEncParams->intraPeriod = 1;  // All I frames
+    } else {
+        mEncParams->intraPeriod =
+            (mIDRFrameRefreshIntervalInSec * mVideoFrameRate);
+    }
+
+    mEncParams->numIntraMB = 0;
+    mEncParams->sceneDetect = PV_ON;
+    mEncParams->searchRange = 16;
+    mEncParams->mv8x8Enable = PV_OFF;
+    mEncParams->gobHeaderInterval = 0;
+    mEncParams->useACPred = PV_ON;
+    mEncParams->intraDCVlcTh = 0;
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftMPEG4Encoder::initEncoder() {
+    CHECK(!mStarted);
+
+    OMX_ERRORTYPE errType = OMX_ErrorNone;
+    if (OMX_ErrorNone != (errType = initEncParams())) {
+        ALOGE("Failed to initialized encoder params");
+        mSignalledError = true;
+        notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+        return errType;
+    }
+
+    if (!PVInitVideoEncoder(mHandle, mEncParams)) {
+        ALOGE("Failed to initialize the encoder");
+        mSignalledError = true;
+        notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+        return OMX_ErrorUndefined;
+    }
+
+    mNumInputFrames = -1;  // 1st buffer for codec specific data
+    mStarted = true;
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftMPEG4Encoder::releaseEncoder() {
+    if (!mStarted) {
+        return OMX_ErrorNone;
+    }
+
+    PVCleanUpVideoEncoder(mHandle);
+
+    delete mInputFrameData;
+    mInputFrameData = NULL;
+
+    delete mEncParams;
+    mEncParams = NULL;
+
+    delete mHandle;
+    mHandle = NULL;
+
+    mStarted = false;
+
+    return OMX_ErrorNone;
+}
+
+void SoftMPEG4Encoder::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    const size_t kInputBufferSize = (mVideoWidth * mVideoHeight * 3) >> 1;
+
+    // 256 * 1024 is a magic number for PV's encoder, not sure why
+    const size_t kOutputBufferSize =
+        (kInputBufferSize > 256 * 1024)
+            ? kInputBufferSize: 256 * 1024;
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = kInputBufferSize;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.video.cMIMEType = const_cast<char *>("video/raw");
+
+    def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+    def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+    def.format.video.xFramerate = (mVideoFrameRate << 16);  // Q16 format
+    def.format.video.nBitrate = mVideoBitRate;
+    def.format.video.nFrameWidth = mVideoWidth;
+    def.format.video.nFrameHeight = mVideoHeight;
+    def.format.video.nStride = mVideoWidth;
+    def.format.video.nSliceHeight = mVideoHeight;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = kOutputBufferSize;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.video.cMIMEType =
+        (mEncodeMode == COMBINE_MODE_WITH_ERR_RES)
+            ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4)
+            : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263);
+
+    def.format.video.eCompressionFormat =
+        (mEncodeMode == COMBINE_MODE_WITH_ERR_RES)
+            ? OMX_VIDEO_CodingMPEG4
+            : OMX_VIDEO_CodingH263;
+
+    def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+    def.format.video.xFramerate = (0 << 16);  // Q16 format
+    def.format.video.nBitrate = mVideoBitRate;
+    def.format.video.nFrameWidth = mVideoWidth;
+    def.format.video.nFrameHeight = mVideoHeight;
+    def.format.video.nStride = mVideoWidth;
+    def.format.video.nSliceHeight = mVideoHeight;
+
+    addPort(def);
+}
+
+OMX_ERRORTYPE SoftMPEG4Encoder::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamVideoErrorCorrection:
+        {
+            return OMX_ErrorNotImplemented;
+        }
+
+        case OMX_IndexParamVideoBitrate:
+        {
+            OMX_VIDEO_PARAM_BITRATETYPE *bitRate =
+                (OMX_VIDEO_PARAM_BITRATETYPE *) params;
+
+            if (bitRate->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            bitRate->eControlRate = OMX_Video_ControlRateVariable;
+            bitRate->nTargetBitrate = mVideoBitRate;
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoPortFormat:
+        {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex > 1) {
+                return OMX_ErrorNoMore;
+            }
+
+            if (formatParams->nPortIndex == 0) {
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+                if (formatParams->nIndex == 0) {
+                    formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+                } else {
+                    formatParams->eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+                }
+            } else {
+                formatParams->eCompressionFormat =
+                    (mEncodeMode == COMBINE_MODE_WITH_ERR_RES)
+                        ? OMX_VIDEO_CodingMPEG4
+                        : OMX_VIDEO_CodingH263;
+
+                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoH263:
+        {
+            OMX_VIDEO_PARAM_H263TYPE *h263type =
+                (OMX_VIDEO_PARAM_H263TYPE *)params;
+
+            if (h263type->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            h263type->nAllowedPictureTypes =
+                (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP);
+            h263type->eProfile = OMX_VIDEO_H263ProfileBaseline;
+            h263type->eLevel = OMX_VIDEO_H263Level45;
+            h263type->bPLUSPTYPEAllowed = OMX_FALSE;
+            h263type->bForceRoundingTypeToZero = OMX_FALSE;
+            h263type->nPictureHeaderRepetition = 0;
+            h263type->nGOBHeaderInterval = 0;
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoMpeg4:
+        {
+            OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type =
+                (OMX_VIDEO_PARAM_MPEG4TYPE *)params;
+
+            if (mpeg4type->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            mpeg4type->eProfile = OMX_VIDEO_MPEG4ProfileCore;
+            mpeg4type->eLevel = OMX_VIDEO_MPEG4Level2;
+            mpeg4type->nAllowedPictureTypes =
+                (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP);
+            mpeg4type->nBFrames = 0;
+            mpeg4type->nIDCVLCThreshold = 0;
+            mpeg4type->bACPred = OMX_TRUE;
+            mpeg4type->nMaxPacketSize = 256;
+            mpeg4type->nTimeIncRes = 1000;
+            mpeg4type->nHeaderExtension = 0;
+            mpeg4type->bReversibleVLC = OMX_FALSE;
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoProfileLevelQuerySupported:
+        {
+            OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileLevel =
+                (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)params;
+
+            if (profileLevel->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (profileLevel->nProfileIndex > 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            if (mEncodeMode == H263_MODE) {
+                profileLevel->eProfile = OMX_VIDEO_H263ProfileBaseline;
+                profileLevel->eLevel = OMX_VIDEO_H263Level45;
+            } else {
+                profileLevel->eProfile = OMX_VIDEO_MPEG4ProfileCore;
+                profileLevel->eLevel = OMX_VIDEO_MPEG4Level2;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamVideoErrorCorrection:
+        {
+            return OMX_ErrorNotImplemented;
+        }
+
+        case OMX_IndexParamVideoBitrate:
+        {
+            OMX_VIDEO_PARAM_BITRATETYPE *bitRate =
+                (OMX_VIDEO_PARAM_BITRATETYPE *) params;
+
+            if (bitRate->nPortIndex != 1 ||
+                bitRate->eControlRate != OMX_Video_ControlRateVariable) {
+                return OMX_ErrorUndefined;
+            }
+
+            mVideoBitRate = bitRate->nTargetBitrate;
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamPortDefinition:
+        {
+            OMX_PARAM_PORTDEFINITIONTYPE *def =
+                (OMX_PARAM_PORTDEFINITIONTYPE *)params;
+            if (def->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (def->nPortIndex == 0) {
+                if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused ||
+                    (def->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar &&
+                     def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar)) {
+                    return OMX_ErrorUndefined;
+                }
+            } else {
+                if ((mEncodeMode == COMBINE_MODE_WITH_ERR_RES &&
+                        def->format.video.eCompressionFormat != OMX_VIDEO_CodingMPEG4) ||
+                    (mEncodeMode == H263_MODE &&
+                        def->format.video.eCompressionFormat != OMX_VIDEO_CodingH263) ||
+                    (def->format.video.eColorFormat != OMX_COLOR_FormatUnused)) {
+                    return OMX_ErrorUndefined;
+                }
+            }
+
+            OMX_ERRORTYPE err = SimpleSoftOMXComponent::internalSetParameter(index, params);
+            if (OMX_ErrorNone != err) {
+                return err;
+            }
+
+            if (def->nPortIndex == 0) {
+                mVideoWidth = def->format.video.nFrameWidth;
+                mVideoHeight = def->format.video.nFrameHeight;
+                mVideoFrameRate = def->format.video.xFramerate >> 16;
+                mVideoColorFormat = def->format.video.eColorFormat;
+            } else {
+                mVideoBitRate = def->format.video.nBitrate;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (strncmp((const char *)roleParams->cRole,
+                        (mEncodeMode == H263_MODE)
+                            ? "video_encoder.h263": "video_encoder.mpeg4",
+                        OMX_MAX_STRINGNAME_SIZE - 1)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoPortFormat:
+        {
+            const OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex > 1) {
+                return OMX_ErrorNoMore;
+            }
+
+            if (formatParams->nPortIndex == 0) {
+                if (formatParams->eCompressionFormat != OMX_VIDEO_CodingUnused ||
+                    ((formatParams->nIndex == 0 &&
+                      formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) ||
+                    (formatParams->nIndex == 1 &&
+                     formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar))) {
+                    return OMX_ErrorUndefined;
+                }
+                mVideoColorFormat = formatParams->eColorFormat;
+            } else {
+                if ((mEncodeMode == H263_MODE &&
+                        formatParams->eCompressionFormat != OMX_VIDEO_CodingH263) ||
+                    (mEncodeMode == COMBINE_MODE_WITH_ERR_RES &&
+                        formatParams->eCompressionFormat != OMX_VIDEO_CodingMPEG4) ||
+                    formatParams->eColorFormat != OMX_COLOR_FormatUnused) {
+                    return OMX_ErrorUndefined;
+                }
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoH263:
+        {
+            OMX_VIDEO_PARAM_H263TYPE *h263type =
+                (OMX_VIDEO_PARAM_H263TYPE *)params;
+
+            if (h263type->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (h263type->eProfile != OMX_VIDEO_H263ProfileBaseline ||
+                h263type->eLevel != OMX_VIDEO_H263Level45 ||
+                (h263type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) ||
+                h263type->bPLUSPTYPEAllowed != OMX_FALSE ||
+                h263type->bForceRoundingTypeToZero != OMX_FALSE ||
+                h263type->nPictureHeaderRepetition != 0 ||
+                h263type->nGOBHeaderInterval != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoMpeg4:
+        {
+            OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type =
+                (OMX_VIDEO_PARAM_MPEG4TYPE *)params;
+
+            if (mpeg4type->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (mpeg4type->eProfile != OMX_VIDEO_MPEG4ProfileCore ||
+                mpeg4type->eLevel != OMX_VIDEO_MPEG4Level2 ||
+                (mpeg4type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) ||
+                mpeg4type->nBFrames != 0 ||
+                mpeg4type->nIDCVLCThreshold != 0 ||
+                mpeg4type->bACPred != OMX_TRUE ||
+                mpeg4type->nMaxPacketSize != 256 ||
+                mpeg4type->nTimeIncRes != 1000 ||
+                mpeg4type->nHeaderExtension != 0 ||
+                mpeg4type->bReversibleVLC != OMX_FALSE) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+void SoftMPEG4Encoder::onQueueFilled(OMX_U32 portIndex) {
+    if (mSignalledError || mSawInputEOS) {
+        return;
+    }
+
+    if (!mStarted) {
+        if (OMX_ErrorNone != initEncoder()) {
+            return;
+        }
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    while (!mSawInputEOS && !inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        outHeader->nTimeStamp = 0;
+        outHeader->nFlags = 0;
+        outHeader->nOffset = 0;
+        outHeader->nFilledLen = 0;
+        outHeader->nOffset = 0;
+
+        uint8_t *outPtr = (uint8_t *) outHeader->pBuffer;
+        int32_t dataLength = outHeader->nAllocLen;
+
+        if (mNumInputFrames < 0) {
+            if (!PVGetVolHeader(mHandle, outPtr, &dataLength, 0)) {
+                ALOGE("Failed to get VOL header");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                return;
+            }
+            ALOGV("Output VOL header: %d bytes", dataLength);
+            ++mNumInputFrames;
+            outHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+            outHeader->nFilledLen = dataLength;
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+            return;
+        }
+
+        // Save the input buffer info so that it can be
+        // passed to an output buffer
+        InputBufferInfo info;
+        info.mTimeUs = inHeader->nTimeStamp;
+        info.mFlags = inHeader->nFlags;
+        mInputBufferInfoVec.push(info);
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            mSawInputEOS = true;
+        }
+
+        if (inHeader->nFilledLen > 0) {
+            const void *inData = inHeader->pBuffer + inHeader->nOffset;
+            uint8_t *inputData = (uint8_t *) inData;
+            if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
+                ConvertYUV420SemiPlanarToYUV420Planar(
+                    inputData, mInputFrameData, mVideoWidth, mVideoHeight);
+                inputData = mInputFrameData;
+            }
+            CHECK(inputData != NULL);
+
+            VideoEncFrameIO vin, vout;
+            memset(&vin, 0, sizeof(vin));
+            memset(&vout, 0, sizeof(vout));
+            vin.height = ((mVideoHeight  + 15) >> 4) << 4;
+            vin.pitch = ((mVideoWidth + 15) >> 4) << 4;
+            vin.timestamp = (inHeader->nTimeStamp + 500) / 1000;  // in ms
+            vin.yChan = inputData;
+            vin.uChan = vin.yChan + vin.height * vin.pitch;
+            vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2);
+
+            unsigned long modTimeMs = 0;
+            int32_t nLayer = 0;
+            MP4HintTrack hintTrack;
+            if (!PVEncodeVideoFrame(mHandle, &vin, &vout,
+                    &modTimeMs, outPtr, &dataLength, &nLayer) ||
+                !PVGetHintTrack(mHandle, &hintTrack)) {
+                ALOGE("Failed to encode frame or get hink track at frame %lld",
+                    mNumInputFrames);
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+            }
+            CHECK(NULL == PVGetOverrunBuffer(mHandle));
+            if (hintTrack.CodeType == 0) {  // I-frame serves as sync frame
+                outHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME;
+            }
+
+            ++mNumInputFrames;
+        } else {
+            dataLength = 0;
+        }
+
+        inQueue.erase(inQueue.begin());
+        inInfo->mOwnedByUs = false;
+        notifyEmptyBufferDone(inHeader);
+
+        outQueue.erase(outQueue.begin());
+        CHECK(!mInputBufferInfoVec.empty());
+        InputBufferInfo *inputBufInfo = mInputBufferInfoVec.begin();
+        mInputBufferInfoVec.erase(mInputBufferInfoVec.begin());
+        outHeader->nTimeStamp = inputBufInfo->mTimeUs;
+        outHeader->nFlags |= (inputBufInfo->mFlags | OMX_BUFFERFLAG_ENDOFFRAME);
+        outHeader->nFilledLen = dataLength;
+        outInfo->mOwnedByUs = false;
+        notifyFillBufferDone(outHeader);
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftMPEG4Encoder(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
new file mode 100644
index 0000000..3e90d54
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 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 SOFT_MPEG4_ENCODER_H_
+#define SOFT_MPEG4_ENCODER_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/foundation/ABase.h>
+#include "SimpleSoftOMXComponent.h"
+#include "mp4enc_api.h"
+
+
+namespace android {
+
+struct MediaBuffer;
+
+struct SoftMPEG4Encoder : public SimpleSoftOMXComponent {
+    SoftMPEG4Encoder(
+            const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+    // Override SimpleSoftOMXComponent methods
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+
+protected:
+    virtual ~SoftMPEG4Encoder();
+
+private:
+    enum {
+        kNumBuffers = 2,
+    };
+
+    // OMX input buffer's timestamp and flags
+    typedef struct {
+        int64_t mTimeUs;
+        int32_t mFlags;
+    } InputBufferInfo;
+
+    MP4EncodingMode mEncodeMode;
+    int32_t  mVideoWidth;
+    int32_t  mVideoHeight;
+    int32_t  mVideoFrameRate;
+    int32_t  mVideoBitRate;
+    int32_t  mVideoColorFormat;
+    int32_t  mIDRFrameRefreshIntervalInSec;
+
+    int64_t  mNumInputFrames;
+    bool     mStarted;
+    bool     mSawInputEOS;
+    bool     mSignalledError;
+
+    tagvideoEncControls   *mHandle;
+    tagvideoEncOptions    *mEncParams;
+    uint8_t               *mInputFrameData;
+    Vector<InputBufferInfo> mInputBufferInfoVec;
+
+    void initPorts();
+    OMX_ERRORTYPE initEncParams();
+    OMX_ERRORTYPE initEncoder();
+    OMX_ERRORTYPE releaseEncoder();
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4Encoder);
+};
+
+}  // namespace android
+
+#endif  // SOFT_MPEG4_ENCODER_H_
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 9115f91..a2e2e85 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -232,7 +232,6 @@
 
     int64_t mLastVideoTimeUs;
     TimedTextDriver *mTextDriver;
-    mutable Mutex mTimedTextLock;
 
     sp<WVMExtractor> mWVMExtractor;
     sp<MediaExtractor> mExtractor;
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index 9b7bb5a..6e53095 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -45,7 +45,9 @@
     { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
     { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
     { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
+    { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
     { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
+    { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
     { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
     { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
     { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" },
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6eeda9a..f7a2525 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1475,7 +1475,6 @@
         mMasterVolume(audioFlinger->masterVolumeSW_l()),
         mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
         mMixerStatus(MIXER_IDLE),
-        mPrevMixerStatus(MIXER_IDLE),
         standbyDelay(AudioFlinger::mStandbyTimeInNsecs),
         mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
         mFastTrackNewMask(0)
@@ -1623,8 +1622,8 @@
     bool isTimed = (flags & IAudioFlinger::TRACK_TIMED) != 0;
 
     // client expresses a preference for FAST, but we get the final say
-    if ((flags & IAudioFlinger::TRACK_FAST) &&
-          !(
+    if (flags & IAudioFlinger::TRACK_FAST) {
+      if (
             // not timed
             (!isTimed) &&
             // either of these use cases:
@@ -1633,11 +1632,11 @@
               (
                 (sharedBuffer != 0)
               ) ||
-              // use case 2: callback handler and frame count at least as large as HAL
+              // use case 2: callback handler and frame count is default or at least as large as HAL
               (
                 (tid != -1) &&
-                // FIXME supported frame counts should not be hard-coded
-                frameCount >= (int) mFrameCount // FIXME int cast is due to wrong parameter type
+                ((frameCount == 0) ||
+                (frameCount >= (int) mFrameCount)) // FIXME int cast is due to wrong parameter type
               )
             ) &&
             // PCM data
@@ -1655,17 +1654,35 @@
             (mFastTrackAvailMask != 0)
             // FIXME test that MixerThread for this fast track has a capable output HAL
             // FIXME add a permission test also?
-          ) ) {
-        ALOGW("AUDIO_POLICY_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
+        ) {
+        ALOGI("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
+                frameCount, mFrameCount);
+        // if frameCount not specified, then it defaults to fast mixer (HAL) frame count
+        if (frameCount == 0) {
+            frameCount = mFrameCount;
+        }
+      } else {
+        ALOGW("AUDIO_OUTPUT_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
                 "mFrameCount=%d format=%d isLinear=%d channelMask=%d sampleRate=%d mSampleRate=%d "
                 "hasFastMixer=%d tid=%d fastTrackAvailMask=%#x",
                 isTimed, sharedBuffer.get(), frameCount, mFrameCount, format,
                 audio_is_linear_pcm(format),
                 channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
         flags &= ~IAudioFlinger::TRACK_FAST;
-        if (0 < frameCount && frameCount < (int) mNormalFrameCount) {
-            frameCount = mNormalFrameCount;
+        // For compatibility with AudioTrack calculation, buffer depth is forced
+        // to be at least 2 x the normal mixer frame count and cover audio hardware latency.
+        // This is probably too conservative, but legacy application code may depend on it.
+        // If you change this calculation, also review the start threshold which is related.
+        uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
+        uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+        if (minBufCount < 2) {
+            minBufCount = 2;
         }
+        int minFrameCount = mNormalFrameCount * minBufCount;
+        if (frameCount < minFrameCount) {
+            frameCount = minFrameCount;
+        }
+      }
     }
 
     if (mType == DIRECT) {
@@ -2374,7 +2391,7 @@
                     ALOGV("%s waking up", myName.string());
                     acquireWakeLock_l();
 
-                    mPrevMixerStatus = MIXER_IDLE;
+                    mMixerStatus = MIXER_IDLE;
 
                     checkSilentMode_l();
 
@@ -2388,11 +2405,7 @@
                 }
             }
 
-            mixer_state newMixerStatus = prepareTracks_l(&tracksToRemove);
-            // Shift in the new status; this could be a queue if it's
-            // useful to filter the mixer status over several cycles.
-            mPrevMixerStatus = mMixerStatus;
-            mMixerStatus = newMixerStatus;
+            mMixerStatus = prepareTracks_l(&tracksToRemove);
 
             // prevent any changes in effect chain list and in each effect chain
             // during mixing and effect process as the audio buffers could be deleted
@@ -2776,11 +2789,11 @@
         // make sure that we have enough frames to mix one full buffer.
         // enforce this condition only once to enable draining the buffer in case the client
         // app does not call stop() and relies on underrun to stop:
-        // hence the test on (mPrevMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
+        // hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
         // during last round
         uint32_t minFrames = 1;
         if (!track->isStopped() && !track->isPausing() &&
-                (mPrevMixerStatus == MIXER_TRACKS_READY)) {
+                (mMixerStatus == MIXER_TRACKS_READY)) {
             if (t->sampleRate() == (int)mSampleRate) {
                 minFrames = mNormalFrameCount;
             } else {
@@ -2929,7 +2942,7 @@
             // If one track is ready, set the mixer ready if:
             //  - the mixer was not ready during previous round OR
             //  - no other track is not ready
-            if (mPrevMixerStatus != MIXER_TRACKS_READY ||
+            if (mMixerStatus != MIXER_TRACKS_READY ||
                     mixerStatus != MIXER_TRACKS_ENABLED) {
                 mixerStatus = MIXER_TRACKS_READY;
             }
@@ -2961,7 +2974,7 @@
                 // If one track is not ready, mark the mixer also not ready if:
                 //  - the mixer was ready during previous round OR
                 //  - no other track is ready
-                } else if (mPrevMixerStatus == MIXER_TRACKS_READY ||
+                } else if (mMixerStatus == MIXER_TRACKS_READY ||
                                 mixerStatus != MIXER_TRACKS_READY) {
                     mixerStatus = MIXER_TRACKS_ENABLED;
                 }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 23fc74d..2a85115 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -1052,8 +1052,8 @@
         uint32_t                        sleepTime;
 
         // mixer status returned by prepareTracks_l()
-        mixer_state                     mMixerStatus;       // current cycle
-        mixer_state                     mPrevMixerStatus;   // previous cycle
+        mixer_state                     mMixerStatus; // current cycle
+                                                      // previous cycle when in prepareTracks_l()
 
         // FIXME move these declarations into the specific sub-class that needs them
         // MIXER only