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