summaryrefslogtreecommitdiffstats
path: root/media/libstagefright
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright')
-rw-r--r--media/libstagefright/AACExtractor.cpp70
-rw-r--r--media/libstagefright/AACWriter.cpp4
-rw-r--r--media/libstagefright/ACodec.cpp1131
-rw-r--r--media/libstagefright/AMRExtractor.cpp2
-rw-r--r--media/libstagefright/AMRWriter.cpp6
-rw-r--r--media/libstagefright/AVIExtractor.cpp1
-rw-r--r--media/libstagefright/Android.mk84
-rw-r--r--media/libstagefright/AudioPlayer.cpp10
-rw-r--r--media/libstagefright/AudioSource.cpp63
-rw-r--r--media/libstagefright/AwesomePlayer.cpp69
-rwxr-xr-xmedia/libstagefright/CameraSource.cpp21
-rw-r--r--media/libstagefright/CameraSourceTimeLapse.cpp15
-rw-r--r--media/libstagefright/DRMExtractor.cpp10
-rw-r--r--media/libstagefright/DataSource.cpp13
-rw-r--r--media/libstagefright/FileSource.cpp6
-rw-r--r--media/libstagefright/JPEGSource.cpp4
-rw-r--r--media/libstagefright/MPEG2TSWriter.cpp16
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp7
-rwxr-xr-xmedia/libstagefright/MPEG4Writer.cpp209
-rw-r--r--media/libstagefright/MediaBuffer.cpp6
-rw-r--r--media/libstagefright/MediaBufferGroup.cpp2
-rw-r--r--media/libstagefright/MediaCodec.cpp1179
-rw-r--r--media/libstagefright/MediaExtractor.cpp5
-rw-r--r--media/libstagefright/MediaSourceSplitter.cpp2
-rw-r--r--media/libstagefright/MetaData.cpp2
-rw-r--r--media/libstagefright/NuCachedSource2.cpp9
-rw-r--r--media/libstagefright/NuMediaExtractor.cpp433
-rw-r--r--media/libstagefright/OMXClient.cpp299
-rwxr-xr-xmedia/libstagefright/OMXCodec.cpp34
-rw-r--r--media/libstagefright/OggExtractor.cpp2
-rw-r--r--media/libstagefright/SampleIterator.cpp2
-rw-r--r--media/libstagefright/SampleTable.cpp35
-rw-r--r--media/libstagefright/StagefrightMetadataRetriever.cpp6
-rw-r--r--media/libstagefright/SurfaceMediaSource.cpp19
-rw-r--r--media/libstagefright/ThrottledSource.cpp2
-rw-r--r--media/libstagefright/TimedEventQueue.cpp2
-rw-r--r--media/libstagefright/VideoSourceDownSampler.cpp2
-rw-r--r--media/libstagefright/WAVExtractor.cpp6
-rw-r--r--media/libstagefright/WVMExtractor.cpp60
-rw-r--r--media/libstagefright/chromium_http/Android.mk5
-rw-r--r--media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp4
-rw-r--r--media/libstagefright/chromium_http/DataUriSource.cpp68
-rw-r--r--media/libstagefright/codecs/aacdec/SoftAAC.cpp12
-rw-r--r--media/libstagefright/codecs/aacenc/AACEncoder.cpp14
-rw-r--r--media/libstagefright/codecs/aacenc/Android.mk26
-rw-r--r--media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp560
-rw-r--r--media/libstagefright/codecs/aacenc/SoftAACEncoder.h82
-rw-r--r--media/libstagefright/codecs/aacenc/src/asm/ARMV7/PrePostMDCT_v7.s13
-rw-r--r--media/libstagefright/codecs/aacenc/src/asm/ARMV7/R4R8First_v7.s13
-rw-r--r--media/libstagefright/codecs/aacenc/src/asm/ARMV7/Radix4FFT_v7.s7
-rw-r--r--media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp4
-rw-r--r--media/libstagefright/codecs/amrnb/enc/Android.mk27
-rw-r--r--media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp404
-rw-r--r--media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h72
-rw-r--r--media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp10
-rw-r--r--media/libstagefright/codecs/amrwbenc/Android.mk22
-rw-r--r--media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp459
-rw-r--r--media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h76
-rw-r--r--media/libstagefright/codecs/avc/enc/AVCEncoder.cpp8
-rw-r--r--media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp6
-rw-r--r--media/libstagefright/colorconversion/ColorConverter.cpp59
-rw-r--r--media/libstagefright/colorconversion/SoftwareRenderer.cpp3
-rw-r--r--media/libstagefright/foundation/AMessage.cpp39
-rw-r--r--media/libstagefright/httplive/LiveSession.cpp41
-rw-r--r--media/libstagefright/httplive/M3UParser.cpp65
-rw-r--r--media/libstagefright/id3/Android.mk2
-rw-r--r--media/libstagefright/id3/ID3.cpp2
-rw-r--r--media/libstagefright/id3/testid3.cpp4
-rw-r--r--media/libstagefright/include/AACExtractor.h2
-rw-r--r--media/libstagefright/include/AwesomePlayer.h8
-rw-r--r--media/libstagefright/include/ChromiumHTTPDataSource.h2
-rw-r--r--media/libstagefright/include/DataUriSource.h76
-rw-r--r--media/libstagefright/include/LiveSession.h5
-rw-r--r--media/libstagefright/include/M3UParser.h4
-rw-r--r--media/libstagefright/include/NuCachedSource2.h2
-rw-r--r--media/libstagefright/include/OMX.h2
-rw-r--r--media/libstagefright/include/SoftwareRenderer.h2
-rw-r--r--media/libstagefright/include/ThrottledSource.h5
-rw-r--r--media/libstagefright/include/WVMExtractor.h8
-rw-r--r--media/libstagefright/matroska/MatroskaExtractor.cpp115
-rw-r--r--media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp2
-rw-r--r--media/libstagefright/omx/OMX.cpp6
-rw-r--r--media/libstagefright/omx/OMXComponentBase.cpp4
-rw-r--r--media/libstagefright/omx/OMXMaster.cpp2
-rw-r--r--media/libstagefright/omx/OMXNodeInstance.cpp6
-rw-r--r--media/libstagefright/omx/SimpleSoftOMXComponent.cpp35
-rw-r--r--media/libstagefright/omx/SoftOMXPlugin.cpp3
-rw-r--r--media/libstagefright/omx/tests/Android.mk8
-rw-r--r--media/libstagefright/omx/tests/OMXHarness.cpp32
-rw-r--r--media/libstagefright/rtsp/AAMRAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AAVCAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AH263Assembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/ARTPConnection.cpp8
-rw-r--r--media/libstagefright/rtsp/ARTPSession.cpp6
-rw-r--r--media/libstagefright/rtsp/ARTSPConnection.cpp2
-rw-r--r--media/libstagefright/rtsp/ARawAudioAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/MyHandler.h43
-rw-r--r--media/libstagefright/tests/SurfaceMediaSource_test.cpp18
-rw-r--r--media/libstagefright/timedtext/Android.mk8
-rw-r--r--media/libstagefright/timedtext/TimedText3GPPSource.cpp113
-rw-r--r--media/libstagefright/timedtext/TimedText3GPPSource.h55
-rw-r--r--media/libstagefright/timedtext/TimedTextDriver.cpp223
-rw-r--r--media/libstagefright/timedtext/TimedTextDriver.h81
-rw-r--r--media/libstagefright/timedtext/TimedTextParser.h75
-rw-r--r--media/libstagefright/timedtext/TimedTextPlayer.cpp469
-rw-r--r--media/libstagefright/timedtext/TimedTextPlayer.h100
-rw-r--r--media/libstagefright/timedtext/TimedTextSRTSource.cpp (renamed from media/libstagefright/timedtext/TimedTextParser.cpp)246
-rw-r--r--media/libstagefright/timedtext/TimedTextSRTSource.h75
-rw-r--r--media/libstagefright/timedtext/TimedTextSource.cpp62
-rw-r--r--media/libstagefright/timedtext/TimedTextSource.h61
-rw-r--r--media/libstagefright/yuv/YUVCanvas.cpp2
-rw-r--r--media/libstagefright/yuv/YUVImage.cpp2
114 files changed, 6666 insertions, 1279 deletions
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index 52b1200..4d1072f 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -22,9 +22,10 @@
#include "include/avc_utils.h"
#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -131,18 +132,28 @@ static size_t getAdtsFrameLength(const sp<DataSource> &source, off64_t offset, s
return frameSize;
}
-AACExtractor::AACExtractor(const sp<DataSource> &source)
+AACExtractor::AACExtractor(
+ const sp<DataSource> &source, const sp<AMessage> &_meta)
: mDataSource(source),
mInitCheck(NO_INIT),
mFrameDurationUs(0) {
- String8 mimeType;
- float confidence;
- if (!SniffAAC(mDataSource, &mimeType, &confidence, NULL)) {
- return;
+ sp<AMessage> meta = _meta;
+
+ if (meta == NULL) {
+ String8 mimeType;
+ float confidence;
+ sp<AMessage> _meta;
+
+ if (!SniffAAC(mDataSource, &mimeType, &confidence, &meta)) {
+ return;
+ }
}
+ int64_t offset;
+ CHECK(meta->findInt64("offset", &offset));
+
uint8_t profile, sf_index, channel, header[2];
- if (mDataSource->readAt(2, &header, 2) < 2) {
+ if (mDataSource->readAt(offset + 2, &header, 2) < 2) {
return;
}
@@ -156,7 +167,6 @@ AACExtractor::AACExtractor(const sp<DataSource> &source)
mMeta = MakeAACCodecSpecificData(profile, sf_index, channel);
- off64_t offset = 0;
off64_t streamSize, numFrames = 0;
size_t frameSize = 0;
int64_t duration = 0;
@@ -245,7 +255,12 @@ AACSource::~AACSource() {
status_t AACSource::start(MetaData *params) {
CHECK(!mStarted);
- mOffset = 0;
+ if (mOffsetVector.empty()) {
+ mOffset = 0;
+ } else {
+ mOffset = mOffsetVector.itemAt(0);
+ }
+
mCurrentTimeUs = 0;
mGroup = new MediaBufferGroup;
mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
@@ -318,10 +333,39 @@ status_t AACSource::read(
bool SniffAAC(
const sp<DataSource> &source, String8 *mimeType, float *confidence,
- sp<AMessage> *) {
+ sp<AMessage> *meta) {
+ off64_t pos = 0;
+
+ for (;;) {
+ uint8_t id3header[10];
+ if (source->readAt(pos, id3header, sizeof(id3header))
+ < (ssize_t)sizeof(id3header)) {
+ return false;
+ }
+
+ if (memcmp("ID3", id3header, 3)) {
+ break;
+ }
+
+ // Skip the ID3v2 header.
+
+ size_t len =
+ ((id3header[6] & 0x7f) << 21)
+ | ((id3header[7] & 0x7f) << 14)
+ | ((id3header[8] & 0x7f) << 7)
+ | (id3header[9] & 0x7f);
+
+ len += 10;
+
+ pos += len;
+
+ ALOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)",
+ pos, pos);
+ }
+
uint8_t header[2];
- if (source->readAt(0, &header, 2) != 2) {
+ if (source->readAt(pos, &header, 2) != 2) {
return false;
}
@@ -329,6 +373,10 @@ bool SniffAAC(
if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) {
*mimeType = MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
*confidence = 0.2;
+
+ *meta = new AMessage;
+ (*meta)->setInt64("offset", pos);
+
return true;
}
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index 1673ccd..9cdb463 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -60,7 +60,7 @@ AACWriter::AACWriter(int fd)
AACWriter::~AACWriter() {
if (mStarted) {
- stop();
+ reset();
}
if (mFd != -1) {
@@ -152,7 +152,7 @@ status_t AACWriter::pause() {
return OK;
}
-status_t AACWriter::stop() {
+status_t AACWriter::reset() {
if (!mStarted) {
return OK;
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ca44ea3..85bd7ba 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -31,9 +31,6 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
-#include <surfaceflinger/Surface.h>
-#include <gui/SurfaceTextureClient.h>
-
#include <OMX_Component.h>
namespace android {
@@ -171,6 +168,9 @@ protected:
private:
void onSetup(const sp<AMessage> &msg);
+ void onAllocateComponent(const sp<AMessage> &msg);
+ void onConfigureComponent(const sp<AMessage> &msg);
+ void onStart();
DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
};
@@ -265,6 +265,8 @@ protected:
private:
void changeStateIfWeOwnAllBuffers();
+ bool mComponentNowIdle;
+
DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState);
};
@@ -309,7 +311,8 @@ private:
ACodec::ACodec()
: mNode(NULL),
- mSentFormat(false) {
+ mSentFormat(false),
+ mIsEncoder(false) {
mUninitializedState = new UninitializedState(this);
mLoadedToIdleState = new LoadedToIdleState(this);
mIdleToExecutingState = new IdleToExecutingState(this);
@@ -341,6 +344,22 @@ void ACodec::initiateSetup(const sp<AMessage> &msg) {
msg->post();
}
+void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) {
+ msg->setWhat(kWhatAllocateComponent);
+ msg->setTarget(id());
+ msg->post();
+}
+
+void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) {
+ msg->setWhat(kWhatConfigureComponent);
+ msg->setTarget(id());
+ msg->post();
+}
+
+void ACodec::initiateStart() {
+ (new AMessage(kWhatStart, id()))->post();
+}
+
void ACodec::signalFlush() {
ALOGV("[%s] signalFlush", mComponentName.c_str());
(new AMessage(kWhatFlush, id()))->post();
@@ -360,62 +379,75 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
CHECK(mDealer[portIndex] == NULL);
CHECK(mBuffers[portIndex].isEmpty());
+ status_t err;
if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
- return allocateOutputBuffersFromNativeWindow();
- }
+ err = allocateOutputBuffersFromNativeWindow();
+ } else {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = portIndex;
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err == OK) {
+ ALOGV("[%s] Allocating %lu buffers of size %lu on %s port",
+ mComponentName.c_str(),
+ def.nBufferCountActual, def.nBufferSize,
+ portIndex == kPortIndexInput ? "input" : "output");
- if (err != OK) {
- return err;
- }
+ size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+ mDealer[portIndex] = new MemoryDealer(totalSize, "ACodec");
- ALOGV("[%s] Allocating %lu buffers of size %lu on %s port",
- mComponentName.c_str(),
- def.nBufferCountActual, def.nBufferSize,
- portIndex == kPortIndexInput ? "input" : "output");
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
+ CHECK(mem.get() != NULL);
- size_t totalSize = def.nBufferCountActual * def.nBufferSize;
- mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
+ IOMX::buffer_id buffer;
- for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
- sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
- CHECK(mem.get() != NULL);
+ if (!strncasecmp(
+ mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.", 21)) {
+ if (portIndex == kPortIndexInput && i == 0) {
+ // Only log this warning once per allocation round.
- IOMX::buffer_id buffer;
+ ALOGW("OMX.TI.DUCATI1.VIDEO.* require the use of "
+ "OMX_AllocateBuffer instead of the preferred "
+ "OMX_UseBuffer. Vendor must fix this.");
+ }
- if (!strcasecmp(
- mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) {
- if (portIndex == kPortIndexInput && i == 0) {
- // Only log this warning once per allocation round.
+ err = mOMX->allocateBufferWithBackup(
+ mNode, portIndex, mem, &buffer);
+ } else {
+ err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+ }
- ALOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of "
- "OMX_AllocateBuffer instead of the preferred "
- "OMX_UseBuffer. Vendor must fix this.");
+ BufferInfo info;
+ info.mBufferID = buffer;
+ info.mStatus = BufferInfo::OWNED_BY_US;
+ info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+ mBuffers[portIndex].push(info);
}
-
- err = mOMX->allocateBufferWithBackup(
- mNode, portIndex, mem, &buffer);
- } else {
- err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
}
+ }
- if (err != OK) {
- return err;
- }
+ if (err != OK) {
+ return err;
+ }
- BufferInfo info;
- info.mBufferID = buffer;
- info.mStatus = BufferInfo::OWNED_BY_US;
- info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
- mBuffers[portIndex].push(info);
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatBuffersAllocated);
+
+ notify->setInt32("portIndex", portIndex);
+ for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+ AString name = StringPrintf("buffer-id_%d", i);
+ notify->setPointer(name.c_str(), mBuffers[portIndex][i].mBufferID);
+
+ name = StringPrintf("data_%d", i);
+ notify->setBuffer(name.c_str(), mBuffers[portIndex][i].mData);
}
+ notify->post();
+
return OK;
}
@@ -671,7 +703,7 @@ ACodec::BufferInfo *ACodec::findBufferByID(
return NULL;
}
-void ACodec::setComponentRole(
+status_t ACodec::setComponentRole(
bool isEncoder, const char *mime) {
struct MimeToRole {
const char *mime;
@@ -700,6 +732,8 @@ void ACodec::setComponentRole(
"video_decoder.mpeg4", "video_encoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_H263,
"video_decoder.h263", "video_encoder.h263" },
+ { MEDIA_MIMETYPE_VIDEO_VPX,
+ "video_decoder.vpx", "video_encoder.vpx" },
};
static const size_t kNumMimeToRole =
@@ -713,7 +747,7 @@ void ACodec::setComponentRole(
}
if (i == kNumMimeToRole) {
- return;
+ return ERROR_UNSUPPORTED;
}
const char *role =
@@ -736,50 +770,83 @@ void ACodec::setComponentRole(
if (err != OK) {
ALOGW("[%s] Failed to set standard component role '%s'.",
mComponentName.c_str(), role);
+
+ return err;
}
}
+
+ return OK;
}
-void ACodec::configureCodec(
+status_t ACodec::configureCodec(
const char *mime, const sp<AMessage> &msg) {
- setComponentRole(false /* isEncoder */, mime);
+ int32_t encoder;
+ if (!msg->findInt32("encoder", &encoder)) {
+ encoder = false;
+ }
- if (!strncasecmp(mime, "video/", 6)) {
- int32_t width, height;
- CHECK(msg->findInt32("width", &width));
- CHECK(msg->findInt32("height", &height));
+ mIsEncoder = encoder;
- CHECK_EQ(setupVideoDecoder(mime, width, height),
- (status_t)OK);
+ status_t err = setComponentRole(encoder /* isEncoder */, mime);
+
+ if (err != OK) {
+ return err;
+ }
+
+ int32_t bitRate = 0;
+ if (encoder && !msg->findInt32("bitrate", &bitRate)) {
+ return INVALID_OPERATION;
+ }
+
+ if (!strncasecmp(mime, "video/", 6)) {
+ if (encoder) {
+ err = setupVideoEncoder(mime, msg);
+ } else {
+ int32_t width, height;
+ if (!msg->findInt32("width", &width)
+ || !msg->findInt32("height", &height)) {
+ err = INVALID_OPERATION;
+ } else {
+ err = setupVideoDecoder(mime, width, height);
+ }
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
int32_t numChannels, sampleRate;
- CHECK(msg->findInt32("channel-count", &numChannels));
- CHECK(msg->findInt32("sample-rate", &sampleRate));
-
- CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK);
+ if (!msg->findInt32("channel-count", &numChannels)
+ || !msg->findInt32("sample-rate", &sampleRate)) {
+ err = INVALID_OPERATION;
+ } else {
+ err = setupAACCodec(encoder, numChannels, sampleRate, bitRate);
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
- CHECK_EQ(setupAMRDecoder(false /* isWAMR */), (status_t)OK);
+ err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
- CHECK_EQ(setupAMRDecoder(true /* isWAMR */), (status_t)OK);
+ err = setupAMRCodec(encoder, true /* isWAMR */, bitRate);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) {
// These are PCM-like formats with a fixed sample rate but
// a variable number of channels.
int32_t numChannels;
- CHECK(msg->findInt32("channel-count", &numChannels));
+ if (!msg->findInt32("channel-count", &numChannels)) {
+ err = INVALID_OPERATION;
+ } else {
+ err = setupG711Codec(encoder, numChannels);
+ }
+ }
- CHECK_EQ(setupG711Decoder(numChannels), (status_t)OK);
+ if (err != OK) {
+ return err;
}
int32_t maxInputSize;
if (msg->findInt32("max-input-size", &maxInputSize)) {
- CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize),
- (status_t)OK);
+ err = setMinBufferSize(kPortIndexInput, (size_t)maxInputSize);
} else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) {
- CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192), // XXX
- (status_t)OK);
+ err = setMinBufferSize(kPortIndexInput, 8192); // XXX
}
+
+ return err;
}
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
@@ -819,12 +886,113 @@ status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
return OK;
}
-status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
+status_t ACodec::selectAudioPortFormat(
+ OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat) {
+ OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
+
+ format.nPortIndex = portIndex;
+ for (OMX_U32 index = 0;; ++index) {
+ format.nIndex = index;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format));
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (format.eEncoding == desiredFormat) {
+ break;
+ }
+ }
+
+ return mOMX->setParameter(
+ mNode, OMX_IndexParamAudioPortFormat, &format, sizeof(format));
+}
+
+status_t ACodec::setupAACCodec(
+ bool encoder,
+ int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
+ status_t err = setupRawAudioFormat(
+ encoder ? kPortIndexInput : kPortIndexOutput,
+ sampleRate,
+ numChannels);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (encoder) {
+ err = selectAudioPortFormat(kPortIndexOutput, OMX_AUDIO_CodingAAC);
+
+ if (err != OK) {
+ return err;
+ }
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ def.format.audio.bFlagErrorConcealment = OMX_TRUE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+ InitOMXParams(&profile);
+ profile.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+ if (err != OK) {
+ return err;
+ }
+
+ profile.nChannels = numChannels;
+
+ profile.eChannelMode =
+ (numChannels == 1)
+ ? OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo;
+
+ profile.nSampleRate = sampleRate;
+ profile.nBitRate = bitRate;
+ profile.nAudioBandWidth = 0;
+ profile.nFrameLength = 0;
+ profile.nAACtools = OMX_AUDIO_AACToolAll;
+ profile.nAACERtools = OMX_AUDIO_AACERNone;
+ profile.eAACProfile = OMX_AUDIO_AACObjectLC;
+ profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+ if (err != OK) {
+ return err;
+ }
+
+ return err;
+ }
+
OMX_AUDIO_PARAM_AACPROFILETYPE profile;
InitOMXParams(&profile);
profile.nPortIndex = kPortIndexInput;
- status_t err = mOMX->getParameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
if (err != OK) {
@@ -835,16 +1003,59 @@ status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
profile.nSampleRate = sampleRate;
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
- err = mOMX->setParameter(
+ return mOMX->setParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+}
- return err;
+static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(
+ bool isAMRWB, int32_t bps) {
+ if (isAMRWB) {
+ if (bps <= 6600) {
+ return OMX_AUDIO_AMRBandModeWB0;
+ } else if (bps <= 8850) {
+ return OMX_AUDIO_AMRBandModeWB1;
+ } else if (bps <= 12650) {
+ return OMX_AUDIO_AMRBandModeWB2;
+ } else if (bps <= 14250) {
+ return OMX_AUDIO_AMRBandModeWB3;
+ } else if (bps <= 15850) {
+ return OMX_AUDIO_AMRBandModeWB4;
+ } else if (bps <= 18250) {
+ return OMX_AUDIO_AMRBandModeWB5;
+ } else if (bps <= 19850) {
+ return OMX_AUDIO_AMRBandModeWB6;
+ } else if (bps <= 23050) {
+ return OMX_AUDIO_AMRBandModeWB7;
+ }
+
+ // 23850 bps
+ return OMX_AUDIO_AMRBandModeWB8;
+ } else { // AMRNB
+ if (bps <= 4750) {
+ return OMX_AUDIO_AMRBandModeNB0;
+ } else if (bps <= 5150) {
+ return OMX_AUDIO_AMRBandModeNB1;
+ } else if (bps <= 5900) {
+ return OMX_AUDIO_AMRBandModeNB2;
+ } else if (bps <= 6700) {
+ return OMX_AUDIO_AMRBandModeNB3;
+ } else if (bps <= 7400) {
+ return OMX_AUDIO_AMRBandModeNB4;
+ } else if (bps <= 7950) {
+ return OMX_AUDIO_AMRBandModeNB5;
+ } else if (bps <= 10200) {
+ return OMX_AUDIO_AMRBandModeNB6;
+ }
+
+ // 12200 bps
+ return OMX_AUDIO_AMRBandModeNB7;
+ }
}
-status_t ACodec::setupAMRDecoder(bool isWAMR) {
+status_t ACodec::setupAMRCodec(bool encoder, bool isWAMR, int32_t bitrate) {
OMX_AUDIO_PARAM_AMRTYPE def;
InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
+ def.nPortIndex = encoder ? kPortIndexOutput : kPortIndexInput;
status_t err =
mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
@@ -854,14 +1065,24 @@ status_t ACodec::setupAMRDecoder(bool isWAMR) {
}
def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+ def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitrate);
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
- def.eAMRBandMode =
- isWAMR ? OMX_AUDIO_AMRBandModeWB0 : OMX_AUDIO_AMRBandModeNB0;
+ if (err != OK) {
+ return err;
+ }
- return mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ return setupRawAudioFormat(
+ encoder ? kPortIndexInput : kPortIndexOutput,
+ isWAMR ? 16000 : 8000 /* sampleRate */,
+ 1 /* numChannels */);
}
-status_t ACodec::setupG711Decoder(int32_t numChannels) {
+status_t ACodec::setupG711Codec(bool encoder, int32_t numChannels) {
+ CHECK(!encoder); // XXX TODO
+
return setupRawAudioFormat(
kPortIndexInput, 8000 /* sampleRate */, numChannels);
}
@@ -1001,22 +1222,36 @@ status_t ACodec::setSupportedOutputFormat() {
&format, sizeof(format));
}
-status_t ACodec::setupVideoDecoder(
- const char *mime, int32_t width, int32_t height) {
- OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+static status_t GetVideoCodingTypeFromMime(
+ const char *mime, OMX_VIDEO_CODINGTYPE *codingType) {
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- compressionFormat = OMX_VIDEO_CodingAVC;
+ *codingType = OMX_VIDEO_CodingAVC;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- compressionFormat = OMX_VIDEO_CodingMPEG4;
+ *codingType = OMX_VIDEO_CodingMPEG4;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- compressionFormat = OMX_VIDEO_CodingH263;
+ *codingType = OMX_VIDEO_CodingH263;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {
- compressionFormat = OMX_VIDEO_CodingMPEG2;
+ *codingType = OMX_VIDEO_CodingMPEG2;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) {
+ *codingType = OMX_VIDEO_CodingVPX;
} else {
- TRESPASS();
+ *codingType = OMX_VIDEO_CodingUnused;
+ return ERROR_UNSUPPORTED;
}
- status_t err = setVideoPortFormatType(
+ return OK;
+}
+
+status_t ACodec::setupVideoDecoder(
+ const char *mime, int32_t width, int32_t height) {
+ OMX_VIDEO_CODINGTYPE compressionFormat;
+ status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = setVideoPortFormatType(
kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
if (err != OK) {
@@ -1046,6 +1281,489 @@ status_t ACodec::setupVideoDecoder(
return OK;
}
+status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) {
+ int32_t tmp;
+ if (!msg->findInt32("color-format", &tmp)) {
+ return INVALID_OPERATION;
+ }
+
+ OMX_COLOR_FORMATTYPE colorFormat =
+ static_cast<OMX_COLOR_FORMATTYPE>(tmp);
+
+ status_t err = setVideoPortFormatType(
+ kPortIndexInput, OMX_VIDEO_CodingUnused, colorFormat);
+
+ if (err != OK) {
+ ALOGE("[%s] does not support color format %d",
+ mComponentName.c_str(), colorFormat);
+
+ return err;
+ }
+
+ /* Input port configuration */
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ def.nPortIndex = kPortIndexInput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ int32_t width, height, bitrate;
+ if (!msg->findInt32("width", &width)
+ || !msg->findInt32("height", &height)
+ || !msg->findInt32("bitrate", &bitrate)) {
+ return INVALID_OPERATION;
+ }
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ int32_t stride;
+ if (!msg->findInt32("stride", &stride)) {
+ stride = width;
+ }
+
+ video_def->nStride = stride;
+
+ int32_t sliceHeight;
+ if (!msg->findInt32("slice-height", &sliceHeight)) {
+ sliceHeight = height;
+ }
+
+ video_def->nSliceHeight = sliceHeight;
+
+ def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2;
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
+ video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ video_def->eColorFormat = colorFormat;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ ALOGE("[%s] failed to set input port definition parameters.",
+ mComponentName.c_str());
+
+ return err;
+ }
+
+ /* Output port configuration */
+
+ OMX_VIDEO_CODINGTYPE compressionFormat;
+ err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = setVideoPortFormatType(
+ kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused);
+
+ if (err != OK) {
+ ALOGE("[%s] does not support compression format %d",
+ mComponentName.c_str(), compressionFormat);
+
+ return err;
+ }
+
+ def.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+ video_def->xFramerate = 0;
+ video_def->nBitrate = bitrate;
+ video_def->eCompressionFormat = compressionFormat;
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ ALOGE("[%s] failed to set output port definition parameters.",
+ mComponentName.c_str());
+
+ return err;
+ }
+
+ switch (compressionFormat) {
+ case OMX_VIDEO_CodingMPEG4:
+ err = setupMPEG4EncoderParameters(msg);
+ break;
+
+ case OMX_VIDEO_CodingH263:
+ err = setupH263EncoderParameters(msg);
+ break;
+
+ case OMX_VIDEO_CodingAVC:
+ err = setupAVCEncoderParameters(msg);
+ break;
+
+ default:
+ break;
+ }
+
+ ALOGI("setupVideoEncoder succeeded");
+
+ return err;
+}
+
+static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) {
+ if (iFramesInterval < 0) {
+ return 0xFFFFFFFF;
+ } else if (iFramesInterval == 0) {
+ return 0;
+ }
+ OMX_U32 ret = frameRate * iFramesInterval;
+ CHECK(ret > 1);
+ return ret;
+}
+
+status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
+ int32_t bitrate, iFrameInterval;
+ if (!msg->findInt32("bitrate", &bitrate)
+ || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ return INVALID_OPERATION;
+ }
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type;
+ InitOMXParams(&mpeg4type);
+ mpeg4type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ mpeg4type.nSliceHeaderSpacing = 0;
+ mpeg4type.bSVH = OMX_FALSE;
+ mpeg4type.bGov = OMX_FALSE;
+
+ mpeg4type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ if (mpeg4type.nPFrames == 0) {
+ mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+ }
+ mpeg4type.nBFrames = 0;
+ mpeg4type.nIDCVLCThreshold = 0;
+ mpeg4type.bACPred = OMX_TRUE;
+ mpeg4type.nMaxPacketSize = 256;
+ mpeg4type.nTimeIncRes = 1000;
+ mpeg4type.nHeaderExtension = 0;
+ mpeg4type.bReversibleVLC = OMX_FALSE;
+
+ int32_t profile;
+ if (msg->findInt32("profile", &profile)) {
+ int32_t level;
+ if (!msg->findInt32("level", &level)) {
+ return INVALID_OPERATION;
+ }
+
+ err = verifySupportForProfileAndLevel(profile, level);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profile);
+ mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(level);
+ }
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = configureBitrate(bitrate);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return setupErrorCorrectionParameters();
+}
+
+status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) {
+ int32_t bitrate, iFrameInterval;
+ if (!msg->findInt32("bitrate", &bitrate)
+ || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ return INVALID_OPERATION;
+ }
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ OMX_VIDEO_PARAM_H263TYPE h263type;
+ InitOMXParams(&h263type);
+ h263type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ h263type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ if (h263type.nPFrames == 0) {
+ h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+ }
+ h263type.nBFrames = 0;
+
+ int32_t profile;
+ if (msg->findInt32("profile", &profile)) {
+ int32_t level;
+ if (!msg->findInt32("level", &level)) {
+ return INVALID_OPERATION;
+ }
+
+ err = verifySupportForProfileAndLevel(profile, level);
+
+ if (err != OK) {
+ return err;
+ }
+
+ h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profile);
+ h263type.eLevel = static_cast<OMX_VIDEO_H263LEVELTYPE>(level);
+ }
+
+ h263type.bPLUSPTYPEAllowed = OMX_FALSE;
+ h263type.bForceRoundingTypeToZero = OMX_FALSE;
+ h263type.nPictureHeaderRepetition = 0;
+ h263type.nGOBHeaderInterval = 0;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = configureBitrate(bitrate);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return setupErrorCorrectionParameters();
+}
+
+status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
+ int32_t bitrate, iFrameInterval;
+ if (!msg->findInt32("bitrate", &bitrate)
+ || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ return INVALID_OPERATION;
+ }
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ OMX_VIDEO_PARAM_AVCTYPE h264type;
+ InitOMXParams(&h264type);
+ h264type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ h264type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ int32_t profile;
+ if (msg->findInt32("profile", &profile)) {
+ int32_t level;
+ if (!msg->findInt32("level", &level)) {
+ return INVALID_OPERATION;
+ }
+
+ err = verifySupportForProfileAndLevel(profile, level);
+
+ if (err != OK) {
+ return err;
+ }
+
+ h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile);
+ h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(level);
+ }
+
+ // XXX
+ if (!strncmp(mComponentName.c_str(), "OMX.TI.DUCATI1", 14)) {
+ h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
+ }
+
+ if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {
+ h264type.nSliceHeaderSpacing = 0;
+ h264type.bUseHadamard = OMX_TRUE;
+ h264type.nRefFrames = 1;
+ h264type.nBFrames = 0;
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ if (h264type.nPFrames == 0) {
+ h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+ }
+ h264type.nRefIdx10ActiveMinus1 = 0;
+ h264type.nRefIdx11ActiveMinus1 = 0;
+ h264type.bEntropyCodingCABAC = OMX_FALSE;
+ h264type.bWeightedPPrediction = OMX_FALSE;
+ h264type.bconstIpred = OMX_FALSE;
+ h264type.bDirect8x8Inference = OMX_FALSE;
+ h264type.bDirectSpatialTemporal = OMX_FALSE;
+ h264type.nCabacInitIdc = 0;
+ }
+
+ if (h264type.nBFrames != 0) {
+ h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
+ }
+
+ h264type.bEnableUEP = OMX_FALSE;
+ h264type.bEnableFMO = OMX_FALSE;
+ h264type.bEnableASO = OMX_FALSE;
+ h264type.bEnableRS = OMX_FALSE;
+ h264type.bFrameMBsOnly = OMX_TRUE;
+ h264type.bMBAFF = OMX_FALSE;
+ h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
+
+ if (!strcasecmp("OMX.Nvidia.h264.encoder", mComponentName.c_str())) {
+ h264type.eLevel = OMX_VIDEO_AVCLevelMax;
+ }
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ return configureBitrate(bitrate);
+}
+
+status_t ACodec::verifySupportForProfileAndLevel(
+ int32_t profile, int32_t level) {
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE params;
+ InitOMXParams(&params);
+ params.nPortIndex = kPortIndexOutput;
+
+ for (params.nProfileIndex = 0;; ++params.nProfileIndex) {
+ status_t err = mOMX->getParameter(
+ mNode,
+ OMX_IndexParamVideoProfileLevelQuerySupported,
+ &params,
+ sizeof(params));
+
+ if (err != OK) {
+ return err;
+ }
+
+ int32_t supportedProfile = static_cast<int32_t>(params.eProfile);
+ int32_t supportedLevel = static_cast<int32_t>(params.eLevel);
+
+ if (profile == supportedProfile && level <= supportedLevel) {
+ return OK;
+ }
+ }
+}
+
+status_t ACodec::configureBitrate(int32_t bitrate) {
+ OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
+ InitOMXParams(&bitrateType);
+ bitrateType.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+
+ if (err != OK) {
+ return err;
+ }
+
+ bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+ bitrateType.nTargetBitrate = bitrate;
+
+ return mOMX->setParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+}
+
+status_t ACodec::setupErrorCorrectionParameters() {
+ OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
+ InitOMXParams(&errorCorrectionType);
+ errorCorrectionType.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoErrorCorrection,
+ &errorCorrectionType, sizeof(errorCorrectionType));
+
+ if (err != OK) {
+ return OK; // Optional feature. Ignore this failure
+ }
+
+ errorCorrectionType.bEnableHEC = OMX_FALSE;
+ errorCorrectionType.bEnableResync = OMX_TRUE;
+ errorCorrectionType.nResynchMarkerSpacing = 256;
+ errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
+ errorCorrectionType.bEnableRVLC = OMX_FALSE;
+
+ return mOMX->setParameter(
+ mNode, OMX_IndexParamVideoErrorCorrection,
+ &errorCorrectionType, sizeof(errorCorrectionType));
+}
+
status_t ACodec::setVideoFormatOnPort(
OMX_U32 portIndex,
int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) {
@@ -1166,6 +1884,9 @@ void ACodec::sendFormatChange() {
notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
notify->setInt32("width", videoDef->nFrameWidth);
notify->setInt32("height", videoDef->nFrameHeight);
+ notify->setInt32("stride", videoDef->nStride);
+ notify->setInt32("slice-height", videoDef->nSliceHeight);
+ notify->setInt32("color-format", videoDef->eColorFormat);
OMX_CONFIG_RECTTYPE rect;
InitOMXParams(&rect);
@@ -1241,10 +1962,11 @@ void ACodec::sendFormatChange() {
mSentFormat = true;
}
-void ACodec::signalError(OMX_ERRORTYPE error) {
+void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", ACodec::kWhatError);
notify->setInt32("omx-error", error);
+ notify->setInt32("err", internalError);
notify->post();
}
@@ -1417,7 +2139,7 @@ void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
notify->setPointer("buffer-id", info->mBufferID);
info->mData->meta()->clear();
- notify->setObject("buffer", info->mData);
+ notify->setBuffer("buffer", info->mData);
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
reply->setPointer("buffer-id", info->mBufferID);
@@ -1433,18 +2155,26 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
IOMX::buffer_id bufferID;
CHECK(msg->findPointer("buffer-id", &bufferID));
- sp<RefBase> obj;
+ sp<ABuffer> buffer;
int32_t err = OK;
- if (!msg->findObject("buffer", &obj)) {
+ bool eos = false;
+
+ if (!msg->findBuffer("buffer", &buffer)) {
CHECK(msg->findInt32("err", &err));
ALOGV("[%s] saw error %d instead of an input buffer",
mCodec->mComponentName.c_str(), err);
- obj.clear();
+ buffer.clear();
+
+ eos = true;
}
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ int32_t tmp;
+ if (buffer != NULL && buffer->meta()->findInt32("eos", &tmp) && tmp) {
+ eos = true;
+ err = ERROR_END_OF_STREAM;
+ }
BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM);
@@ -1456,7 +2186,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
switch (mode) {
case KEEP_BUFFERS:
{
- if (buffer == NULL) {
+ if (eos) {
if (!mCodec->mPortEOS[kPortIndexInput]) {
mCodec->mPortEOS[kPortIndexInput] = true;
mCodec->mInputEOSResult = err;
@@ -1467,9 +2197,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
case RESUBMIT_BUFFERS:
{
- if (buffer != NULL) {
- CHECK(!mCodec->mPortEOS[kPortIndexInput]);
-
+ if (buffer != NULL && !mCodec->mPortEOS[kPortIndexInput]) {
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
@@ -1480,6 +2208,10 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
flags |= OMX_BUFFERFLAG_CODECCONFIG;
}
+ if (eos) {
+ flags |= OMX_BUFFERFLAG_EOS;
+ }
+
if (buffer != info->mData) {
if (0 && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
ALOGV("[%s] Needs to copy input data.",
@@ -1493,6 +2225,9 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
ALOGV("[%s] calling emptyBuffer %p w/ codec specific data",
mCodec->mComponentName.c_str(), bufferID);
+ } else if (flags & OMX_BUFFERFLAG_EOS) {
+ ALOGV("[%s] calling emptyBuffer %p w/ EOS",
+ mCodec->mComponentName.c_str(), bufferID);
} else {
ALOGV("[%s] calling emptyBuffer %p w/ time %lld us",
mCodec->mComponentName.c_str(), bufferID, timeUs);
@@ -1509,7 +2244,15 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
- getMoreInputDataIfPossible();
+ if (!eos) {
+ getMoreInputDataIfPossible();
+ } else {
+ ALOGV("[%s] Signalled EOS on the input port",
+ mCodec->mComponentName.c_str());
+
+ mCodec->mPortEOS[kPortIndexInput] = true;
+ mCodec->mInputEOSResult = err;
+ }
} else if (!mCodec->mPortEOS[kPortIndexInput]) {
if (err != ERROR_END_OF_STREAM) {
ALOGV("[%s] Signalling EOS on the input port "
@@ -1582,8 +2325,8 @@ bool ACodec::BaseState::onOMXFillBufferDone(
int64_t timeUs,
void *platformPrivate,
void *dataPtr) {
- ALOGV("[%s] onOMXFillBufferDone %p time %lld us",
- mCodec->mComponentName.c_str(), bufferID, timeUs);
+ ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx",
+ mCodec->mComponentName.c_str(), bufferID, timeUs, flags);
ssize_t index;
BufferInfo *info =
@@ -1601,46 +2344,48 @@ bool ACodec::BaseState::onOMXFillBufferDone(
case RESUBMIT_BUFFERS:
{
- if (rangeLength == 0) {
- if (!(flags & OMX_BUFFERFLAG_EOS)) {
- ALOGV("[%s] calling fillBuffer %p",
- mCodec->mComponentName.c_str(), info->mBufferID);
+ if (rangeLength == 0 && !(flags & OMX_BUFFERFLAG_EOS)) {
+ ALOGV("[%s] calling fillBuffer %p",
+ mCodec->mComponentName.c_str(), info->mBufferID);
- CHECK_EQ(mCodec->mOMX->fillBuffer(
- mCodec->mNode, info->mBufferID),
- (status_t)OK);
+ CHECK_EQ(mCodec->mOMX->fillBuffer(
+ mCodec->mNode, info->mBufferID),
+ (status_t)OK);
- info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
- }
- } else {
- if (!mCodec->mSentFormat) {
- mCodec->sendFormatChange();
- }
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ break;
+ }
- if (mCodec->mNativeWindow == NULL) {
- info->mData->setRange(rangeOffset, rangeLength);
- }
+ if (!mCodec->mIsEncoder && !mCodec->mSentFormat) {
+ mCodec->sendFormatChange();
+ }
- info->mData->meta()->setInt64("timeUs", timeUs);
+ if (mCodec->mNativeWindow == NULL) {
+ info->mData->setRange(rangeOffset, rangeLength);
+ }
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
- notify->setPointer("buffer-id", info->mBufferID);
- notify->setObject("buffer", info->mData);
+ info->mData->meta()->setInt64("timeUs", timeUs);
- sp<AMessage> reply =
- new AMessage(kWhatOutputBufferDrained, mCodec->id());
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+ notify->setPointer("buffer-id", info->mBufferID);
+ notify->setBuffer("buffer", info->mData);
+ notify->setInt32("flags", flags);
- reply->setPointer("buffer-id", info->mBufferID);
+ sp<AMessage> reply =
+ new AMessage(kWhatOutputBufferDrained, mCodec->id());
- notify->setMessage("reply", reply);
+ reply->setPointer("buffer-id", info->mBufferID);
- notify->post();
+ notify->setMessage("reply", reply);
- info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
- }
+ notify->post();
+
+ info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
if (flags & OMX_BUFFERFLAG_EOS) {
+ ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str());
+
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatEOS);
notify->setInt32("err", mCodec->mInputEOSResult);
@@ -1678,12 +2423,13 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
&& msg->findInt32("render", &render) && render != 0) {
// The client wants this buffer to be rendered.
- if (mCodec->mNativeWindow->queueBuffer(
+ status_t err;
+ if ((err = mCodec->mNativeWindow->queueBuffer(
mCodec->mNativeWindow.get(),
- info->mGraphicBuffer.get()) == OK) {
+ info->mGraphicBuffer.get())) == OK) {
info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
} else {
- mCodec->signalError();
+ mCodec->signalError(OMX_ErrorUndefined, err);
info->mStatus = BufferInfo::OWNED_BY_US;
}
} else {
@@ -1758,6 +2504,27 @@ bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case ACodec::kWhatAllocateComponent:
+ {
+ onAllocateComponent(msg);
+ handled = true;
+ break;
+ }
+
+ case ACodec::kWhatConfigureComponent:
+ {
+ onConfigureComponent(msg);
+ handled = true;
+ break;
+ }
+
+ case ACodec::kWhatStart:
+ {
+ onStart();
+ handled = true;
+ break;
+ }
+
case ACodec::kWhatShutdown:
{
sp<AMessage> notify = mCodec->mNotify->dup();
@@ -1787,27 +2554,54 @@ bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
void ACodec::UninitializedState::onSetup(
const sp<AMessage> &msg) {
+ onAllocateComponent(msg);
+ onConfigureComponent(msg);
+ onStart();
+}
+
+void ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
+ ALOGV("onAllocateComponent");
+
+ if (mCodec->mNode != NULL) {
+ CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
+
+ mCodec->mNativeWindow.clear();
+ mCodec->mNode = NULL;
+ mCodec->mOMX.clear();
+ mCodec->mComponentName.clear();
+ }
+
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
sp<IOMX> omx = client.interface();
+ Vector<String8> matchingCodecs;
+
AString mime;
- CHECK(msg->findString("mime", &mime));
- Vector<String8> matchingCodecs;
- OMXCodec::findMatchingCodecs(
- mime.c_str(),
- false, // createEncoder
- NULL, // matchComponentName
- 0, // flags
- &matchingCodecs);
+ AString componentName;
+ if (msg->findString("componentName", &componentName)) {
+ matchingCodecs.push_back(String8(componentName.c_str()));
+ } else {
+ CHECK(msg->findString("mime", &mime));
+
+ int32_t encoder;
+ if (!msg->findInt32("encoder", &encoder)) {
+ encoder = false;
+ }
+
+ OMXCodec::findMatchingCodecs(
+ mime.c_str(),
+ encoder, // createEncoder
+ NULL, // matchComponentName
+ 0, // flags
+ &matchingCodecs);
+ }
sp<CodecObserver> observer = new CodecObserver;
IOMX::node_id node = NULL;
- AString componentName;
-
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).string();
@@ -1826,7 +2620,12 @@ void ACodec::UninitializedState::onSetup(
}
if (node == NULL) {
- ALOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str());
+ if (!mime.empty()) {
+ ALOGE("Unable to instantiate a decoder for type '%s'.",
+ mime.c_str());
+ } else {
+ ALOGE("Unable to instantiate decoder '%s'.", componentName.c_str());
+ }
mCodec->signalError(OMX_ErrorComponentNotFound);
return;
@@ -1844,20 +2643,52 @@ void ACodec::UninitializedState::onSetup(
mCodec->mInputEOSResult = OK;
- mCodec->configureCodec(mime.c_str(), msg);
+ {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatComponentAllocated);
+ notify->setString("componentName", mCodec->mComponentName.c_str());
+ notify->post();
+ }
+}
+
+void ACodec::UninitializedState::onConfigureComponent(
+ const sp<AMessage> &msg) {
+ ALOGV("onConfigureComponent");
+
+ CHECK(mCodec->mNode != NULL);
+
+ AString mime;
+ CHECK(msg->findString("mime", &mime));
+
+ status_t err = mCodec->configureCodec(mime.c_str(), msg);
+
+ if (err != OK) {
+ mCodec->signalError(OMX_ErrorUndefined, err);
+ return;
+ }
sp<RefBase> obj;
if (msg->findObject("native-window", &obj)
- && strncmp("OMX.google.", componentName.c_str(), 11)) {
+ && strncmp("OMX.google.", mCodec->mComponentName.c_str(), 11)) {
sp<NativeWindowWrapper> nativeWindow(
static_cast<NativeWindowWrapper *>(obj.get()));
CHECK(nativeWindow != NULL);
mCodec->mNativeWindow = nativeWindow->getNativeWindow();
}
-
CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
- CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle),
+ {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatComponentConfigured);
+ notify->post();
+ }
+}
+
+void ACodec::UninitializedState::onStart() {
+ ALOGV("onStart");
+
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
(status_t)OK);
mCodec->changeState(mCodec->mLoadedToIdleState);
@@ -1878,7 +2709,7 @@ void ACodec::LoadedToIdleState::stateEntered() {
"(error 0x%08x)",
err);
- mCodec->signalError();
+ mCodec->signalError(OMX_ErrorUndefined, err);
}
}
@@ -2202,7 +3033,7 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
"port reconfiguration (error 0x%08x)",
err);
- mCodec->signalError();
+ mCodec->signalError(OMX_ErrorUndefined, err);
// This is technically not correct, since we were unable
// to allocate output buffers and therefore the output port
@@ -2240,7 +3071,8 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
////////////////////////////////////////////////////////////////////////////////
ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
- : BaseState(codec) {
+ : BaseState(codec),
+ mComponentNowIdle(false) {
}
bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
@@ -2274,6 +3106,7 @@ bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
void ACodec::ExecutingToIdleState::stateEntered() {
ALOGV("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
+ mComponentNowIdle = false;
mCodec->mSentFormat = false;
}
@@ -2285,6 +3118,8 @@ bool ACodec::ExecutingToIdleState::onOMXEvent(
CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+ mComponentNowIdle = true;
+
changeStateIfWeOwnAllBuffers();
return true;
@@ -2303,7 +3138,7 @@ bool ACodec::ExecutingToIdleState::onOMXEvent(
}
void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
- if (mCodec->allYourBuffersAreBelongToUs()) {
+ if (mComponentNowIdle && mCodec->allYourBuffersAreBelongToUs()) {
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded),
(status_t)OK);
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 5a28347..03dcbf9 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -20,9 +20,9 @@
#include "include/AMRExtractor.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 6c4e307..ca85640 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/AMRWriter.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -52,7 +52,7 @@ AMRWriter::AMRWriter(int fd)
AMRWriter::~AMRWriter() {
if (mStarted) {
- stop();
+ reset();
}
if (mFd != -1) {
@@ -152,7 +152,7 @@ status_t AMRWriter::pause() {
return OK;
}
-status_t AMRWriter::stop() {
+status_t AMRWriter::reset() {
if (!mStarted) {
return OK;
}
diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp
index a3187b7..5a6211e 100644
--- a/media/libstagefright/AVIExtractor.cpp
+++ b/media/libstagefright/AVIExtractor.cpp
@@ -577,6 +577,7 @@ static const char *GetMIMETypeForHandler(uint32_t handler) {
case FOURCC('a', 'v', 'c', '1'):
case FOURCC('d', 'a', 'v', 'c'):
case FOURCC('x', '2', '6', '4'):
+ case FOURCC('H', '2', '6', '4'):
case FOURCC('v', 's', 's', 'h'):
return MEDIA_MIMETYPE_VIDEO_AVC;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 690deac..cfb1e29 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \
AACWriter.cpp \
AMRExtractor.cpp \
AMRWriter.cpp \
+ AVIExtractor.cpp \
AudioPlayer.cpp \
AudioSource.cpp \
AwesomePlayer.cpp \
@@ -28,12 +29,14 @@ LOCAL_SRC_FILES:= \
MPEG4Writer.cpp \
MediaBuffer.cpp \
MediaBufferGroup.cpp \
+ MediaCodec.cpp \
MediaDefs.cpp \
MediaExtractor.cpp \
MediaSource.cpp \
MediaSourceSplitter.cpp \
MetaData.cpp \
NuCachedSource2.cpp \
+ NuMediaExtractor.cpp \
OMXClient.cpp \
OMXCodec.cpp \
OggExtractor.cpp \
@@ -60,25 +63,30 @@ LOCAL_C_INCLUDES:= \
$(TOP)/external/openssl/include \
LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libmedia \
- libutils \
- libcutils \
- libui \
- libsonivox \
- libvorbisidec \
+ libbinder \
+ libmedia \
+ libutils \
+ libcutils \
+ libui \
+ libsonivox \
+ libvorbisidec \
libstagefright_yuv \
libcamera_client \
- libdrmframework \
- libcrypto \
- libssl \
- libgui \
+ libdrmframework \
+ libcrypto \
+ libssl \
+ libgui \
+ libstagefright_omx \
+ liblog \
+ libicuuc \
+ libicui18n \
+ libz \
+ libdl \
+ libchromium_net \
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
libstagefright_aacenc \
- libstagefright_amrnbenc \
- libstagefright_amrwbenc \
libstagefright_avcenc \
libstagefright_m4vh263enc \
libstagefright_matroska \
@@ -88,59 +96,15 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_httplive \
libstagefright_id3 \
libFLAC \
+ libstagefright_chromium_http \
-################################################################################
-
-# The following was shamelessly copied from external/webkit/Android.mk and
-# currently must follow the same logic to determine how webkit was built and
-# if it's safe to link against libchromium.net
-
-# V8 also requires an ARMv7 CPU, and since we must use jsc, we cannot
-# use the Chrome http stack either.
-ifneq ($(strip $(ARCH_ARM_HAVE_ARMV7A)),true)
- USE_ALT_HTTP := true
-endif
-
-# See if the user has specified a stack they want to use
-HTTP_STACK = $(HTTP)
-# We default to the Chrome HTTP stack.
-DEFAULT_HTTP = chrome
-ALT_HTTP = android
-
-ifneq ($(HTTP_STACK),chrome)
- ifneq ($(HTTP_STACK),android)
- # No HTTP stack is specified, pickup the one we want as default.
- ifeq ($(USE_ALT_HTTP),true)
- HTTP_STACK = $(ALT_HTTP)
- else
- HTTP_STACK = $(DEFAULT_HTTP)
- endif
- endif
-endif
-
-ifeq ($(HTTP_STACK),chrome)
-
-LOCAL_SHARED_LIBRARIES += \
- liblog \
- libicuuc \
- libicui18n \
- libz \
- libdl \
-
-LOCAL_STATIC_LIBRARIES += \
- libstagefright_chromium_http
-
-LOCAL_SHARED_LIBRARIES += libstlport libchromium_net
+LOCAL_SHARED_LIBRARIES += libstlport
include external/stlport/libstlport.mk
+# TODO: Chromium is always available, so this flag can be removed.
LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
-endif # ifeq ($(HTTP_STACK),chrome)
-
-################################################################################
-
LOCAL_SHARED_LIBRARIES += \
- libstagefright_amrnb_common \
libstagefright_enc_common \
libstagefright_avc_common \
libstagefright_foundation \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 9a9c3ef..df27566 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -268,6 +268,16 @@ bool AudioPlayer::reachedEOS(status_t *finalStatus) {
return mReachedEOS;
}
+status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) {
+ if (mAudioSink.get() != NULL) {
+ return mAudioSink->setPlaybackRatePermille(ratePermille);
+ } else if (mAudioTrack != NULL){
+ return mAudioTrack->setSampleRate(ratePermille * mSampleRate / 1000);
+ } else {
+ return NO_INIT;
+ }
+}
+
// static
size_t AudioPlayer::AudioSinkCallback(
MediaPlayerBase::AudioSink *audioSink,
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 2172cc0..5b2ea1f 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -47,7 +47,7 @@ static void AudioRecordCallbackFunction(int event, void *user, void *info) {
}
AudioSource::AudioSource(
- int inputSource, uint32_t sampleRate, uint32_t channels)
+ audio_source_t inputSource, uint32_t sampleRate, uint32_t channels)
: mStarted(false),
mSampleRate(sampleRate),
mPrevSampleTimeUs(0),
@@ -72,7 +72,7 @@ AudioSource::AudioSource(
AudioSource::~AudioSource() {
if (mStarted) {
- stop();
+ reset();
}
delete mRecord;
@@ -130,7 +130,7 @@ void AudioSource::waitOutstandingEncodingFrames_l() {
}
}
-status_t AudioSource::stop() {
+status_t AudioSource::reset() {
Mutex::Autolock autoLock(mLock);
if (!mStarted) {
return UNKNOWN_ERROR;
@@ -282,8 +282,6 @@ status_t AudioSource::dataCallbackTimestamp(
mPrevSampleTimeUs = mStartTimeUs;
}
- int64_t timestampUs = mPrevSampleTimeUs;
-
size_t numLostBytes = 0;
if (mNumFramesReceived > 0) { // Ignore earlier frame lost
// getInputFramesLost() returns the number of lost frames.
@@ -293,37 +291,58 @@ status_t AudioSource::dataCallbackTimestamp(
CHECK_EQ(numLostBytes & 1, 0u);
CHECK_EQ(audioBuffer.size & 1, 0u);
- size_t bufferSize = numLostBytes + audioBuffer.size;
- MediaBuffer *buffer = new MediaBuffer(bufferSize);
if (numLostBytes > 0) {
- memset(buffer->data(), 0, numLostBytes);
- memcpy((uint8_t *) buffer->data() + numLostBytes,
- audioBuffer.i16, audioBuffer.size);
- } else {
- if (audioBuffer.size == 0) {
- ALOGW("Nothing is available from AudioRecord callback buffer");
- buffer->release();
- return OK;
+ // Loss of audio frames should happen rarely; thus the LOGW should
+ // not cause a logging spam
+ ALOGW("Lost audio record data: %d bytes", numLostBytes);
+ }
+
+ while (numLostBytes > 0) {
+ size_t bufferSize = numLostBytes;
+ if (numLostBytes > kMaxBufferSize) {
+ numLostBytes -= kMaxBufferSize;
+ bufferSize = kMaxBufferSize;
+ } else {
+ numLostBytes = 0;
}
- memcpy((uint8_t *) buffer->data(),
- audioBuffer.i16, audioBuffer.size);
+ MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize);
+ memset(lostAudioBuffer->data(), 0, bufferSize);
+ lostAudioBuffer->set_range(0, bufferSize);
+ queueInputBuffer_l(lostAudioBuffer, timeUs);
+ }
+
+ if (audioBuffer.size == 0) {
+ ALOGW("Nothing is available from AudioRecord callback buffer");
+ return OK;
}
+ const size_t bufferSize = audioBuffer.size;
+ MediaBuffer *buffer = new MediaBuffer(bufferSize);
+ memcpy((uint8_t *) buffer->data(),
+ audioBuffer.i16, audioBuffer.size);
buffer->set_range(0, bufferSize);
- timestampUs += ((1000000LL * (bufferSize >> 1)) +
- (mSampleRate >> 1)) / mSampleRate;
+ queueInputBuffer_l(buffer, timeUs);
+ return OK;
+}
+
+void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) {
+ const size_t bufferSize = buffer->range_length();
+ const size_t frameSize = mRecord->frameSize();
+ const int64_t timestampUs =
+ mPrevSampleTimeUs +
+ ((1000000LL * (bufferSize / frameSize)) +
+ (mSampleRate >> 1)) / mSampleRate;
if (mNumFramesReceived == 0) {
buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
}
+
buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
mPrevSampleTimeUs = timestampUs;
- mNumFramesReceived += buffer->range_length() / sizeof(int16_t);
+ mNumFramesReceived += bufferSize / frameSize;
mBuffersReceived.push_back(buffer);
mFrameAvailableCondition.signal();
-
- return OK;
}
void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index d0cb7ff..8cfb8d3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -30,7 +30,7 @@
#include "include/MPEG2TSExtractor.h"
#include "include/WVMExtractor.h"
-#include "timedtext/TimedTextPlayer.h"
+#include "timedtext/TimedTextDriver.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -47,10 +47,8 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
-#include <surfaceflinger/Surface.h>
#include <gui/ISurfaceTexture.h>
#include <gui/SurfaceTextureClient.h>
-#include <surfaceflinger/ISurfaceComposer.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -192,7 +190,7 @@ AwesomePlayer::AwesomePlayer()
mVideoBuffer(NULL),
mDecryptHandle(NULL),
mLastVideoTimeUs(-1),
- mTextPlayer(NULL) {
+ mTextDriver(NULL) {
CHECK_EQ(mClient.connect(), (status_t)OK);
DataSource::RegisterDefaultSniffers();
@@ -335,6 +333,14 @@ status_t AwesomePlayer::setDataSource_l(
return UNKNOWN_ERROR;
}
+ if (extractor->getDrmFlag()) {
+ checkDrmStatus(dataSource);
+ }
+
+ return setDataSource_l(extractor);
+}
+
+void AwesomePlayer::checkDrmStatus(const sp<DataSource>& dataSource) {
dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
if (mDecryptHandle != NULL) {
CHECK(mDrmManagerClient);
@@ -342,8 +348,6 @@ status_t AwesomePlayer::setDataSource_l(
notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
}
}
-
- return setDataSource_l(extractor);
}
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
@@ -524,9 +528,9 @@ void AwesomePlayer::reset_l() {
delete mAudioPlayer;
mAudioPlayer = NULL;
- if (mTextPlayer != NULL) {
- delete mTextPlayer;
- mTextPlayer = NULL;
+ if (mTextDriver != NULL) {
+ delete mTextDriver;
+ mTextDriver = NULL;
}
mVideoRenderer.clear();
@@ -1112,7 +1116,7 @@ status_t AwesomePlayer::pause_l(bool at_eos) {
}
if (mFlags & TEXTPLAYER_STARTED) {
- mTextPlayer->pause();
+ mTextDriver->pause();
modifyFlags(TEXT_RUNNING, CLEAR);
}
@@ -1266,9 +1270,9 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) {
}
status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
- if (mTextPlayer != NULL) {
+ if (mTextDriver != NULL) {
if (index >= 0) { // to turn on a text track
- status_t err = mTextPlayer->setTimedTextTrackIndex(index);
+ status_t err = mTextDriver->setTimedTextTrackIndex(index);
if (err != OK) {
return err;
}
@@ -1284,7 +1288,7 @@ status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
modifyFlags(TEXTPLAYER_STARTED, CLEAR);
}
- return mTextPlayer->setTimedTextTrackIndex(index);
+ return mTextDriver->setTimedTextTrackIndex(index);
}
} else {
return INVALID_OPERATION;
@@ -1313,7 +1317,7 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
seekAudioIfNecessary_l();
if (mFlags & TEXTPLAYER_STARTED) {
- mTextPlayer->seekTo(mSeekTimeUs);
+ mTextDriver->seekToAsync(mSeekTimeUs);
}
if (!(mFlags & PLAYING)) {
@@ -1354,15 +1358,15 @@ void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
mAudioTrack = source;
}
-void AwesomePlayer::addTextSource(sp<MediaSource> source) {
+void AwesomePlayer::addTextSource(const sp<MediaSource>& source) {
Mutex::Autolock autoLock(mTimedTextLock);
CHECK(source != NULL);
- if (mTextPlayer == NULL) {
- mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue);
+ if (mTextDriver == NULL) {
+ mTextDriver = new TimedTextDriver(mListener);
}
- mTextPlayer->addTextSource(source);
+ mTextDriver->addInBandTextSource(source);
}
status_t AwesomePlayer::initAudioDecoder() {
@@ -1603,7 +1607,7 @@ void AwesomePlayer::onVideoEvent() {
mSeekTimeUs,
mSeeking == SEEK_VIDEO_ONLY
? MediaSource::ReadOptions::SEEK_NEXT_SYNC
- : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
+ : MediaSource::ReadOptions::SEEK_CLOSEST);
}
for (;;) {
status_t err = mVideoSource->read(&mVideoBuffer, &options);
@@ -1689,7 +1693,7 @@ void AwesomePlayer::onVideoEvent() {
}
if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
- mTextPlayer->resume();
+ mTextDriver->resume();
modifyFlags(TEXT_RUNNING, SET);
}
@@ -2095,7 +2099,7 @@ status_t AwesomePlayer::finishSetDataSource_l() {
String8 mimeType;
float confidence;
sp<AMessage> dummy;
- bool success = SniffDRM(dataSource, &mimeType, &confidence, &dummy);
+ bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
if (!success
|| strcasecmp(
@@ -2115,13 +2119,8 @@ status_t AwesomePlayer::finishSetDataSource_l() {
}
}
- dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
-
- if (mDecryptHandle != NULL) {
- CHECK(mDrmManagerClient);
- if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
- }
+ if (extractor->getDrmFlag()) {
+ checkDrmStatus(dataSource);
}
status_t err = setDataSource_l(extractor);
@@ -2240,16 +2239,24 @@ status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
case KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE:
{
Mutex::Autolock autoLock(mTimedTextLock);
- if (mTextPlayer == NULL) {
- mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue);
+ if (mTextDriver == NULL) {
+ mTextDriver = new TimedTextDriver(mListener);
}
- return mTextPlayer->setParameter(key, request);
+ return mTextDriver->addOutOfBandTextSource(request);
}
case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS:
{
return setCacheStatCollectFreq(request);
}
+ case KEY_PARAMETER_PLAYBACK_RATE_PERMILLE:
+ {
+ if (mAudioPlayer != NULL) {
+ return mAudioPlayer->setPlaybackRatePermille(request.readInt32());
+ } else {
+ return NO_INIT;
+ }
+ }
default:
{
return ERROR_UNSUPPORTED;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 1850c9c..2df5528 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -20,14 +20,14 @@
#include <OMX_Component.h>
#include <binder/IPCThreadState.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/CameraSource.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <camera/Camera.h>
#include <camera/CameraParameters.h>
-#include <surfaceflinger/Surface.h>
+#include <gui/Surface.h>
#include <utils/String8.h>
#include <cutils/properties.h>
@@ -114,7 +114,7 @@ static int32_t getColorFormat(const char* colorFormat) {
ALOGE("Uknown color format (%s), please add it to "
"CameraSource::getColorFormat", colorFormat);
- CHECK_EQ(0, "Unknown color format");
+ CHECK(!"Unknown color format");
}
CameraSource *CameraSource::Create() {
@@ -517,7 +517,7 @@ status_t CameraSource::initWithCameraAccess(
// This CHECK is good, since we just passed the lock/unlock
// check earlier by calling mCamera->setParameters().
- CHECK_EQ(OK, mCamera->setPreviewDisplay(mSurface));
+ CHECK_EQ((status_t)OK, mCamera->setPreviewDisplay(mSurface));
// By default, do not store metadata in video buffers
mIsMetaDataStoredInVideoBuffers = false;
@@ -548,7 +548,7 @@ status_t CameraSource::initWithCameraAccess(
CameraSource::~CameraSource() {
if (mStarted) {
- stop();
+ reset();
} else if (mInitCheck == OK) {
// Camera is initialized but because start() is never called,
// the lock on Camera is never released(). This makes sure
@@ -566,7 +566,8 @@ void CameraSource::startCameraRecording() {
if (mCameraFlags & FLAGS_HOT_CAMERA) {
mCamera->unlock();
mCamera.clear();
- CHECK_EQ(OK, mCameraRecordingProxy->startRecording(new ProxyListener(this)));
+ CHECK_EQ((status_t)OK,
+ mCameraRecordingProxy->startRecording(new ProxyListener(this)));
} else {
mCamera->setListener(new CameraSourceListener(this));
mCamera->startRecording();
@@ -632,8 +633,8 @@ void CameraSource::releaseCamera() {
mCameraFlags = 0;
}
-status_t CameraSource::stop() {
- ALOGD("stop: E");
+status_t CameraSource::reset() {
+ ALOGD("reset: E");
Mutex::Autolock autoLock(mLock);
mStarted = false;
mFrameAvailableCondition.signal();
@@ -670,7 +671,7 @@ status_t CameraSource::stop() {
}
CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
- ALOGD("stop: X");
+ ALOGD("reset: X");
return OK;
}
@@ -718,7 +719,7 @@ void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
return;
}
}
- CHECK_EQ(0, "signalBufferReturned: bogus buffer");
+ CHECK(!"signalBufferReturned: bogus buffer");
}
status_t CameraSource::read(
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 263ab50..26ce7ae 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -20,9 +20,9 @@
#include <binder/IPCThreadState.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/CameraSourceTimeLapse.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MetaData.h>
#include <camera/Camera.h>
#include <camera/CameraParameters.h>
@@ -87,6 +87,10 @@ CameraSourceTimeLapse::CameraSourceTimeLapse(
}
CameraSourceTimeLapse::~CameraSourceTimeLapse() {
+ if (mLastReadBufferCopy) {
+ mLastReadBufferCopy->release();
+ mLastReadBufferCopy = NULL;
+ }
}
void CameraSourceTimeLapse::startQuickReadReturns() {
@@ -204,15 +208,6 @@ status_t CameraSourceTimeLapse::read(
}
}
-void CameraSourceTimeLapse::stopCameraRecording() {
- ALOGV("stopCameraRecording");
- CameraSource::stopCameraRecording();
- if (mLastReadBufferCopy) {
- mLastReadBufferCopy->release();
- mLastReadBufferCopy = NULL;
- }
-}
-
sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy(
const sp<IMemory> &source_data) {
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
index 9452ab1..524c3aa 100644
--- a/media/libstagefright/DRMExtractor.cpp
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -23,6 +23,7 @@
#include <arpa/inet.h>
#include <utils/String8.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/Utils.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaSource.h>
@@ -30,7 +31,6 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
#include <drm/drm_framework_common.h>
#include <utils/Errors.h>
@@ -282,13 +282,13 @@ bool SniffDRM(
if (decryptHandle != NULL) {
if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) {
*mimeType = String8("drm+container_based+") + decryptHandle->mimeType;
+ *confidence = 10.0f;
} else if (decryptHandle->decryptApiType == DecryptApiType::ELEMENTARY_STREAM_BASED) {
*mimeType = String8("drm+es_based+") + decryptHandle->mimeType;
- } else if (decryptHandle->decryptApiType == DecryptApiType::WV_BASED) {
- *mimeType = MEDIA_MIMETYPE_CONTAINER_WVM;
- ALOGW("SniffWVM: found match\n");
+ *confidence = 10.0f;
+ } else {
+ return false;
}
- *confidence = 10.0f;
return true;
}
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 43539bb..d0a7880 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -15,6 +15,12 @@
*/
#include "include/AMRExtractor.h"
+#include "include/AVIExtractor.h"
+
+#if CHROMIUM_AVAILABLE
+#include "include/DataUriSource.h"
+#endif
+
#include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
@@ -26,6 +32,7 @@
#include "include/DRMExtractor.h"
#include "include/FLACExtractor.h"
#include "include/AACExtractor.h"
+#include "include/WVMExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -112,7 +119,9 @@ void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffMPEG2TS);
RegisterSniffer(SniffMP3);
RegisterSniffer(SniffAAC);
+ RegisterSniffer(SniffAVI);
RegisterSniffer(SniffMPEG2PS);
+ RegisterSniffer(SniffWVM);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
@@ -134,6 +143,10 @@ sp<DataSource> DataSource::CreateFromURI(
return NULL;
}
source = new NuCachedSource2(httpSource);
+# if CHROMIUM_AVAILABLE
+ } else if (!strncasecmp("data:", uri, 5)) {
+ source = new DataUriSource(uri);
+#endif
} else {
// Assume it's a filename.
source = new FileSource(uri);
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index 73cb48c..73c8d03 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaDebug.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
@@ -127,7 +127,7 @@ status_t FileSource::getSize(off64_t *size) {
return OK;
}
-sp<DecryptHandle> FileSource::DrmInitialization() {
+sp<DecryptHandle> FileSource::DrmInitialization(const char *mime) {
if (mDrmManagerClient == NULL) {
mDrmManagerClient = new DrmManagerClient();
}
@@ -138,7 +138,7 @@ sp<DecryptHandle> FileSource::DrmInitialization() {
if (mDecryptHandle == NULL) {
mDecryptHandle = mDrmManagerClient->openDecryptSession(
- mFd, mOffset, mLength);
+ mFd, mOffset, mLength, mime);
}
if (mDecryptHandle == NULL) {
diff --git a/media/libstagefright/JPEGSource.cpp b/media/libstagefright/JPEGSource.cpp
index e818115..bafa4b2 100644
--- a/media/libstagefright/JPEGSource.cpp
+++ b/media/libstagefright/JPEGSource.cpp
@@ -18,10 +18,10 @@
#define LOG_TAG "JPEGSource"
#include <utils/Log.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/JPEGSource.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -59,7 +59,7 @@ JPEGSource::JPEGSource(const sp<DataSource> &source)
mWidth(0),
mHeight(0),
mOffset(0) {
- CHECK_EQ(parseJPEG(), OK);
+ CHECK_EQ(parseJPEG(), (status_t)OK);
CHECK(mSource->getSize(&mSize) == OK);
}
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 36009ab..f702376 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -244,7 +244,7 @@ void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kNotifyBuffer);
- notify->setObject("buffer", out);
+ notify->setBuffer("buffer", out);
notify->setInt32("oob", true);
notify->post();
}
@@ -270,7 +270,7 @@ void MPEG2TSWriter::SourceInfo::postAVCFrame(MediaBuffer *buffer) {
copy->meta()->setInt32("isSync", true);
}
- notify->setObject("buffer", copy);
+ notify->setBuffer("buffer", copy);
notify->post();
}
@@ -351,7 +351,7 @@ bool MPEG2TSWriter::SourceInfo::flushAACFrames() {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kNotifyBuffer);
- notify->setObject("buffer", mAACBuffer);
+ notify->setBuffer("buffer", mAACBuffer);
notify->post();
mAACBuffer.clear();
@@ -513,7 +513,7 @@ void MPEG2TSWriter::init() {
MPEG2TSWriter::~MPEG2TSWriter() {
if (mStarted) {
- stop();
+ reset();
}
mLooper->unregisterHandler(mReflector->id());
@@ -564,7 +564,7 @@ status_t MPEG2TSWriter::start(MetaData *param) {
return OK;
}
-status_t MPEG2TSWriter::stop() {
+status_t MPEG2TSWriter::reset() {
CHECK(mStarted);
for (size_t i = 0; i < mSources.size(); ++i) {
@@ -614,10 +614,8 @@ void MPEG2TSWriter::onMessageReceived(const sp<AMessage> &msg) {
++mNumSourcesDone;
} else if (what == SourceInfo::kNotifyBuffer) {
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
-
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
int32_t oob;
if (msg->findInt32("oob", &oob) && oob) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 22bdd95..6c95d4e 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -20,7 +20,6 @@
#include "include/MPEG4Extractor.h"
#include "include/SampleTable.h"
#include "include/ESDS.h"
-#include "timedtext/TimedTextPlayer.h"
#include <arpa/inet.h>
@@ -1372,8 +1371,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
uint32_t type = ntohl(buffer);
// For the 3GPP file format, the handler-type within the 'hdlr' box
- // shall be 'text'
- if (type == FOURCC('t', 'e', 'x', 't')) {
+ // shall be 'text'. We also want to support 'sbtl' handler type
+ // for a practical reason as various MPEG4 containers use it.
+ if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
}
@@ -2429,4 +2429,3 @@ bool SniffMPEG4(
}
} // namespace android
-
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 06dd875..7ebbe1d 100755
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -23,10 +23,10 @@
#include <pthread.h>
#include <sys/prctl.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -70,6 +70,10 @@ public:
status_t dump(int fd, const Vector<String16>& args) const;
private:
+ enum {
+ kMaxCttsOffsetTimeUs = 1000000LL, // 1 second
+ };
+
MPEG4Writer *mOwner;
sp<MetaData> mMeta;
sp<MediaSource> mSource;
@@ -137,11 +141,12 @@ private:
: sampleCount(count), sampleDuration(timescaledDur) {}
uint32_t sampleCount;
- int32_t sampleDuration; // time scale based
+ uint32_t sampleDuration; // time scale based
};
- bool mHasNegativeCttsDeltaDuration;
size_t mNumCttsTableEntries;
List<CttsTableEntry> mCttsTableEntries;
+ int64_t mMinCttsOffsetTimeUs;
+ int64_t mMaxCttsOffsetTimeUs;
// Sequence parameter set or picture parameter set
struct AVCParamSet {
@@ -172,6 +177,8 @@ private:
// Update the audio track's drift information.
void updateDriftTime(const sp<MetaData>& meta);
+ int32_t getStartTimeOffsetScaledTime() const;
+
static void *ThreadWrapper(void *me);
status_t threadEntry();
@@ -282,7 +289,7 @@ MPEG4Writer::MPEG4Writer(int fd)
}
MPEG4Writer::~MPEG4Writer() {
- stop();
+ reset();
while (!mTracks.empty()) {
List<Track *>::iterator it = mTracks.begin();
@@ -471,7 +478,7 @@ status_t MPEG4Writer::start(MetaData *param) {
!param->findInt32(kKeyTimeScale, &mTimeScale)) {
mTimeScale = 1000;
}
- CHECK(mTimeScale > 0);
+ CHECK_GT(mTimeScale, 0);
ALOGV("movie time scale: %d", mTimeScale);
mStreamableFile = true;
@@ -490,7 +497,7 @@ status_t MPEG4Writer::start(MetaData *param) {
}
mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
}
- CHECK(mEstimatedMoovBoxSize >= 8);
+ CHECK_GE(mEstimatedMoovBoxSize, 8);
lseek64(mFd, mFreeBoxOffset, SEEK_SET);
writeInt32(mEstimatedMoovBoxSize);
write("free", 4);
@@ -616,7 +623,7 @@ void MPEG4Writer::release() {
mStarted = false;
}
-status_t MPEG4Writer::stop() {
+status_t MPEG4Writer::reset() {
if (mInitCheck != OK) {
return OK;
} else {
@@ -684,7 +691,7 @@ status_t MPEG4Writer::stop() {
mWriteMoovBoxToMemory = false;
if (mStreamableFile) {
- CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
+ CHECK_LE(mMoovBoxBufferOffset + 8, mEstimatedMoovBoxSize);
// Moov box
lseek64(mFd, mFreeBoxOffset, SEEK_SET);
@@ -856,7 +863,7 @@ off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
mOffset += length + 4;
} else {
- CHECK(length < 65536);
+ CHECK_LT(length, 65536);
uint8_t x = length >> 8;
::write(mFd, &x, 1);
@@ -1085,7 +1092,7 @@ bool MPEG4Writer::reachedEOS() {
void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
ALOGI("setStartTimestampUs: %lld", timeUs);
- CHECK(timeUs >= 0);
+ CHECK_GE(timeUs, 0ll);
Mutex::Autolock autoLock(mLock);
if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
mStartTimestampUs = timeUs;
@@ -1186,9 +1193,6 @@ void MPEG4Writer::Track::addOneCttsTableEntry(
if (mIsAudio) {
return;
}
- if (duration < 0 && !mHasNegativeCttsDeltaDuration) {
- mHasNegativeCttsDeltaDuration = true;
- }
CttsTableEntry cttsEntry(sampleCount, duration);
mCttsTableEntries.push_back(cttsEntry);
++mNumCttsTableEntries;
@@ -1218,7 +1222,7 @@ void MPEG4Writer::Track::setTimeScale() {
mTimeScale = timeScale;
}
- CHECK(mTimeScale > 0);
+ CHECK_GT(mTimeScale, 0);
}
void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
@@ -1299,7 +1303,7 @@ void MPEG4Writer::bufferChunk(const Chunk& chunk) {
}
}
- CHECK("Received a chunk for a unknown track" == 0);
+ CHECK(!"Received a chunk for a unknown track");
}
void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
@@ -1509,7 +1513,6 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
mMdatSizeBytes = 0;
mMaxChunkDurationUs = 0;
- mHasNegativeCttsDeltaDuration = false;
pthread_create(&mThread, &attr, ThreadWrapper, this);
pthread_attr_destroy(&attr);
@@ -1833,29 +1836,18 @@ status_t MPEG4Writer::Track::threadEntry() {
int32_t nChunks = 0;
int32_t nZeroLengthFrames = 0;
int64_t lastTimestampUs = 0; // Previous sample time stamp
- int64_t lastCttsTimeUs = 0; // Previous sample time stamp
int64_t lastDurationUs = 0; // Between the previous two samples
int64_t currDurationTicks = 0; // Timescale based ticks
int64_t lastDurationTicks = 0; // Timescale based ticks
int32_t sampleCount = 1; // Sample count in the current stts table entry
- int64_t currCttsDurTicks = 0; // Timescale based ticks
- int64_t lastCttsDurTicks = 0; // Timescale based ticks
- int32_t cttsSampleCount = 1; // Sample count in the current ctts table entry
- uint32_t previousSampleSize = 0; // Size of the previous sample
+ uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0;
int64_t timestampUs = 0;
- int64_t cttsDeltaTimeUs = 0;
- bool hasBFrames = false;
+ int64_t cttsOffsetTimeUs = 0;
+ int64_t currCttsOffsetTimeTicks = 0; // Timescale based ticks
+ int64_t lastCttsOffsetTimeTicks = -1; // Timescale based ticks
+ int32_t cttsSampleCount = 0; // Sample count in the current ctts table entry
-#if 1
- // XXX: Samsung's video encoder's output buffer timestamp
- // is not correct. see bug 4724339
- char value[PROPERTY_VALUE_MAX];
- if (property_get("rw.media.record.hasb", value, NULL) &&
- (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
- hasBFrames = true;
- }
-#endif
if (mIsAudio) {
prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
} else {
@@ -1897,7 +1889,7 @@ status_t MPEG4Writer::Track::threadEntry() {
(const uint8_t *)buffer->data()
+ buffer->range_offset(),
buffer->range_length());
- CHECK_EQ(OK, err);
+ CHECK_EQ((status_t)OK, err);
} else if (mIsMPEG4) {
mCodecSpecificDataSize = buffer->range_length();
mCodecSpecificData = malloc(mCodecSpecificDataSize);
@@ -1963,32 +1955,64 @@ status_t MPEG4Writer::Track::threadEntry() {
if (mResumed) {
int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
- CHECK(durExcludingEarlierPausesUs >= 0);
+ CHECK_GE(durExcludingEarlierPausesUs, 0ll);
int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
- CHECK(pausedDurationUs >= lastDurationUs);
+ CHECK_GE(pausedDurationUs, lastDurationUs);
previousPausedDurationUs += pausedDurationUs - lastDurationUs;
mResumed = false;
}
timestampUs -= previousPausedDurationUs;
- CHECK(timestampUs >= 0);
- if (!mIsAudio && hasBFrames) {
+ CHECK_GE(timestampUs, 0ll);
+ if (!mIsAudio) {
/*
* Composition time: timestampUs
* Decoding time: decodingTimeUs
- * Composition time delta = composition time - decoding time
- *
- * We save picture decoding time stamp delta in stts table entries,
- * and composition time delta duration in ctts table entries.
+ * Composition time offset = composition time - decoding time
*/
int64_t decodingTimeUs;
CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
decodingTimeUs -= previousPausedDurationUs;
- int64_t timeUs = decodingTimeUs;
- cttsDeltaTimeUs = timestampUs - decodingTimeUs;
+ cttsOffsetTimeUs =
+ timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
+ CHECK_GE(cttsOffsetTimeUs, 0ll);
timestampUs = decodingTimeUs;
- ALOGV("decoding time: %lld and ctts delta time: %lld",
- timestampUs, cttsDeltaTimeUs);
+ ALOGV("decoding time: %lld and ctts offset time: %lld",
+ timestampUs, cttsOffsetTimeUs);
+
+ // Update ctts box table if necessary
+ currCttsOffsetTimeTicks =
+ (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL;
+ CHECK_LE(currCttsOffsetTimeTicks, 0x0FFFFFFFFLL);
+ if (mNumSamples == 0) {
+ // Force the first ctts table entry to have one single entry
+ // so that we can do adjustment for the initial track start
+ // time offset easily in writeCttsBox().
+ lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
+ addOneCttsTableEntry(1, currCttsOffsetTimeTicks);
+ cttsSampleCount = 0; // No sample in ctts box is pending
+ } else {
+ if (currCttsOffsetTimeTicks != lastCttsOffsetTimeTicks) {
+ addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+ lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
+ cttsSampleCount = 1; // One sample in ctts box is pending
+ } else {
+ ++cttsSampleCount;
+ }
+ }
+
+ // Update ctts time offset range
+ if (mNumSamples == 0) {
+ mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+ mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+ } else {
+ if (currCttsOffsetTimeTicks > mMaxCttsOffsetTimeUs) {
+ mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+ } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTimeUs) {
+ mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+ }
+ }
+
}
if (mIsRealTimeRecording) {
@@ -1997,7 +2021,7 @@ status_t MPEG4Writer::Track::threadEntry() {
}
}
- CHECK(timestampUs >= 0);
+ CHECK_GE(timestampUs, 0ll);
ALOGV("%s media time stamp: %lld and previous paused duration %lld",
mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
if (timestampUs > mTrackDurationUs) {
@@ -2012,6 +2036,7 @@ status_t MPEG4Writer::Track::threadEntry() {
currDurationTicks =
((timestampUs * mTimeScale + 500000LL) / 1000000LL -
(lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+ CHECK_GE(currDurationTicks, 0ll);
mSampleSizes.push_back(sampleSize);
++mNumSamples;
@@ -2020,25 +2045,12 @@ status_t MPEG4Writer::Track::threadEntry() {
// Force the first sample to have its own stts entry so that
// we can adjust its value later to maintain the A/V sync.
if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) {
- ALOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us",
- mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks);
addOneSttsTableEntry(sampleCount, lastDurationTicks);
sampleCount = 1;
} else {
++sampleCount;
}
- if (!mIsAudio) {
- currCttsDurTicks =
- ((cttsDeltaTimeUs * mTimeScale + 500000LL) / 1000000LL -
- (lastCttsTimeUs * mTimeScale + 500000LL) / 1000000LL);
- if (currCttsDurTicks != lastCttsDurTicks) {
- addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
- cttsSampleCount = 1;
- } else {
- ++cttsSampleCount;
- }
- }
}
if (mSamplesHaveSameSize) {
if (mNumSamples >= 2 && previousSampleSize != sampleSize) {
@@ -2052,11 +2064,6 @@ status_t MPEG4Writer::Track::threadEntry() {
lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
- if (!mIsAudio) {
- lastCttsDurTicks = currCttsDurTicks;
- lastCttsTimeUs = cttsDeltaTimeUs;
- }
-
if (isSync != 0) {
addOneStssTableEntry(mNumSamples);
}
@@ -2125,10 +2132,8 @@ status_t MPEG4Writer::Track::threadEntry() {
if (mNumSamples == 1) {
lastDurationUs = 0; // A single sample's duration
lastDurationTicks = 0;
- lastCttsDurTicks = 0;
} else {
++sampleCount; // Count for the last sample
- ++cttsSampleCount;
}
if (mNumSamples <= 2) {
@@ -2140,7 +2145,14 @@ status_t MPEG4Writer::Track::threadEntry() {
addOneSttsTableEntry(sampleCount, lastDurationTicks);
}
- addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
+ // The last ctts box may not have been written yet, and this
+ // is to make sure that we write out the last ctts box.
+ if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
+ if (cttsSampleCount > 0) {
+ addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+ }
+ }
+
mTrackDurationUs += lastDurationUs;
mReachedEOS = true;
@@ -2406,7 +2418,7 @@ void MPEG4Writer::Track::writeVideoFourCCBox() {
mOwner->writeInt16(0x18); // depth
mOwner->writeInt16(-1); // predefined
- CHECK(23 + mCodecSpecificDataSize < 128);
+ CHECK_LT(23 + mCodecSpecificDataSize, 128);
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
writeMp4vEsdsBox();
@@ -2465,10 +2477,10 @@ void MPEG4Writer::Track::writeAudioFourCCBox() {
void MPEG4Writer::Track::writeMp4aEsdsBox() {
mOwner->beginBox("esds");
CHECK(mCodecSpecificData);
- CHECK(mCodecSpecificDataSize > 0);
+ CHECK_GT(mCodecSpecificDataSize, 0);
// Make sure all sizes encode to a single byte.
- CHECK(mCodecSpecificDataSize + 23 < 128);
+ CHECK_LT(mCodecSpecificDataSize + 23, 128);
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt8(0x03); // ES_DescrTag
@@ -2502,7 +2514,7 @@ void MPEG4Writer::Track::writeMp4aEsdsBox() {
void MPEG4Writer::Track::writeMp4vEsdsBox() {
CHECK(mCodecSpecificData);
- CHECK(mCodecSpecificDataSize > 0);
+ CHECK_GT(mCodecSpecificDataSize, 0);
mOwner->beginBox("esds");
mOwner->writeInt32(0); // version=0, flags=0
@@ -2662,7 +2674,7 @@ void MPEG4Writer::Track::writeDinfBox() {
void MPEG4Writer::Track::writeAvccBox() {
CHECK(mCodecSpecificData);
- CHECK(mCodecSpecificDataSize >= 5);
+ CHECK_GE(mCodecSpecificDataSize, 5);
// Patch avcc's lengthSize field to match the number
// of bytes we use to indicate the size of a nal unit.
@@ -2690,23 +2702,26 @@ void MPEG4Writer::Track::writePaspBox() {
mOwner->endBox(); // pasp
}
+int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const {
+ int64_t trackStartTimeOffsetUs = 0;
+ int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
+ if (mStartTimestampUs != moovStartTimeUs) {
+ CHECK_GT(mStartTimestampUs, moovStartTimeUs);
+ trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
+ }
+ return (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL;
+}
+
void MPEG4Writer::Track::writeSttsBox() {
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mNumSttsTableEntries);
// Compensate for small start time difference from different media tracks
- int64_t trackStartTimeOffsetUs = 0;
- int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
- if (mStartTimestampUs != moovStartTimeUs) {
- CHECK(mStartTimestampUs > moovStartTimeUs);
- trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
- }
List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
CHECK(it != mSttsTableEntries.end() && it->sampleCount == 1);
mOwner->writeInt32(it->sampleCount);
- int32_t dur = (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL;
- mOwner->writeInt32(dur + it->sampleDuration);
+ mOwner->writeInt32(getStartTimeOffsetScaledTime() + it->sampleDuration);
int64_t totalCount = 1;
while (++it != mSttsTableEntries.end()) {
@@ -2714,7 +2729,7 @@ void MPEG4Writer::Track::writeSttsBox() {
mOwner->writeInt32(it->sampleDuration);
totalCount += it->sampleCount;
}
- CHECK(totalCount == mNumSamples);
+ CHECK_EQ(totalCount, mNumSamples);
mOwner->endBox(); // stts
}
@@ -2723,6 +2738,11 @@ void MPEG4Writer::Track::writeCttsBox() {
return;
}
+ // There is no B frame at all
+ if (mMinCttsOffsetTimeUs == mMaxCttsOffsetTimeUs) {
+ return;
+ }
+
// Do not write ctts box when there is no need to have it.
if ((mNumCttsTableEntries == 1 &&
mCttsTableEntries.begin()->sampleDuration == 0) ||
@@ -2730,24 +2750,29 @@ void MPEG4Writer::Track::writeCttsBox() {
return;
}
- ALOGV("ctts box has %d entries", mNumCttsTableEntries);
+ ALOGD("ctts box has %d entries with range [%lld, %lld]",
+ mNumCttsTableEntries, mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs);
mOwner->beginBox("ctts");
- if (mHasNegativeCttsDeltaDuration) {
- mOwner->writeInt32(0x00010000); // version=1, flags=0
- } else {
- mOwner->writeInt32(0); // version=0, flags=0
- }
+ // Version 1 allows to use negative offset time value, but
+ // we are sticking to version 0 for now.
+ mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mNumCttsTableEntries);
- int64_t totalCount = 0;
- for (List<CttsTableEntry>::iterator it = mCttsTableEntries.begin();
- it != mCttsTableEntries.end(); ++it) {
+ // Compensate for small start time difference from different media tracks
+ List<CttsTableEntry>::iterator it = mCttsTableEntries.begin();
+ CHECK(it != mCttsTableEntries.end() && it->sampleCount == 1);
+ mOwner->writeInt32(it->sampleCount);
+ mOwner->writeInt32(getStartTimeOffsetScaledTime() +
+ it->sampleDuration - mMinCttsOffsetTimeUs);
+
+ int64_t totalCount = 1;
+ while (++it != mCttsTableEntries.end()) {
mOwner->writeInt32(it->sampleCount);
- mOwner->writeInt32(it->sampleDuration);
+ mOwner->writeInt32(it->sampleDuration - mMinCttsOffsetTimeUs);
totalCount += it->sampleCount;
}
- CHECK(totalCount == mNumSamples);
+ CHECK_EQ(totalCount, mNumSamples);
mOwner->endBox(); // ctts
}
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index 96271e4..11b80bf 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -22,8 +22,8 @@
#include <stdlib.h>
#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MetaData.h>
#include <ui/GraphicBuffer.h>
@@ -157,7 +157,7 @@ void MediaBuffer::reset() {
}
MediaBuffer::~MediaBuffer() {
- CHECK_EQ(mObserver, NULL);
+ CHECK(mObserver == NULL);
if (mOwnsData && mData != NULL) {
free(mData);
@@ -188,7 +188,7 @@ int MediaBuffer::refcount() const {
}
MediaBuffer *MediaBuffer::clone() {
- CHECK_EQ(mGraphicBuffer, NULL);
+ CHECK(mGraphicBuffer == NULL);
MediaBuffer *buffer = new MediaBuffer(mData, mSize);
buffer->set_range(mRangeOffset, mRangeLength);
diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
index c8d05f4..80aae51 100644
--- a/media/libstagefright/MediaBufferGroup.cpp
+++ b/media/libstagefright/MediaBufferGroup.cpp
@@ -17,9 +17,9 @@
#define LOG_TAG "MediaBufferGroup"
#include <utils/Log.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
namespace android {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
new file mode 100644
index 0000000..e14b1c4
--- /dev/null
+++ b/media/libstagefright/MediaCodec.cpp
@@ -0,0 +1,1179 @@
+/*
+ * Copyright 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 "MediaCodec"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaCodec.h>
+
+#include "include/SoftwareRenderer.h"
+
+#include <gui/SurfaceTextureClient.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/NativeWindowWrapper.h>
+
+namespace android {
+
+// static
+sp<MediaCodec> MediaCodec::CreateByType(
+ const sp<ALooper> &looper, const char *mime, bool encoder) {
+ sp<MediaCodec> codec = new MediaCodec(looper);
+ if (codec->init(mime, true /* nameIsType */, encoder) != OK) {
+ return NULL;
+ }
+
+ return codec;
+}
+
+// static
+sp<MediaCodec> MediaCodec::CreateByComponentName(
+ const sp<ALooper> &looper, const char *name) {
+ sp<MediaCodec> codec = new MediaCodec(looper);
+ if (codec->init(name, false /* nameIsType */, false /* encoder */) != OK) {
+ return NULL;
+ }
+
+ return codec;
+}
+
+MediaCodec::MediaCodec(const sp<ALooper> &looper)
+ : mState(UNINITIALIZED),
+ mLooper(looper),
+ mCodec(new ACodec),
+ mFlags(0),
+ mSoftRenderer(NULL),
+ mDequeueInputTimeoutGeneration(0),
+ mDequeueInputReplyID(0),
+ mDequeueOutputTimeoutGeneration(0),
+ mDequeueOutputReplyID(0) {
+}
+
+MediaCodec::~MediaCodec() {
+ CHECK_EQ(mState, UNINITIALIZED);
+}
+
+// static
+status_t MediaCodec::PostAndAwaitResponse(
+ const sp<AMessage> &msg, sp<AMessage> *response) {
+ status_t err = msg->postAndAwaitResponse(response);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (!(*response)->findInt32("err", &err)) {
+ err = OK;
+ }
+
+ return err;
+}
+
+status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
+ // Current video decoders do not return from OMX_FillThisBuffer
+ // quickly, violating the OpenMAX specs, until that is remedied
+ // we need to invest in an extra looper to free the main event
+ // queue.
+ bool needDedicatedLooper = false;
+ if (nameIsType && !strncasecmp(name, "video/", 6)) {
+ needDedicatedLooper = true;
+ } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) {
+ needDedicatedLooper = true;
+ }
+
+ if (needDedicatedLooper) {
+ if (mCodecLooper == NULL) {
+ mCodecLooper = new ALooper;
+ mCodecLooper->setName("CodecLooper");
+ mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ }
+
+ mCodecLooper->registerHandler(mCodec);
+ } else {
+ mLooper->registerHandler(mCodec);
+ }
+
+ mLooper->registerHandler(this);
+
+ mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id()));
+
+ sp<AMessage> msg = new AMessage(kWhatInit, id());
+ msg->setString("name", name);
+ msg->setInt32("nameIsType", nameIsType);
+
+ if (nameIsType) {
+ msg->setInt32("encoder", encoder);
+ }
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::configure(
+ const sp<AMessage> &format,
+ const sp<SurfaceTextureClient> &nativeWindow,
+ uint32_t flags) {
+ sp<AMessage> msg = new AMessage(kWhatConfigure, id());
+
+ msg->setMessage("format", format);
+ msg->setInt32("flags", flags);
+
+ if (nativeWindow != NULL) {
+ if (!(mFlags & kFlagIsSoftwareCodec)) {
+ msg->setObject(
+ "native-window",
+ new NativeWindowWrapper(nativeWindow));
+ } else {
+ mNativeWindow = nativeWindow;
+ }
+ }
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::start() {
+ sp<AMessage> msg = new AMessage(kWhatStart, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::stop() {
+ sp<AMessage> msg = new AMessage(kWhatStop, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::queueInputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t presentationTimeUs,
+ uint32_t flags) {
+ sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id());
+ msg->setSize("index", index);
+ msg->setSize("offset", offset);
+ msg->setSize("size", size);
+ msg->setInt64("timeUs", presentationTimeUs);
+ msg->setInt32("flags", flags);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+ sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
+ msg->setInt64("timeoutUs", timeoutUs);
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ CHECK(response->findSize("index", index));
+
+ return OK;
+}
+
+status_t MediaCodec::dequeueOutputBuffer(
+ size_t *index,
+ size_t *offset,
+ size_t *size,
+ int64_t *presentationTimeUs,
+ uint32_t *flags,
+ int64_t timeoutUs) {
+ sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, id());
+ msg->setInt64("timeoutUs", timeoutUs);
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ CHECK(response->findSize("index", index));
+ CHECK(response->findSize("offset", offset));
+ CHECK(response->findSize("size", size));
+ CHECK(response->findInt64("timeUs", presentationTimeUs));
+ CHECK(response->findInt32("flags", (int32_t *)flags));
+
+ return OK;
+}
+
+status_t MediaCodec::renderOutputBufferAndRelease(size_t index) {
+ sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+ msg->setSize("index", index);
+ msg->setInt32("render", true);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::releaseOutputBuffer(size_t index) {
+ sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+ msg->setSize("index", index);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
+ sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id());
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ CHECK(response->findMessage("format", format));
+
+ return OK;
+}
+
+status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+ msg->setInt32("portIndex", kPortIndexInput);
+ msg->setPointer("buffers", buffers);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::getOutputBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+ msg->setInt32("portIndex", kPortIndexOutput);
+ msg->setPointer("buffers", buffers);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::flush() {
+ sp<AMessage> msg = new AMessage(kWhatFlush, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void MediaCodec::cancelPendingDequeueOperations() {
+ if (mFlags & kFlagDequeueInputPending) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+ response->postReply(mDequeueInputReplyID);
+
+ ++mDequeueInputTimeoutGeneration;
+ mDequeueInputReplyID = 0;
+ mFlags &= ~kFlagDequeueInputPending;
+ }
+
+ if (mFlags & kFlagDequeueOutputPending) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+ response->postReply(mDequeueOutputReplyID);
+
+ ++mDequeueOutputTimeoutGeneration;
+ mDequeueOutputReplyID = 0;
+ mFlags &= ~kFlagDequeueOutputPending;
+ }
+}
+
+bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) {
+ if (mState != STARTED
+ || (mFlags & kFlagStickyError)
+ || (newRequest && (mFlags & kFlagDequeueInputPending))) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+
+ return true;
+ }
+
+ ssize_t index = dequeuePortBuffer(kPortIndexInput);
+
+ if (index < 0) {
+ CHECK_EQ(index, -EAGAIN);
+ return false;
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setSize("index", index);
+ response->postReply(replyID);
+
+ return true;
+}
+
+bool MediaCodec::handleDequeueOutputBuffer(uint32_t replyID, bool newRequest) {
+ sp<AMessage> response = new AMessage;
+
+ if (mState != STARTED
+ || (mFlags & kFlagStickyError)
+ || (newRequest && (mFlags & kFlagDequeueOutputPending))) {
+ response->setInt32("err", INVALID_OPERATION);
+ } else if (mFlags & kFlagOutputBuffersChanged) {
+ response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED);
+ mFlags &= ~kFlagOutputBuffersChanged;
+ } else if (mFlags & kFlagOutputFormatChanged) {
+ response->setInt32("err", INFO_FORMAT_CHANGED);
+ mFlags &= ~kFlagOutputFormatChanged;
+ } else {
+ ssize_t index = dequeuePortBuffer(kPortIndexOutput);
+
+ if (index < 0) {
+ CHECK_EQ(index, -EAGAIN);
+ return false;
+ }
+
+ const sp<ABuffer> &buffer =
+ mPortBuffers[kPortIndexOutput].itemAt(index).mData;
+
+ response->setSize("index", index);
+ response->setSize("offset", buffer->offset());
+ response->setSize("size", buffer->size());
+
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ response->setInt64("timeUs", timeUs);
+
+ int32_t omxFlags;
+ CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags));
+
+ uint32_t flags = 0;
+ if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+ flags |= BUFFER_FLAG_SYNCFRAME;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ flags |= BUFFER_FLAG_CODECCONFIG;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_EOS) {
+ flags |= BUFFER_FLAG_EOS;
+ }
+
+ response->setInt32("flags", flags);
+ }
+
+ response->postReply(replyID);
+
+ return true;
+}
+
+void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatCodecNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ switch (what) {
+ case ACodec::kWhatError:
+ {
+ int32_t omxError, internalError;
+ CHECK(msg->findInt32("omx-error", &omxError));
+ CHECK(msg->findInt32("err", &internalError));
+
+ ALOGE("Codec reported an error. "
+ "(omx error 0x%08x, internalError %d)",
+ omxError, internalError);
+
+ bool sendErrorReponse = true;
+
+ switch (mState) {
+ case INITIALIZING:
+ {
+ setState(UNINITIALIZED);
+ break;
+ }
+
+ case CONFIGURING:
+ {
+ setState(INITIALIZED);
+ break;
+ }
+
+ case STARTING:
+ {
+ setState(CONFIGURED);
+ break;
+ }
+
+ case STOPPING:
+ {
+ // Ignore the error, assuming we'll still get
+ // the shutdown complete notification.
+
+ sendErrorReponse = false;
+ break;
+ }
+
+ case FLUSHING:
+ {
+ setState(STARTED);
+ break;
+ }
+
+ case STARTED:
+ {
+ sendErrorReponse = false;
+
+ mFlags |= kFlagStickyError;
+
+ cancelPendingDequeueOperations();
+ break;
+ }
+
+ default:
+ {
+ sendErrorReponse = false;
+
+ mFlags |= kFlagStickyError;
+ break;
+ }
+ }
+
+ if (sendErrorReponse) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", UNKNOWN_ERROR);
+
+ response->postReply(mReplyID);
+ }
+ break;
+ }
+
+ case ACodec::kWhatComponentAllocated:
+ {
+ CHECK_EQ(mState, INITIALIZING);
+ setState(INITIALIZED);
+
+ AString componentName;
+ CHECK(msg->findString("componentName", &componentName));
+
+ if (componentName.startsWith("OMX.google.")) {
+ mFlags |= kFlagIsSoftwareCodec;
+ } else {
+ mFlags &= ~kFlagIsSoftwareCodec;
+ }
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ case ACodec::kWhatComponentConfigured:
+ {
+ CHECK_EQ(mState, CONFIGURING);
+ setState(CONFIGURED);
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ case ACodec::kWhatBuffersAllocated:
+ {
+ int32_t portIndex;
+ CHECK(msg->findInt32("portIndex", &portIndex));
+
+ ALOGV("%s buffers allocated",
+ portIndex == kPortIndexInput ? "input" : "output");
+
+ CHECK(portIndex == kPortIndexInput
+ || portIndex == kPortIndexOutput);
+
+ mPortBuffers[portIndex].clear();
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+ for (size_t i = 0;; ++i) {
+ AString name = StringPrintf("buffer-id_%d", i);
+
+ void *bufferID;
+ if (!msg->findPointer(name.c_str(), &bufferID)) {
+ break;
+ }
+
+ name = StringPrintf("data_%d", i);
+
+ BufferInfo info;
+ info.mBufferID = bufferID;
+ info.mOwnedByClient = false;
+ CHECK(msg->findBuffer(name.c_str(), &info.mData));
+
+ buffers->push_back(info);
+ }
+
+ if (portIndex == kPortIndexOutput) {
+ if (mState == STARTING) {
+ // We're always allocating output buffers after
+ // allocating input buffers, so this is a good
+ // indication that now all buffers are allocated.
+ setState(STARTED);
+ (new AMessage)->postReply(mReplyID);
+ } else {
+ mFlags |= kFlagOutputBuffersChanged;
+ }
+ }
+ break;
+ }
+
+ case ACodec::kWhatOutputFormatChanged:
+ {
+ ALOGV("codec output format changed");
+
+ if ((mFlags & kFlagIsSoftwareCodec)
+ && mNativeWindow != NULL) {
+ AString mime;
+ CHECK(msg->findString("mime", &mime));
+
+ if (!strncasecmp("video/", mime.c_str(), 6)) {
+ delete mSoftRenderer;
+ mSoftRenderer = NULL;
+
+ int32_t width, height;
+ CHECK(msg->findInt32("width", &width));
+ CHECK(msg->findInt32("height", &height));
+
+ int32_t colorFormat;
+ CHECK(msg->findInt32(
+ "color-format", &colorFormat));
+
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+ meta->setInt32(kKeyColorFormat, colorFormat);
+
+ mSoftRenderer =
+ new SoftwareRenderer(mNativeWindow, meta);
+ }
+ }
+
+ mOutputFormat = msg;
+ mFlags |= kFlagOutputFormatChanged;
+ break;
+ }
+
+ case ACodec::kWhatFillThisBuffer:
+ {
+ /* size_t index = */updateBuffers(kPortIndexInput, msg);
+
+ if (mState == FLUSHING || mState == STOPPING) {
+ returnBuffersToCodecOnPort(kPortIndexInput);
+ break;
+ }
+
+ if (mFlags & kFlagDequeueInputPending) {
+ CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));
+
+ ++mDequeueInputTimeoutGeneration;
+ mFlags &= ~kFlagDequeueInputPending;
+ mDequeueInputReplyID = 0;
+ }
+ break;
+ }
+
+ case ACodec::kWhatDrainThisBuffer:
+ {
+ /* size_t index = */updateBuffers(kPortIndexOutput, msg);
+
+ if (mState == FLUSHING || mState == STOPPING) {
+ returnBuffersToCodecOnPort(kPortIndexOutput);
+ break;
+ }
+
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
+
+ int32_t omxFlags;
+ CHECK(msg->findInt32("flags", &omxFlags));
+
+ buffer->meta()->setInt32("omxFlags", omxFlags);
+
+ if (mFlags & kFlagDequeueOutputPending) {
+ CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID));
+
+ ++mDequeueOutputTimeoutGeneration;
+ mFlags &= ~kFlagDequeueOutputPending;
+ mDequeueOutputReplyID = 0;
+ }
+ break;
+ }
+
+ case ACodec::kWhatEOS:
+ {
+ // We already notify the client of this by using the
+ // corresponding flag in "onOutputBufferReady".
+ break;
+ }
+
+ case ACodec::kWhatShutdownCompleted:
+ {
+ CHECK_EQ(mState, STOPPING);
+ setState(UNINITIALIZED);
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ case ACodec::kWhatFlushCompleted:
+ {
+ CHECK_EQ(mState, FLUSHING);
+ setState(STARTED);
+
+ mCodec->signalResume();
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ break;
+ }
+
+ case kWhatInit:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != UNINITIALIZED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(INITIALIZING);
+
+ AString name;
+ CHECK(msg->findString("name", &name));
+
+ int32_t nameIsType;
+ int32_t encoder = false;
+ CHECK(msg->findInt32("nameIsType", &nameIsType));
+ if (nameIsType) {
+ CHECK(msg->findInt32("encoder", &encoder));
+ }
+
+ sp<AMessage> format = new AMessage;
+
+ if (nameIsType) {
+ format->setString("mime", name.c_str());
+ format->setInt32("encoder", encoder);
+ } else {
+ format->setString("componentName", name.c_str());
+ }
+
+ mCodec->initiateAllocateComponent(format);
+ break;
+ }
+
+ case kWhatConfigure:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != INITIALIZED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(CONFIGURING);
+
+ sp<RefBase> obj;
+ if (!msg->findObject("native-window", &obj)) {
+ obj.clear();
+ }
+
+ sp<AMessage> format;
+ CHECK(msg->findMessage("format", &format));
+
+ if (obj != NULL) {
+ format->setObject("native-window", obj);
+ }
+
+ uint32_t flags;
+ CHECK(msg->findInt32("flags", (int32_t *)&flags));
+
+ if (flags & CONFIGURE_FLAG_ENCODE) {
+ format->setInt32("encoder", true);
+ }
+
+ mCodec->initiateConfigureComponent(format);
+ break;
+ }
+
+ case kWhatStart:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != CONFIGURED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(STARTING);
+
+ mCodec->initiateStart();
+ break;
+ }
+
+ case kWhatStop:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != INITIALIZED
+ && mState != CONFIGURED && mState != STARTED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(STOPPING);
+
+ mCodec->initiateShutdown();
+ returnBuffersToCodec();
+ break;
+ }
+
+ case kWhatDequeueInputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (handleDequeueInputBuffer(replyID, true /* new request */)) {
+ break;
+ }
+
+ int64_t timeoutUs;
+ CHECK(msg->findInt64("timeoutUs", &timeoutUs));
+
+ if (timeoutUs == 0ll) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(replyID);
+ break;
+ }
+
+ mFlags |= kFlagDequeueInputPending;
+ mDequeueInputReplyID = replyID;
+
+ if (timeoutUs > 0ll) {
+ sp<AMessage> timeoutMsg =
+ new AMessage(kWhatDequeueInputTimedOut, id());
+ timeoutMsg->setInt32(
+ "generation", ++mDequeueInputTimeoutGeneration);
+ timeoutMsg->post(timeoutUs);
+ }
+ break;
+ }
+
+ case kWhatDequeueInputTimedOut:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mDequeueInputTimeoutGeneration) {
+ // Obsolete
+ break;
+ }
+
+ CHECK(mFlags & kFlagDequeueInputPending);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(mDequeueInputReplyID);
+
+ mFlags &= ~kFlagDequeueInputPending;
+ mDequeueInputReplyID = 0;
+ break;
+ }
+
+ case kWhatQueueInputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ status_t err = onQueueInputBuffer(msg);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatDequeueOutputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (handleDequeueOutputBuffer(replyID, true /* new request */)) {
+ break;
+ }
+
+ int64_t timeoutUs;
+ CHECK(msg->findInt64("timeoutUs", &timeoutUs));
+
+ if (timeoutUs == 0ll) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(replyID);
+ break;
+ }
+
+ mFlags |= kFlagDequeueOutputPending;
+ mDequeueOutputReplyID = replyID;
+
+ if (timeoutUs > 0ll) {
+ sp<AMessage> timeoutMsg =
+ new AMessage(kWhatDequeueOutputTimedOut, id());
+ timeoutMsg->setInt32(
+ "generation", ++mDequeueOutputTimeoutGeneration);
+ timeoutMsg->post(timeoutUs);
+ }
+ break;
+ }
+
+ case kWhatDequeueOutputTimedOut:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mDequeueOutputTimeoutGeneration) {
+ // Obsolete
+ break;
+ }
+
+ CHECK(mFlags & kFlagDequeueOutputPending);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(mDequeueOutputReplyID);
+
+ mFlags &= ~kFlagDequeueOutputPending;
+ mDequeueOutputReplyID = 0;
+ break;
+ }
+
+ case kWhatReleaseOutputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ status_t err = onReleaseOutputBuffer(msg);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatGetBuffers:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ int32_t portIndex;
+ CHECK(msg->findInt32("portIndex", &portIndex));
+
+ Vector<sp<ABuffer> > *dstBuffers;
+ CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
+
+ dstBuffers->clear();
+ const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex];
+
+ for (size_t i = 0; i < srcBuffers.size(); ++i) {
+ const BufferInfo &info = srcBuffers.itemAt(i);
+
+ dstBuffers->push_back(info.mData);
+ }
+
+ (new AMessage)->postReply(replyID);
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(FLUSHING);
+
+ mCodec->signalFlush();
+ returnBuffersToCodec();
+ break;
+ }
+
+ case kWhatGetOutputFormat:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if ((mState != STARTED && mState != FLUSHING)
+ || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setMessage("format", mOutputFormat);
+ response->postReply(replyID);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+void MediaCodec::setState(State newState) {
+ if (newState == UNINITIALIZED) {
+ delete mSoftRenderer;
+ mSoftRenderer = NULL;
+
+ mNativeWindow.clear();
+
+ mOutputFormat.clear();
+ mFlags &= ~kFlagOutputFormatChanged;
+ mFlags &= ~kFlagOutputBuffersChanged;
+ mFlags &= ~kFlagStickyError;
+ }
+
+ mState = newState;
+
+ cancelPendingDequeueOperations();
+}
+
+void MediaCodec::returnBuffersToCodec() {
+ returnBuffersToCodecOnPort(kPortIndexInput);
+ returnBuffersToCodecOnPort(kPortIndexOutput);
+}
+
+void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex) {
+ CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (info->mNotify != NULL) {
+ sp<AMessage> msg = info->mNotify;
+ info->mNotify = NULL;
+ info->mOwnedByClient = false;
+
+ if (portIndex == kPortIndexInput) {
+ msg->setInt32("err", ERROR_END_OF_STREAM);
+ }
+ msg->post();
+ }
+ }
+
+ mAvailPortBuffers[portIndex].clear();
+}
+
+size_t MediaCodec::updateBuffers(
+ int32_t portIndex, const sp<AMessage> &msg) {
+ CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+ void *bufferID;
+ CHECK(msg->findPointer("buffer-id", &bufferID));
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (info->mBufferID == bufferID) {
+ CHECK(info->mNotify == NULL);
+ CHECK(msg->findMessage("reply", &info->mNotify));
+
+ mAvailPortBuffers[portIndex].push_back(i);
+
+ return i;
+ }
+ }
+
+ TRESPASS();
+
+ return 0;
+}
+
+status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
+ size_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ uint32_t flags;
+ CHECK(msg->findSize("index", &index));
+ CHECK(msg->findSize("offset", &offset));
+ CHECK(msg->findSize("size", &size));
+ CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("flags", (int32_t *)&flags));
+
+ if (index >= mPortBuffers[kPortIndexInput].size()) {
+ return -ERANGE;
+ }
+
+ BufferInfo *info = &mPortBuffers[kPortIndexInput].editItemAt(index);
+
+ if (info->mNotify == NULL || !info->mOwnedByClient) {
+ return -EACCES;
+ }
+
+ if (offset + size > info->mData->capacity()) {
+ return -EINVAL;
+ }
+
+ sp<AMessage> reply = info->mNotify;
+ info->mNotify = NULL;
+ info->mOwnedByClient = false;
+
+ info->mData->setRange(offset, size);
+ info->mData->meta()->setInt64("timeUs", timeUs);
+
+ if (flags & BUFFER_FLAG_EOS) {
+ info->mData->meta()->setInt32("eos", true);
+ }
+
+ if (flags & BUFFER_FLAG_CODECCONFIG) {
+ info->mData->meta()->setInt32("csd", true);
+ }
+
+ reply->setBuffer("buffer", info->mData);
+ reply->post();
+
+ return OK;
+}
+
+status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
+ size_t index;
+ CHECK(msg->findSize("index", &index));
+
+ int32_t render;
+ if (!msg->findInt32("render", &render)) {
+ render = 0;
+ }
+
+ if (mState != STARTED) {
+ return -EINVAL;
+ }
+
+ if (index >= mPortBuffers[kPortIndexOutput].size()) {
+ return -ERANGE;
+ }
+
+ BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
+
+ if (info->mNotify == NULL || !info->mOwnedByClient) {
+ return -EACCES;
+ }
+
+ if (render) {
+ info->mNotify->setInt32("render", true);
+
+ if (mSoftRenderer != NULL) {
+ mSoftRenderer->render(
+ info->mData->data(), info->mData->size(), NULL);
+ }
+ }
+
+ info->mNotify->post();
+ info->mNotify = NULL;
+ info->mOwnedByClient = false;
+
+ return OK;
+}
+
+ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
+ CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+ List<size_t> *availBuffers = &mAvailPortBuffers[portIndex];
+
+ if (availBuffers->empty()) {
+ return -EAGAIN;
+ }
+
+ size_t index = *availBuffers->begin();
+ availBuffers->erase(availBuffers->begin());
+
+ BufferInfo *info = &mPortBuffers[portIndex].editItemAt(index);
+ CHECK(!info->mOwnedByClient);
+ info->mOwnedByClient = true;
+
+ return index;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 7b17d65..2171492 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include "include/AMRExtractor.h"
+#include "include/AVIExtractor.h"
#include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
@@ -109,10 +110,12 @@ sp<MediaExtractor> MediaExtractor::Create(
ret = new MatroskaExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
ret = new MPEG2TSExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)) {
+ ret = new AVIExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
ret = new WVMExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
- ret = new AACExtractor(source);
+ ret = new AACExtractor(source, meta);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
ret = new MPEG2PSExtractor(source);
}
diff --git a/media/libstagefright/MediaSourceSplitter.cpp b/media/libstagefright/MediaSourceSplitter.cpp
index 8af0694..3b64ded 100644
--- a/media/libstagefright/MediaSourceSplitter.cpp
+++ b/media/libstagefright/MediaSourceSplitter.cpp
@@ -18,8 +18,8 @@
#define LOG_TAG "MediaSourceSplitter"
#include <utils/Log.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaSourceSplitter.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index 884f3b4..66dec90 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -17,7 +17,7 @@
#include <stdlib.h>
#include <string.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MetaData.h>
namespace android {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 249c298..0957426 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -370,6 +370,7 @@ void NuCachedSource2::onFetch() {
&& (mSource->flags() & DataSource::kIsHTTPBasedSource)) {
ALOGV("Disconnecting at high watermark");
static_cast<HTTPBase *>(mSource.get())->disconnect();
+ mFinalStatus = -EAGAIN;
}
}
} else {
@@ -549,7 +550,7 @@ ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
size_t delta = offset - mCacheOffset;
- if (mFinalStatus != OK) {
+ if (mFinalStatus != OK && mNumRetriesLeft == 0) {
if (delta >= mCache->totalSize()) {
return mFinalStatus;
}
@@ -591,7 +592,7 @@ status_t NuCachedSource2::seekInternal_l(off64_t offset) {
size_t totalSize = mCache->totalSize();
CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
- mFinalStatus = OK;
+ mNumRetriesLeft = kMaxNumRetries;
mFetching = true;
return OK;
@@ -603,8 +604,8 @@ void NuCachedSource2::resumeFetchingIfNecessary() {
restartPrefetcherIfNecessary_l(true /* ignore low water threshold */);
}
-sp<DecryptHandle> NuCachedSource2::DrmInitialization() {
- return mSource->DrmInitialization();
+sp<DecryptHandle> NuCachedSource2::DrmInitialization(const char* mime) {
+ return mSource->DrmInitialization(mime);
}
void NuCachedSource2::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
new file mode 100644
index 0000000..afd4763
--- /dev/null
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright 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 "NuMediaExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/NuMediaExtractor.h>
+
+#include "include/ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+NuMediaExtractor::NuMediaExtractor() {
+}
+
+NuMediaExtractor::~NuMediaExtractor() {
+ releaseTrackSamples();
+
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ CHECK_EQ((status_t)OK, info->mSource->stop());
+ }
+
+ mSelectedTracks.clear();
+}
+
+status_t NuMediaExtractor::setDataSource(const char *path) {
+ sp<DataSource> dataSource = DataSource::CreateFromURI(path);
+
+ if (dataSource == NULL) {
+ return -ENOENT;
+ }
+
+ mImpl = MediaExtractor::Create(dataSource);
+
+ if (mImpl == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ return OK;
+}
+
+size_t NuMediaExtractor::countTracks() const {
+ return mImpl == NULL ? 0 : mImpl->countTracks();
+}
+
+status_t NuMediaExtractor::getTrackFormat(
+ size_t index, sp<AMessage> *format) const {
+ *format = NULL;
+
+ if (mImpl == NULL) {
+ return -EINVAL;
+ }
+
+ if (index >= mImpl->countTracks()) {
+ return -ERANGE;
+ }
+
+ sp<MetaData> meta = mImpl->getTrackMetaData(index);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> msg = new AMessage;
+ msg->setString("mime", mime);
+
+ if (!strncasecmp("video/", mime, 6)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ msg->setInt32("width", width);
+ msg->setInt32("height", height);
+ } else {
+ CHECK(!strncasecmp("audio/", mime, 6));
+
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ msg->setInt32("channel-count", numChannels);
+ msg->setInt32("sample-rate", sampleRate);
+ }
+
+ int32_t maxInputSize;
+ if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+ msg->setInt32("max-input-size", maxInputSize);
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ sp<ABuffer> buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+
+ msg->setBuffer("csd-0", buffer);
+
+ buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-1", buffer);
+ } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+ memcpy(buffer->data(), codec_specific_data,
+ codec_specific_data_size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
+ } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
+
+ if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
+ return -EINVAL;
+ }
+
+ buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-1", buffer);
+ }
+
+ *format = msg;
+
+ return OK;
+}
+
+status_t NuMediaExtractor::selectTrack(size_t index) {
+ if (mImpl == NULL) {
+ return -EINVAL;
+ }
+
+ if (index >= mImpl->countTracks()) {
+ return -ERANGE;
+ }
+
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ if (info->mTrackIndex == index) {
+ // This track has already been selected.
+ return OK;
+ }
+ }
+
+ sp<MediaSource> source = mImpl->getTrack(index);
+
+ CHECK_EQ((status_t)OK, source->start());
+
+ mSelectedTracks.push();
+ TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
+
+ info->mSource = source;
+ info->mTrackIndex = index;
+ info->mFinalResult = OK;
+ info->mSample = NULL;
+ info->mSampleTimeUs = -1ll;
+ info->mFlags = 0;
+
+ const char *mime;
+ CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
+ info->mFlags |= kIsVorbis;
+ }
+
+ return OK;
+}
+
+void NuMediaExtractor::releaseTrackSamples() {
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ if (info->mSample != NULL) {
+ info->mSample->release();
+ info->mSample = NULL;
+
+ info->mSampleTimeUs = -1ll;
+ }
+ }
+}
+
+ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
+ TrackInfo *minInfo = NULL;
+ ssize_t minIndex = -1;
+
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ if (seekTimeUs >= 0ll) {
+ info->mFinalResult = OK;
+
+ if (info->mSample != NULL) {
+ info->mSample->release();
+ info->mSample = NULL;
+ info->mSampleTimeUs = -1ll;
+ }
+ } else if (info->mFinalResult != OK) {
+ continue;
+ }
+
+ if (info->mSample == NULL) {
+ MediaSource::ReadOptions options;
+ if (seekTimeUs >= 0ll) {
+ options.setSeekTo(seekTimeUs);
+ }
+ status_t err = info->mSource->read(&info->mSample, &options);
+
+ if (err != OK) {
+ CHECK(info->mSample == NULL);
+
+ info->mFinalResult = err;
+ info->mSampleTimeUs = -1ll;
+ continue;
+ } else {
+ CHECK(info->mSample != NULL);
+ CHECK(info->mSample->meta_data()->findInt64(
+ kKeyTime, &info->mSampleTimeUs));
+ }
+ }
+
+ if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
+ minInfo = info;
+ minIndex = i;
+ }
+ }
+
+ return minIndex;
+}
+
+status_t NuMediaExtractor::seekTo(int64_t timeUs) {
+ return fetchTrackSamples(timeUs);
+}
+
+status_t NuMediaExtractor::advance() {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+
+ info->mSample->release();
+ info->mSample = NULL;
+ info->mSampleTimeUs = -1ll;
+
+ return OK;
+}
+
+status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+
+ size_t sampleSize = info->mSample->range_length();
+
+ if (info->mFlags & kIsVorbis) {
+ // Each sample's data is suffixed by the number of page samples
+ // or -1 if not available.
+ sampleSize += sizeof(int32_t);
+ }
+
+ if (buffer->capacity() < sampleSize) {
+ return -ENOMEM;
+ }
+
+ const uint8_t *src =
+ (const uint8_t *)info->mSample->data()
+ + info->mSample->range_offset();
+
+ memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
+
+ if (info->mFlags & kIsVorbis) {
+ int32_t numPageSamples;
+ if (!info->mSample->meta_data()->findInt32(
+ kKeyValidSamples, &numPageSamples)) {
+ numPageSamples = -1;
+ }
+
+ memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
+ &numPageSamples,
+ sizeof(numPageSamples));
+ }
+
+ buffer->setRange(0, sampleSize);
+
+ return OK;
+}
+
+status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+ *trackIndex = info->mTrackIndex;
+
+ return OK;
+}
+
+status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+ *sampleTimeUs = info->mSampleTimeUs;
+
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 9de873e..7cdb793 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -20,11 +20,299 @@
#include <binder/IServiceManager.h>
#include <media/IMediaPlayerService.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/OMXClient.h>
+#include <utils/KeyedVector.h>
+
+#include "include/OMX.h"
namespace android {
+struct MuxOMX : public IOMX {
+ MuxOMX(const sp<IOMX> &remoteOMX);
+ virtual ~MuxOMX();
+
+ virtual IBinder *onAsBinder() { return NULL; }
+
+ virtual bool livesLocally(node_id node, pid_t pid);
+
+ virtual status_t listNodes(List<ComponentInfo> *list);
+
+ virtual status_t allocateNode(
+ const char *name, const sp<IOMXObserver> &observer,
+ node_id *node);
+
+ virtual status_t freeNode(node_id node);
+
+ virtual status_t sendCommand(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
+
+ virtual status_t getParameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t setParameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
+ virtual status_t getConfig(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t setConfig(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
+ virtual status_t getState(
+ node_id node, OMX_STATETYPE* state);
+
+ virtual status_t storeMetaDataInBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable);
+
+ virtual status_t enableGraphicBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable);
+
+ virtual status_t getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage);
+
+ virtual status_t useBuffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer);
+
+ virtual status_t useGraphicBuffer(
+ node_id node, OMX_U32 port_index,
+ const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
+
+ virtual status_t allocateBuffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer, void **buffer_data);
+
+ virtual status_t allocateBufferWithBackup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer);
+
+ virtual status_t freeBuffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer);
+
+ virtual status_t fillBuffer(node_id node, buffer_id buffer);
+
+ virtual status_t emptyBuffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp);
+
+ virtual status_t getExtensionIndex(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index);
+
+private:
+ mutable Mutex mLock;
+
+ sp<IOMX> mRemoteOMX;
+ sp<IOMX> mLocalOMX;
+
+ KeyedVector<node_id, bool> mIsLocalNode;
+
+ bool isLocalNode(node_id node) const;
+ bool isLocalNode_l(node_id node) const;
+ const sp<IOMX> &getOMX(node_id node) const;
+ const sp<IOMX> &getOMX_l(node_id node) const;
+
+ static bool IsSoftwareComponent(const char *name);
+
+ DISALLOW_EVIL_CONSTRUCTORS(MuxOMX);
+};
+
+MuxOMX::MuxOMX(const sp<IOMX> &remoteOMX)
+ : mRemoteOMX(remoteOMX) {
+}
+
+MuxOMX::~MuxOMX() {
+}
+
+bool MuxOMX::isLocalNode(node_id node) const {
+ Mutex::Autolock autoLock(mLock);
+
+ return isLocalNode_l(node);
+}
+
+bool MuxOMX::isLocalNode_l(node_id node) const {
+ return mIsLocalNode.indexOfKey(node) >= 0;
+}
+
+// static
+bool MuxOMX::IsSoftwareComponent(const char *name) {
+ return !strncasecmp(name, "OMX.google.", 11);
+}
+
+const sp<IOMX> &MuxOMX::getOMX(node_id node) const {
+ return isLocalNode(node) ? mLocalOMX : mRemoteOMX;
+}
+
+const sp<IOMX> &MuxOMX::getOMX_l(node_id node) const {
+ return isLocalNode_l(node) ? mLocalOMX : mRemoteOMX;
+}
+
+bool MuxOMX::livesLocally(node_id node, pid_t pid) {
+ return getOMX(node)->livesLocally(node, pid);
+}
+
+status_t MuxOMX::listNodes(List<ComponentInfo> *list) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mLocalOMX == NULL) {
+ mLocalOMX = new OMX;
+ }
+
+ return mLocalOMX->listNodes(list);
+}
+
+status_t MuxOMX::allocateNode(
+ const char *name, const sp<IOMXObserver> &observer,
+ node_id *node) {
+ Mutex::Autolock autoLock(mLock);
+
+ sp<IOMX> omx;
+
+ if (IsSoftwareComponent(name)) {
+ if (mLocalOMX == NULL) {
+ mLocalOMX = new OMX;
+ }
+ omx = mLocalOMX;
+ } else {
+ omx = mRemoteOMX;
+ }
+
+ status_t err = omx->allocateNode(name, observer, node);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (omx == mLocalOMX) {
+ mIsLocalNode.add(*node, true);
+ }
+
+ return OK;
+}
+
+status_t MuxOMX::freeNode(node_id node) {
+ Mutex::Autolock autoLock(mLock);
+
+ status_t err = getOMX_l(node)->freeNode(node);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mIsLocalNode.removeItem(node);
+
+ return OK;
+}
+
+status_t MuxOMX::sendCommand(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ return getOMX(node)->sendCommand(node, cmd, param);
+}
+
+status_t MuxOMX::getParameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ return getOMX(node)->getParameter(node, index, params, size);
+}
+
+status_t MuxOMX::setParameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ return getOMX(node)->setParameter(node, index, params, size);
+}
+
+status_t MuxOMX::getConfig(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ return getOMX(node)->getConfig(node, index, params, size);
+}
+
+status_t MuxOMX::setConfig(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ return getOMX(node)->setConfig(node, index, params, size);
+}
+
+status_t MuxOMX::getState(
+ node_id node, OMX_STATETYPE* state) {
+ return getOMX(node)->getState(node, state);
+}
+
+status_t MuxOMX::storeMetaDataInBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+ return getOMX(node)->storeMetaDataInBuffers(node, port_index, enable);
+}
+
+status_t MuxOMX::enableGraphicBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+ return getOMX(node)->enableGraphicBuffers(node, port_index, enable);
+}
+
+status_t MuxOMX::getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage) {
+ return getOMX(node)->getGraphicBufferUsage(node, port_index, usage);
+}
+
+status_t MuxOMX::useBuffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ return getOMX(node)->useBuffer(node, port_index, params, buffer);
+}
+
+status_t MuxOMX::useGraphicBuffer(
+ node_id node, OMX_U32 port_index,
+ const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
+ return getOMX(node)->useGraphicBuffer(
+ node, port_index, graphicBuffer, buffer);
+}
+
+status_t MuxOMX::allocateBuffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer, void **buffer_data) {
+ return getOMX(node)->allocateBuffer(
+ node, port_index, size, buffer, buffer_data);
+}
+
+status_t MuxOMX::allocateBufferWithBackup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ return getOMX(node)->allocateBufferWithBackup(
+ node, port_index, params, buffer);
+}
+
+status_t MuxOMX::freeBuffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer) {
+ return getOMX(node)->freeBuffer(node, port_index, buffer);
+}
+
+status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer) {
+ return getOMX(node)->fillBuffer(node, buffer);
+}
+
+status_t MuxOMX::emptyBuffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ return getOMX(node)->emptyBuffer(
+ node, buffer, range_offset, range_length, flags, timestamp);
+}
+
+status_t MuxOMX::getExtensionIndex(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) {
+ return getOMX(node)->getExtensionIndex(node, parameter_name, index);
+}
+
OMXClient::OMXClient() {
}
@@ -38,10 +326,19 @@ status_t OMXClient::connect() {
mOMX = service->getOMX();
CHECK(mOMX.get() != NULL);
+ if (!mOMX->livesLocally(NULL /* node */, getpid())) {
+ ALOGI("Using client-side OMX mux.");
+ mOMX = new MuxOMX(mOMX);
+ }
+
return OK;
}
void OMXClient::disconnect() {
+ if (mOMX.get() != NULL) {
+ mOMX.clear();
+ mOMX = NULL;
+ }
}
} // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 60d9bb7..1325462 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -19,8 +19,6 @@
#include <utils/Log.h>
#include "include/AACEncoder.h"
-#include "include/AMRNBEncoder.h"
-#include "include/AMRWBEncoder.h"
#include "include/AVCEncoder.h"
#include "include/M4vH263Encoder.h"
@@ -71,8 +69,6 @@ static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaDa
#define FACTORY_REF(name) { #name, Make##name },
-FACTORY_CREATE_ENCODER(AMRNBEncoder)
-FACTORY_CREATE_ENCODER(AMRWBEncoder)
FACTORY_CREATE_ENCODER(AACEncoder)
FACTORY_CREATE_ENCODER(AVCEncoder)
FACTORY_CREATE_ENCODER(M4vH263Encoder)
@@ -86,8 +82,6 @@ static sp<MediaSource> InstantiateSoftwareEncoder(
};
static const FactoryInfo kFactoryInfo[] = {
- FACTORY_REF(AMRNBEncoder)
- FACTORY_REF(AMRWBEncoder)
FACTORY_REF(AACEncoder)
FACTORY_REF(AVCEncoder)
FACTORY_REF(M4vH263Encoder)
@@ -149,10 +143,11 @@ static const CodecInfo kDecoderInfo[] = {
static const CodecInfo kEncoderInfo[] = {
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.encode" },
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBEncoder" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.encoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
- { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBEncoder" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.encoder" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.encoder" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
@@ -1482,11 +1477,12 @@ OMXCodec::OMXCodec(
const sp<MediaSource> &source,
const sp<ANativeWindow> &nativeWindow)
: mOMX(omx),
- mOMXLivesLocally(omx->livesLocally(getpid())),
+ mOMXLivesLocally(omx->livesLocally(node, getpid())),
mNode(node),
mQuirks(quirks),
mFlags(flags),
mIsEncoder(isEncoder),
+ mIsVideo(!strncasecmp("video/", mime, 6)),
mMIME(strdup(mime)),
mComponentName(strdup(componentName)),
mSource(source),
@@ -1545,6 +1541,8 @@ void OMXCodec::setComponentRole(
"video_decoder.mpeg4", "video_encoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_H263,
"video_decoder.h263", "video_encoder.h263" },
+ { MEDIA_MIMETYPE_VIDEO_VPX,
+ "video_decoder.vpx", "video_encoder.vpx" },
};
static const size_t kNumMimeToRole =
@@ -2191,8 +2189,8 @@ error:
}
}
-int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) {
- CHECK(mIsEncoder);
+int64_t OMXCodec::getDecodingTimeUs() {
+ CHECK(mIsEncoder && mIsVideo);
if (mDecodingTimeList.empty()) {
CHECK(mSignalledEOS || mNoMoreOutputData);
@@ -2203,12 +2201,7 @@ int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) {
List<int64_t>::iterator it = mDecodingTimeList.begin();
int64_t timeUs = *it;
-
- // If the output buffer is codec specific configuration,
- // do not remove the decoding time from the list.
- if (!isCodecSpecific) {
- mDecodingTimeList.erase(it);
- }
+ mDecodingTimeList.erase(it);
return timeUs;
}
@@ -2387,8 +2380,8 @@ void OMXCodec::on_message(const omx_message &msg) {
mNoMoreOutputData = true;
}
- if (mIsEncoder) {
- int64_t decodingTimeUs = retrieveDecodingTimeUs(isCodecSpecific);
+ if (mIsEncoder && mIsVideo) {
+ int64_t decodingTimeUs = isCodecSpecific? 0: getDecodingTimeUs();
buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
}
@@ -3249,7 +3242,7 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) {
int64_t lastBufferTimeUs;
CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs));
CHECK(lastBufferTimeUs >= 0);
- if (mIsEncoder) {
+ if (mIsEncoder && mIsVideo) {
mDecodingTimeList.push_back(lastBufferTimeUs);
}
@@ -3565,6 +3558,7 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t
//////////////// output port ////////////////////
// format
OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
format.nPortIndex = kPortIndexOutput;
format.nIndex = 0;
status_t err = OMX_ErrorNone;
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 73efc27..5e79e78 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -21,10 +21,10 @@
#include "include/OggExtractor.h"
#include <cutils/properties.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index 81ec5c1..eae721b 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -22,8 +22,8 @@
#include <arpa/inet.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/Utils.h>
#include "include/SampleTable.h"
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 8d80d63..d9858d7 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -618,26 +618,31 @@ status_t SampleTable::findSyncSampleNear(
}
uint32_t left = 0;
- while (left < mNumSyncSamples) {
- uint32_t x = mSyncSamples[left];
+ uint32_t right = mNumSyncSamples;
+ while (left < right) {
+ uint32_t center = left + (right - left) / 2;
+ uint32_t x = mSyncSamples[center];
- if (x >= start_sample_index) {
+ if (start_sample_index < x) {
+ right = center;
+ } else if (start_sample_index > x) {
+ left = center + 1;
+ } else {
+ left = center;
break;
}
-
- ++left;
}
-
if (left == mNumSyncSamples) {
if (flags == kFlagAfter) {
ALOGE("tried to find a sync frame after the last one: %d", left);
return ERROR_OUT_OF_RANGE;
}
+ left = left - 1;
}
- if (left > 0) {
- --left;
- }
+ // Now ssi[left] is the sync sample index just before (or at)
+ // start_sample_index.
+ // Also start_sample_index < ssi[left + 1], if left + 1 < mNumSyncSamples.
uint32_t x = mSyncSamples[left];
@@ -682,7 +687,11 @@ status_t SampleTable::findSyncSampleNear(
x = mSyncSamples[left - 1];
- CHECK(x <= start_sample_index);
+ if (x > start_sample_index) {
+ // The table of sync sample indices was not sorted
+ // properly.
+ return ERROR_MALFORMED;
+ }
}
break;
}
@@ -696,7 +705,11 @@ status_t SampleTable::findSyncSampleNear(
x = mSyncSamples[left + 1];
- CHECK(x >= start_sample_index);
+ if (x < start_sample_index) {
+ // The table of sync sample indices was not sorted
+ // properly.
+ return ERROR_MALFORMED;
+ }
}
break;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 43bfd9e..35f9c1f 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -20,10 +20,10 @@
#include "include/StagefrightMetadataRetriever.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/ColorConverter.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
@@ -37,7 +37,7 @@ StagefrightMetadataRetriever::StagefrightMetadataRetriever()
ALOGV("StagefrightMetadataRetriever()");
DataSource::RegisterDefaultSniffers();
- CHECK_EQ(mClient.connect(), OK);
+ CHECK_EQ(mClient.connect(), (status_t)OK);
}
StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
@@ -169,7 +169,7 @@ static VideoFrame *extractVideoFrameWithCodecFlags(
|| (buffer != NULL && buffer->range_length() == 0));
if (err != OK) {
- CHECK_EQ(buffer, NULL);
+ CHECK(buffer == NULL);
ALOGV("decoding frame failed.");
decoder->stop();
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 48df058..ab2cff0 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -16,22 +16,23 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "SurfaceMediaSource"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/SurfaceMediaSource.h>
-#include <ui/GraphicBuffer.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/openmax/OMX_IVCommon.h>
#include <media/stagefright/MetadataBufferType.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-#include <surfaceflinger/IGraphicBufferAlloc.h>
+#include <ui/GraphicBuffer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/IGraphicBufferAlloc.h>
#include <OMX_Component.h>
#include <utils/Log.h>
#include <utils/String8.h>
+#include <private/gui/ComposerService.h>
+
namespace android {
SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) :
@@ -58,7 +59,7 @@ SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) :
SurfaceMediaSource::~SurfaceMediaSource() {
ALOGV("SurfaceMediaSource::~SurfaceMediaSource");
if (!mStopped) {
- stop();
+ reset();
}
}
@@ -714,9 +715,9 @@ status_t SurfaceMediaSource::start(MetaData *params)
}
-status_t SurfaceMediaSource::stop()
+status_t SurfaceMediaSource::reset()
{
- ALOGV("Stop");
+ ALOGV("Reset");
Mutex::Autolock lock(mMutex);
// TODO: Add waiting on mFrameCompletedCondition here?
@@ -853,7 +854,7 @@ void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) {
}
if (!foundBuffer) {
- CHECK_EQ(0, "signalBufferReturned: bogus buffer");
+ CHECK(!"signalBufferReturned: bogus buffer");
}
}
diff --git a/media/libstagefright/ThrottledSource.cpp b/media/libstagefright/ThrottledSource.cpp
index 88e07b0..b1fcafd 100644
--- a/media/libstagefright/ThrottledSource.cpp
+++ b/media/libstagefright/ThrottledSource.cpp
@@ -16,7 +16,7 @@
#include "include/ThrottledSource.h"
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
namespace android {
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 12c9c36..f4b5d4f 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -31,7 +31,7 @@
#include <sys/prctl.h>
#include <sys/time.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#ifdef ANDROID_SIMULATOR
#include <jni.h>
diff --git a/media/libstagefright/VideoSourceDownSampler.cpp b/media/libstagefright/VideoSourceDownSampler.cpp
index 1b66990..90a42c9 100644
--- a/media/libstagefright/VideoSourceDownSampler.cpp
+++ b/media/libstagefright/VideoSourceDownSampler.cpp
@@ -17,9 +17,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "VideoSourceDownSampler"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/VideoSourceDownSampler.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/YUVImage.h>
#include <media/stagefright/YUVCanvas.h>
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 0bcaf08..501f480 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -20,9 +20,9 @@
#include "include/WAVExtractor.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -217,7 +217,7 @@ status_t WAVExtractor::init() {
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
break;
default:
- CHECK_EQ(mWaveFormat, WAVE_FORMAT_MULAW);
+ CHECK_EQ(mWaveFormat, (uint16_t)WAVE_FORMAT_MULAW);
mTrackMeta->setCString(
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
break;
@@ -362,7 +362,7 @@ status_t WAVSource::read(
// Convert 8-bit unsigned samples to 16-bit signed.
MediaBuffer *tmp;
- CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
+ CHECK_EQ(mGroup->acquire_buffer(&tmp), (status_t)OK);
// The new buffer holds the sample number of samples, but each
// one is 2 bytes wide.
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 2092cb6..c7ad513 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -21,6 +21,7 @@
#include <arpa/inet.h>
#include <utils/String8.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/Utils.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaSource.h>
@@ -28,7 +29,6 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
#include <dlfcn.h>
#include <utils/Errors.h>
@@ -45,17 +45,12 @@ namespace android {
static Mutex gWVMutex;
WVMExtractor::WVMExtractor(const sp<DataSource> &source)
- : mDataSource(source) {
- {
- Mutex::Autolock autoLock(gWVMutex);
- if (gVendorLibHandle == NULL) {
- gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
- }
+ : mDataSource(source)
+{
+ Mutex::Autolock autoLock(gWVMutex);
- if (gVendorLibHandle == NULL) {
- ALOGE("Failed to open libwvm.so");
- return;
- }
+ if (!getVendorLibHandle()) {
+ return;
}
typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>);
@@ -64,13 +59,28 @@ WVMExtractor::WVMExtractor(const sp<DataSource> &source)
"_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
if (getInstanceFunc) {
+ CHECK(source->DrmInitialization(MEDIA_MIMETYPE_CONTAINER_WVM) != NULL);
mImpl = (*getInstanceFunc)(source);
CHECK(mImpl != NULL);
+ setDrmFlag(true);
} else {
ALOGE("Failed to locate GetInstance in libwvm.so");
}
}
+bool WVMExtractor::getVendorLibHandle()
+{
+ if (gVendorLibHandle == NULL) {
+ gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
+ }
+
+ if (gVendorLibHandle == NULL) {
+ ALOGE("Failed to open libwvm.so");
+ }
+
+ return gVendorLibHandle != NULL;
+}
+
WVMExtractor::~WVMExtractor() {
}
@@ -113,5 +123,33 @@ void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) {
}
}
+bool SniffWVM(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *) {
+
+ Mutex::Autolock autoLock(gWVMutex);
+
+ if (!WVMExtractor::getVendorLibHandle()) {
+ return false;
+ }
+
+ typedef WVMLoadableExtractor *(*SnifferFunc)(const sp<DataSource>&);
+ SnifferFunc snifferFunc =
+ (SnifferFunc) dlsym(gVendorLibHandle,
+ "_ZN7android15IsWidevineMediaERKNS_2spINS_10DataSourceEEE");
+
+ if (snifferFunc) {
+ if ((*snifferFunc)(source)) {
+ *mimeType = MEDIA_MIMETYPE_CONTAINER_WVM;
+ *confidence = 10.0f;
+ return true;
+ }
+ } else {
+ ALOGE("IsWidevineMedia not found in libwvm.so");
+ }
+
+ return false;
+}
+
} //namespace android
diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk
index 6573e3c..63775f1 100644
--- a/media/libstagefright/chromium_http/Android.mk
+++ b/media/libstagefright/chromium_http/Android.mk
@@ -3,8 +3,9 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- ChromiumHTTPDataSource.cpp \
- support.cpp \
+ DataUriSource.cpp \
+ ChromiumHTTPDataSource.cpp \
+ support.cpp
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
index 180460b..76f7946 100644
--- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
+++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
@@ -259,7 +259,7 @@ void ChromiumHTTPDataSource::onDisconnectComplete() {
mCondition.broadcast();
}
-sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() {
+sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization(const char* mime) {
Mutex::Autolock autoLock(mLock);
if (mDrmManagerClient == NULL) {
@@ -275,7 +275,7 @@ sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() {
* original one
*/
mDecryptHandle = mDrmManagerClient->openDecryptSession(
- String8(mURI.c_str()));
+ String8(mURI.c_str()), mime);
}
if (mDecryptHandle == NULL) {
diff --git a/media/libstagefright/chromium_http/DataUriSource.cpp b/media/libstagefright/chromium_http/DataUriSource.cpp
new file mode 100644
index 0000000..ecf3fa1
--- /dev/null
+++ b/media/libstagefright/chromium_http/DataUriSource.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <include/DataUriSource.h>
+
+#include <net/base/data_url.h>
+#include <googleurl/src/gurl.h>
+
+
+namespace android {
+
+DataUriSource::DataUriSource(const char *uri) :
+ mDataUri(uri),
+ mInited(NO_INIT) {
+
+ // Copy1: const char *uri -> String8 mDataUri.
+ std::string mimeTypeStr, unusedCharsetStr, dataStr;
+ // Copy2: String8 mDataUri -> std::string
+ const bool ret = net::DataURL::Parse(
+ GURL(std::string(mDataUri.string())),
+ &mimeTypeStr, &unusedCharsetStr, &dataStr);
+ // Copy3: std::string dataStr -> AString mData
+ mData.setTo(dataStr.data(), dataStr.length());
+ mInited = ret ? OK : UNKNOWN_ERROR;
+
+ // The chromium data url implementation defaults to using "text/plain"
+ // if no mime type is specified. We prefer to leave this unspecified
+ // instead, since the mime type is sniffed in most cases.
+ if (mimeTypeStr != "text/plain") {
+ mMimeType = mimeTypeStr.c_str();
+ }
+}
+
+ssize_t DataUriSource::readAt(off64_t offset, void *out, size_t size) {
+ if (mInited != OK) {
+ return mInited;
+ }
+
+ const off64_t length = mData.size();
+ if (offset >= length) {
+ return UNKNOWN_ERROR;
+ }
+
+ const char *dataBuf = mData.c_str();
+ const size_t bytesToCopy =
+ offset + size >= length ? (length - offset) : size;
+
+ if (bytesToCopy > 0) {
+ memcpy(out, dataBuf + offset, bytesToCopy);
+ }
+
+ return bytesToCopy;
+}
+
+} // namespace android
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
index da9d280..ea6c360 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -218,6 +218,18 @@ OMX_ERRORTYPE SoftAAC::internalSetParameter(
return OMX_ErrorNone;
}
+ case OMX_IndexParamAudioPcm:
+ {
+ const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
default:
return SimpleSoftOMXComponent::internalSetParameter(index, params);
}
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index 2b8633d..8b5007e 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -22,8 +22,8 @@
#include "voAAC.h"
#include "cmnMemory.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -114,8 +114,8 @@ status_t AACEncoder::setAudioSpecificConfigData() {
ALOGV("setAudioSpecificConfigData: %d hz, %d bps, and %d channels",
mSampleRate, mBitRate, mChannels);
- int32_t index;
- CHECK_EQ(OK, getSampleRateTableIndex(mSampleRate, index));
+ int32_t index = 0;
+ CHECK_EQ((status_t)OK, getSampleRateTableIndex(mSampleRate, index));
if (mChannels > 2 || mChannels <= 0) {
ALOGE("Unsupported number of channels(%d)", mChannels);
return UNKNOWN_ERROR;
@@ -142,7 +142,7 @@ status_t AACEncoder::start(MetaData *params) {
mBufferGroup = new MediaBufferGroup;
mBufferGroup->add_buffer(new MediaBuffer(2048));
- CHECK_EQ(OK, initCheck());
+ CHECK_EQ((status_t)OK, initCheck());
mNumInputSamples = 0;
mAnchorTimeUs = 0;
@@ -183,7 +183,7 @@ status_t AACEncoder::stop() {
mSource->stop();
if (mEncoderHandle) {
- CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+ CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
mEncoderHandle = NULL;
}
delete mApiHandle;
@@ -223,7 +223,7 @@ status_t AACEncoder::read(
CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));
MediaBuffer *buffer;
- CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+ CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
uint8_t *outPtr = (uint8_t *)buffer->data();
bool readFromSource = false;
int64_t wallClockTimeUs = -1;
@@ -255,7 +255,7 @@ status_t AACEncoder::read(
}
size_t align = mInputBuffer->range_length() % sizeof(int16_t);
- CHECK_EQ(align, 0);
+ CHECK_EQ(align, (size_t)0);
int64_t timeUs;
if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index 8318ba4..34a2796 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -85,3 +85,29 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7
endif
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAACEncoder.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+ frameworks/base/media/libstagefright/codecs/common/include \
+
+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)
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp
new file mode 100644
index 0000000..c6724c2
--- /dev/null
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder.cpp
@@ -0,0 +1,560 @@
+/*
+ * 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 "SoftAACEncoder"
+#include <utils/Log.h>
+
+#include "SoftAACEncoder.h"
+
+#include "voAAC.h"
+#include "cmnMemory.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;
+}
+
+SoftAACEncoder::SoftAACEncoder(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mEncoderHandle(NULL),
+ mApiHandle(NULL),
+ mMemOperator(NULL),
+ mNumChannels(1),
+ mSampleRate(44100),
+ mBitRate(0),
+ mSentCodecSpecificData(false),
+ mInputSize(0),
+ mInputFrame(NULL),
+ mInputTimeUs(-1ll),
+ mSawInputEOS(false),
+ mSignalledError(false) {
+ initPorts();
+ CHECK_EQ(initEncoder(), (status_t)OK);
+
+ setAudioParams();
+}
+
+SoftAACEncoder::~SoftAACEncoder() {
+ delete[] mInputFrame;
+ mInputFrame = NULL;
+
+ if (mEncoderHandle) {
+ CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+ mEncoderHandle = NULL;
+ }
+
+ delete mApiHandle;
+ mApiHandle = NULL;
+
+ delete mMemOperator;
+ mMemOperator = NULL;
+}
+
+void SoftAACEncoder::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 SoftAACEncoder::initEncoder() {
+ mApiHandle = new VO_AUDIO_CODECAPI;
+
+ if (VO_ERR_NONE != voGetAACEncAPI(mApiHandle)) {
+ ALOGE("Failed to get api handle");
+ return UNKNOWN_ERROR;
+ }
+
+ mMemOperator = new VO_MEM_OPERATOR;
+ mMemOperator->Alloc = cmnMemAlloc;
+ mMemOperator->Copy = cmnMemCopy;
+ mMemOperator->Free = cmnMemFree;
+ mMemOperator->Set = cmnMemSet;
+ mMemOperator->Check = cmnMemCheck;
+
+ VO_CODEC_INIT_USERDATA userData;
+ memset(&userData, 0, sizeof(userData));
+ userData.memflag = VO_IMF_USERMEMOPERATOR;
+ userData.memData = (VO_PTR) mMemOperator;
+ if (VO_ERR_NONE !=
+ mApiHandle->Init(&mEncoderHandle, VO_AUDIO_CodingAAC, &userData)) {
+ ALOGE("Failed to init AAC encoder");
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAACEncoder::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_AACObjectMain;
+ 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 SoftAACEncoder::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 (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);
+ }
+}
+
+status_t SoftAACEncoder::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);
+
+ status_t err = setAudioSpecificConfigData();
+
+ if (err != OK) {
+ return err;
+ }
+
+ AACENC_PARAM params;
+ memset(&params, 0, sizeof(params));
+ params.sampleRate = mSampleRate;
+ params.bitRate = mBitRate;
+ params.nChannels = mNumChannels;
+ params.adtsUsed = 0; // We add adts header in the file writer if needed.
+ if (VO_ERR_NONE != mApiHandle->SetParam(
+ mEncoderHandle, VO_PID_AAC_ENCPARAM, &params)) {
+ ALOGE("Failed to set AAC encoder parameters");
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+static status_t getSampleRateTableIndex(int32_t sampleRate, int32_t &index) {
+ static const int32_t kSampleRateTable[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000
+ };
+ const int32_t tableSize =
+ sizeof(kSampleRateTable) / sizeof(kSampleRateTable[0]);
+
+ for (int32_t i = 0; i < tableSize; ++i) {
+ if (sampleRate == kSampleRateTable[i]) {
+ index = i;
+ return OK;
+ }
+ }
+
+ return UNKNOWN_ERROR;
+}
+
+status_t SoftAACEncoder::setAudioSpecificConfigData() {
+ // The AAC encoder's audio specific config really only encodes
+ // number of channels and the sample rate (mapped to an index into
+ // a fixed sample rate table).
+
+ int32_t index;
+ status_t err = getSampleRateTableIndex(mSampleRate, index);
+ if (err != OK) {
+ ALOGE("Unsupported sample rate (%lu Hz)", mSampleRate);
+ return err;
+ }
+
+ if (mNumChannels > 2 || mNumChannels <= 0) {
+ ALOGE("Unsupported number of channels(%lu)", mNumChannels);
+ return UNKNOWN_ERROR;
+ }
+
+ // OMX_AUDIO_AACObjectLC
+ mAudioSpecificConfigData[0] = ((0x02 << 3) | (index >> 1));
+ mAudioSpecificConfigData[1] = ((index & 0x01) << 7) | (mNumChannels << 3);
+
+ return OK;
+}
+
+void SoftAACEncoder::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;
+ }
+
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+ outHeader->nFilledLen = sizeof(mAudioSpecificConfigData);
+ outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG;
+
+ uint8_t *out = outHeader->pBuffer + outHeader->nOffset;
+ memcpy(out, mAudioSpecificConfigData, sizeof(mAudioSpecificConfigData));
+
+#if 0
+ ALOGI("sending codec specific data.");
+ hexdump(out, sizeof(mAudioSpecificConfigData));
+#endif
+
+ outQueue.erase(outQueue.begin());
+ outInfo->mOwnedByUs = false;
+ notifyFillBufferDone(outHeader);
+
+ mSentCodecSpecificData = true;
+ }
+
+ size_t numBytesPerInputFrame =
+ mNumChannels * kNumSamplesPerFrame * sizeof(int16_t);
+
+ 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) {
+ ALOGV("saw input 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;
+
+ VO_CODECBUFFER inputData;
+ memset(&inputData, 0, sizeof(inputData));
+ inputData.Buffer = (unsigned char *)mInputFrame;
+ inputData.Length = numBytesPerInputFrame;
+ CHECK(VO_ERR_NONE ==
+ mApiHandle->SetInputData(mEncoderHandle, &inputData));
+
+ VO_CODECBUFFER outputData;
+ memset(&outputData, 0, sizeof(outputData));
+ VO_AUDIO_OUTPUTINFO outputInfo;
+ memset(&outputInfo, 0, sizeof(outputInfo));
+
+ uint8_t *outPtr = (uint8_t *)outHeader->pBuffer + outHeader->nOffset;
+ size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
+
+ VO_U32 ret = VO_ERR_NONE;
+ size_t nOutputBytes = 0;
+ do {
+ outputData.Buffer = outPtr;
+ outputData.Length = outAvailable - nOutputBytes;
+ ret = mApiHandle->GetOutputData(
+ mEncoderHandle, &outputData, &outputInfo);
+ if (ret == VO_ERR_NONE) {
+ outPtr += outputData.Length;
+ nOutputBytes += outputData.Length;
+ }
+ } while (ret != VO_ERR_INPUT_BUFFER_SMALL);
+
+ 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::SoftAACEncoder(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder.h b/media/libstagefright/codecs/aacenc/SoftAACEncoder.h
new file mode 100644
index 0000000..d148eb7
--- /dev/null
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder.h
@@ -0,0 +1,82 @@
+/*
+ * 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_H_
+
+#define SOFT_AAC_ENCODER_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct VO_AUDIO_CODECAPI;
+struct VO_MEM_OPERATOR;
+
+namespace android {
+
+struct SoftAACEncoder : public SimpleSoftOMXComponent {
+ SoftAACEncoder(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAACEncoder();
+
+ 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,
+ };
+
+ void *mEncoderHandle;
+ VO_AUDIO_CODECAPI *mApiHandle;
+ VO_MEM_OPERATOR *mMemOperator;
+
+ OMX_U32 mNumChannels;
+ OMX_U32 mSampleRate;
+ OMX_U32 mBitRate;
+
+ bool mSentCodecSpecificData;
+ size_t mInputSize;
+ int16_t *mInputFrame;
+ int64_t mInputTimeUs;
+
+ bool mSawInputEOS;
+
+ uint8_t mAudioSpecificConfigData[2];
+
+ bool mSignalledError;
+
+ void initPorts();
+ status_t initEncoder();
+
+ status_t setAudioSpecificConfigData();
+ status_t setAudioParams();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAACEncoder);
+};
+
+} // namespace android
+
+#endif // SOFT_AAC_ENCODER_H_
diff --git a/media/libstagefright/codecs/aacenc/src/asm/ARMV7/PrePostMDCT_v7.s b/media/libstagefright/codecs/aacenc/src/asm/ARMV7/PrePostMDCT_v7.s
index b2bc9d9..7f6b881 100644
--- a/media/libstagefright/codecs/aacenc/src/asm/ARMV7/PrePostMDCT_v7.s
+++ b/media/libstagefright/codecs/aacenc/src/asm/ARMV7/PrePostMDCT_v7.s
@@ -23,9 +23,13 @@
.section .text
.global PreMDCT
+ .fnstart
PreMDCT:
stmdb sp!, {r4 - r11, lr}
+ .save {r4 - r11, lr}
+ fstmfdd sp!, {d8 - d15}
+ .vsave {d8 - d15}
add r9, r0, r1, lsl #2
sub r3, r9, #32
@@ -74,14 +78,20 @@ PreMDCT_LOOP:
bne PreMDCT_LOOP
PreMDCT_END:
+ fldmfdd sp!, {d8 - d15}
ldmia sp!, {r4 - r11, pc}
@ENDP @ |PreMDCT|
+ .fnend
.section .text
.global PostMDCT
+ .fnstart
PostMDCT:
stmdb sp!, {r4 - r11, lr}
+ .save {r4 - r11, lr}
+ fstmfdd sp!, {d8 - d15}
+ .vsave {d8 - d15}
add r9, r0, r1, lsl #2
sub r3, r9, #32
@@ -129,7 +139,8 @@ PostMDCT_LOOP:
bne PostMDCT_LOOP
PostMDCT_END:
+ fldmfdd sp!, {d8 - d15}
ldmia sp!, {r4 - r11, pc}
@ENDP @ |PostMDCT|
- .end
+ .fnend
diff --git a/media/libstagefright/codecs/aacenc/src/asm/ARMV7/R4R8First_v7.s b/media/libstagefright/codecs/aacenc/src/asm/ARMV7/R4R8First_v7.s
index 3033156..03fa6a9 100644
--- a/media/libstagefright/codecs/aacenc/src/asm/ARMV7/R4R8First_v7.s
+++ b/media/libstagefright/codecs/aacenc/src/asm/ARMV7/R4R8First_v7.s
@@ -23,9 +23,13 @@
.section .text
.global Radix8First
+ .fnstart
Radix8First:
stmdb sp!, {r4 - r11, lr}
+ .save {r4 - r11, lr}
+ fstmfdd sp!, {d8 - d15}
+ .vsave {d8 - d15}
ldr r3, SQRT1_2
cmp r1, #0
@@ -103,17 +107,23 @@ Radix8First_LOOP:
bne Radix8First_LOOP
Radix8First_END:
+ fldmfdd sp!, {d8 - d15}
ldmia sp!, {r4 - r11, pc}
SQRT1_2:
.word 0x2d413ccd
@ENDP @ |Radix8First|
+ .fnend
.section .text
.global Radix4First
+ .fnstart
Radix4First:
stmdb sp!, {r4 - r11, lr}
+ .save {r4 - r11, lr}
+ fstmfdd sp!, {d8 - d15}
+ .vsave {d8 - d15}
cmp r1, #0
beq Radix4First_END
@@ -140,7 +150,8 @@ Radix4First_LOOP:
bne Radix4First_LOOP
Radix4First_END:
+ fldmfdd sp!, {d8 - d15}
ldmia sp!, {r4 - r11, pc}
@ENDP @ |Radix4First|
- .end
+ .fnend
diff --git a/media/libstagefright/codecs/aacenc/src/asm/ARMV7/Radix4FFT_v7.s b/media/libstagefright/codecs/aacenc/src/asm/ARMV7/Radix4FFT_v7.s
index f874825..431bc30 100644
--- a/media/libstagefright/codecs/aacenc/src/asm/ARMV7/Radix4FFT_v7.s
+++ b/media/libstagefright/codecs/aacenc/src/asm/ARMV7/Radix4FFT_v7.s
@@ -23,9 +23,13 @@
.section .text
.global Radix4FFT
+ .fnstart
Radix4FFT:
stmdb sp!, {r4 - r11, lr}
+ .save {r4 - r11, lr}
+ fstmfdd sp!, {d8 - d15}
+ .vsave {d8 - d15}
mov r1, r1, asr #2
cmp r1, #0
@@ -137,7 +141,8 @@ Radix4FFT_LOOP1_END:
bne Radix4FFT_LOOP1
Radix4FFT_END:
+ fldmfdd sp!, {d8 - d15}
ldmia sp!, {r4 - r11, pc}
@ENDP @ |Radix4FFT|
- .end
+ .fnend
diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
index 3afbc4f..27d7e4d 100644
--- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
@@ -18,8 +18,8 @@
#include "gsmamr_enc.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -210,7 +210,7 @@ status_t AMRNBEncoder::read(
}
MediaBuffer *buffer;
- CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+ CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
uint8_t *outPtr = (uint8_t *)buffer->data();
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.mk b/media/libstagefright/codecs/amrnb/enc/Android.mk
index b6aed81..94e8726 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.mk
+++ b/media/libstagefright/codecs/amrnb/enc/Android.mk
@@ -74,3 +74,30 @@ LOCAL_CFLAGS := \
LOCAL_MODULE := libstagefright_amrnbenc
include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAMRNBEncoder.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../common/include \
+ $(LOCAL_PATH)/../common
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_amrnbenc
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_amrnb_common
+
+LOCAL_MODULE := libstagefright_soft_amrnbenc
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
new file mode 100644
index 0000000..07f8b4f
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
@@ -0,0 +1,404 @@
+/*
+ * 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 "SoftAMRNBEncoder"
+#include <utils/Log.h>
+
+#include "SoftAMRNBEncoder.h"
+
+#include "gsmamr_enc.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+namespace android {
+
+static const int32_t kSampleRate = 8000;
+
+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;
+}
+
+SoftAMRNBEncoder::SoftAMRNBEncoder(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mEncState(NULL),
+ mSidState(NULL),
+ mBitRate(0),
+ mMode(MR475),
+ mInputSize(0),
+ mInputTimeUs(-1ll),
+ mSawInputEOS(false),
+ mSignalledError(false) {
+ initPorts();
+ CHECK_EQ(initEncoder(), (status_t)OK);
+}
+
+SoftAMRNBEncoder::~SoftAMRNBEncoder() {
+ if (mEncState != NULL) {
+ AMREncodeExit(&mEncState, &mSidState);
+ mEncState = mSidState = NULL;
+ }
+}
+
+void SoftAMRNBEncoder::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);
+ 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/3gpp");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAMR;
+
+ addPort(def);
+}
+
+status_t SoftAMRNBEncoder::initEncoder() {
+ if (AMREncodeInit(&mEncState, &mSidState, false /* dtx_enable */) != 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAMRNBEncoder::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_CodingAMR;
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAmr:
+ {
+ OMX_AUDIO_PARAM_AMRTYPE *amrParams =
+ (OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (amrParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ amrParams->nChannels = 1;
+ amrParams->nBitRate = mBitRate;
+ amrParams->eAMRBandMode = (OMX_AUDIO_AMRBANDMODETYPE)(mMode + 1);
+ amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
+ amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+
+ 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_ChannelCF;
+
+ pcmParams->nChannels = 1;
+ pcmParams->nSamplingRate = kSampleRate;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAMRNBEncoder::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.amrnb",
+ 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_CodingAMR)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAmr:
+ {
+ OMX_AUDIO_PARAM_AMRTYPE *amrParams =
+ (OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (amrParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (amrParams->nChannels != 1
+ || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff
+ || amrParams->eAMRFrameFormat
+ != OMX_AUDIO_AMRFrameFormatFSF
+ || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeNB0
+ || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeNB7) {
+ return OMX_ErrorUndefined;
+ }
+
+ mBitRate = amrParams->nBitRate;
+ mMode = amrParams->eAMRBandMode - 1;
+
+ amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
+ amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioPcm:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 0) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (pcmParams->nChannels != 1
+ || pcmParams->nSamplingRate != kSampleRate) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftAMRNBEncoder::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
+
+ 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" 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 (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 / kSampleRate) / sizeof(int16_t);
+
+ if (inHeader->nFilledLen == 0) {
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ ALOGV("saw input 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 = outHeader->pBuffer + outHeader->nOffset;
+ size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
+
+ Frame_Type_3GPP frameType;
+ int res = AMREncode(
+ mEncState, mSidState, (Mode)mMode,
+ mInputFrame, outPtr, &frameType, AMR_TX_WMF);
+
+ CHECK_GE(res, 0);
+ CHECK_LE((size_t)res, outAvailable);
+
+ // Convert header byte from WMF to IETF format.
+ outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c;
+
+ outHeader->nFilledLen = res;
+ 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::SoftAMRNBEncoder(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h
new file mode 100644
index 0000000..50178c4
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h
@@ -0,0 +1,72 @@
+/*
+ * 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_AMRNB_ENCODER_H_
+
+#define SOFT_AMRNB_ENCODER_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftAMRNBEncoder : public SimpleSoftOMXComponent {
+ SoftAMRNBEncoder(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAMRNBEncoder();
+
+ 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 = 160,
+ };
+
+ void *mEncState;
+ void *mSidState;
+
+ OMX_U32 mBitRate;
+ int mMode;
+
+ size_t mInputSize;
+ int16_t mInputFrame[kNumSamplesPerFrame];
+ int64_t mInputTimeUs;
+
+ bool mSawInputEOS;
+ bool mSignalledError;
+
+ void initPorts();
+ status_t initEncoder();
+
+ status_t setAudioParams();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAMRNBEncoder);
+};
+
+} // namespace android
+
+#endif // SOFT_AMRNB_ENCODER_H_
diff --git a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
index 60b1163..7fd3a95 100644
--- a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
@@ -22,8 +22,8 @@
#include "voAMRWB.h"
#include "cmnMemory.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -134,7 +134,7 @@ status_t AMRWBEncoder::start(MetaData *params) {
// The largest buffer size is header + 477 bits
mBufferGroup->add_buffer(new MediaBuffer(1024));
- CHECK_EQ(OK, initCheck());
+ CHECK_EQ((status_t)OK, initCheck());
mNumFramesOutput = 0;
@@ -163,7 +163,7 @@ status_t AMRWBEncoder::stop() {
mBufferGroup = NULL;
- CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+ CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
mEncoderHandle = NULL;
delete mApiHandle;
@@ -222,7 +222,7 @@ status_t AMRWBEncoder::read(
}
size_t align = mInputBuffer->range_length() % sizeof(int16_t);
- CHECK_EQ(align, 0);
+ CHECK_EQ(align, (size_t)0);
int64_t timeUs;
if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
@@ -271,7 +271,7 @@ status_t AMRWBEncoder::read(
CHECK(VO_ERR_NONE == mApiHandle->SetInputData(mEncoderHandle,&inputData));
MediaBuffer *buffer;
- CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+ CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
uint8_t *outPtr = (uint8_t *)buffer->data();
VO_CODECBUFFER outputData;
diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk
index ae43870..6ce6171 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.mk
+++ b/media/libstagefright/codecs/amrwbenc/Android.mk
@@ -117,4 +117,26 @@ endif
include $(BUILD_STATIC_LIBRARY)
+################################################################################
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SoftAMRWBEncoder.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+ frameworks/base/include/media/stagefright/openmax \
+ frameworks/base/media/libstagefright/codecs/common/include \
+
+LOCAL_STATIC_LIBRARIES := \
+ libstagefright_amrwbenc
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright_omx libstagefright_foundation libutils \
+ libstagefright_enc_common
+
+LOCAL_MODULE := libstagefright_soft_amrwbenc
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
new file mode 100644
index 0000000..9ccb49c
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp
@@ -0,0 +1,459 @@
+/*
+ * 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 "SoftAMRWBEncoder"
+#include <utils/Log.h>
+
+#include "SoftAMRWBEncoder.h"
+
+#include "cmnMemory.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+namespace android {
+
+static const int32_t kSampleRate = 16000;
+
+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;
+}
+
+SoftAMRWBEncoder::SoftAMRWBEncoder(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SimpleSoftOMXComponent(name, callbacks, appData, component),
+ mEncoderHandle(NULL),
+ mApiHandle(NULL),
+ mMemOperator(NULL),
+ mBitRate(0),
+ mMode(VOAMRWB_MD66),
+ mInputSize(0),
+ mInputTimeUs(-1ll),
+ mSawInputEOS(false),
+ mSignalledError(false) {
+ initPorts();
+ CHECK_EQ(initEncoder(), (status_t)OK);
+}
+
+SoftAMRWBEncoder::~SoftAMRWBEncoder() {
+ if (mEncoderHandle != NULL) {
+ CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+ mEncoderHandle = NULL;
+ }
+
+ delete mApiHandle;
+ mApiHandle = NULL;
+
+ delete mMemOperator;
+ mMemOperator = NULL;
+}
+
+void SoftAMRWBEncoder::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);
+ 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/amr-wb");
+ def.format.audio.pNativeRender = NULL;
+ def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAMR;
+
+ addPort(def);
+}
+
+status_t SoftAMRWBEncoder::initEncoder() {
+ mApiHandle = new VO_AUDIO_CODECAPI;
+
+ if (VO_ERR_NONE != voGetAMRWBEncAPI(mApiHandle)) {
+ ALOGE("Failed to get api handle");
+ return UNKNOWN_ERROR;
+ }
+
+ mMemOperator = new VO_MEM_OPERATOR;
+ mMemOperator->Alloc = cmnMemAlloc;
+ mMemOperator->Copy = cmnMemCopy;
+ mMemOperator->Free = cmnMemFree;
+ mMemOperator->Set = cmnMemSet;
+ mMemOperator->Check = cmnMemCheck;
+
+ VO_CODEC_INIT_USERDATA userData;
+ memset(&userData, 0, sizeof(userData));
+ userData.memflag = VO_IMF_USERMEMOPERATOR;
+ userData.memData = (VO_PTR) mMemOperator;
+
+ if (VO_ERR_NONE != mApiHandle->Init(
+ &mEncoderHandle, VO_AUDIO_CodingAMRWB, &userData)) {
+ ALOGE("Failed to init AMRWB encoder");
+ return UNKNOWN_ERROR;
+ }
+
+ VOAMRWBFRAMETYPE type = VOAMRWB_RFC3267;
+ if (VO_ERR_NONE != mApiHandle->SetParam(
+ mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &type)) {
+ ALOGE("Failed to set AMRWB encoder frame type to %d", type);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+OMX_ERRORTYPE SoftAMRWBEncoder::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_CodingAMR;
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAmr:
+ {
+ OMX_AUDIO_PARAM_AMRTYPE *amrParams =
+ (OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (amrParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ amrParams->nChannels = 1;
+ amrParams->nBitRate = mBitRate;
+
+ amrParams->eAMRBandMode =
+ (OMX_AUDIO_AMRBANDMODETYPE)(mMode + OMX_AUDIO_AMRBandModeWB0);
+
+ amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
+ amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+
+ 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_ChannelCF;
+
+ pcmParams->nChannels = 1;
+ pcmParams->nSamplingRate = kSampleRate;
+
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return SimpleSoftOMXComponent::internalGetParameter(index, params);
+ }
+}
+
+OMX_ERRORTYPE SoftAMRWBEncoder::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.amrwb",
+ 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_CodingAMR)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+ case OMX_IndexParamAudioAmr:
+ {
+ OMX_AUDIO_PARAM_AMRTYPE *amrParams =
+ (OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+ if (amrParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (amrParams->nChannels != 1
+ || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff
+ || amrParams->eAMRFrameFormat
+ != OMX_AUDIO_AMRFrameFormatFSF
+ || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeWB0
+ || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeWB8) {
+ return OMX_ErrorUndefined;
+ }
+
+ mBitRate = amrParams->nBitRate;
+
+ mMode = (VOAMRWBMODE)(
+ amrParams->eAMRBandMode - OMX_AUDIO_AMRBandModeWB0);
+
+ amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
+ amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+
+ if (VO_ERR_NONE !=
+ mApiHandle->SetParam(
+ mEncoderHandle, VO_PID_AMRWB_MODE, &mMode)) {
+ ALOGE("Failed to set AMRWB encoder mode to %d", mMode);
+ 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;
+ }
+
+ if (pcmParams->nChannels != 1
+ || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+
+ default:
+ return SimpleSoftOMXComponent::internalSetParameter(index, params);
+ }
+}
+
+void SoftAMRWBEncoder::onQueueFilled(OMX_U32 portIndex) {
+ if (mSignalledError) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(0);
+ List<BufferInfo *> &outQueue = getPortQueue(1);
+
+ size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
+
+ 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" 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 (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 / kSampleRate) / sizeof(int16_t);
+
+ if (inHeader->nFilledLen == 0) {
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ ALOGV("saw input 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 = outHeader->pBuffer + outHeader->nOffset;
+ size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
+
+ VO_CODECBUFFER inputData;
+ memset(&inputData, 0, sizeof(inputData));
+ inputData.Buffer = (unsigned char *) mInputFrame;
+ inputData.Length = mInputSize;
+
+ CHECK_EQ(VO_ERR_NONE,
+ mApiHandle->SetInputData(mEncoderHandle, &inputData));
+
+ VO_CODECBUFFER outputData;
+ memset(&outputData, 0, sizeof(outputData));
+ VO_AUDIO_OUTPUTINFO outputInfo;
+ memset(&outputInfo, 0, sizeof(outputInfo));
+
+ outputData.Buffer = outPtr;
+ outputData.Length = outAvailable;
+ VO_U32 ret = mApiHandle->GetOutputData(
+ mEncoderHandle, &outputData, &outputInfo);
+ CHECK(ret == VO_ERR_NONE || ret == VO_ERR_INPUT_BUFFER_SMALL);
+
+ outHeader->nFilledLen = outputData.Length;
+ 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 %ld bytes of data (time = %lld us, flags = 0x%08lx)",
+ outHeader->nFilledLen, 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::SoftAMRWBEncoder(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h
new file mode 100644
index 0000000..d0c1dab
--- /dev/null
+++ b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h
@@ -0,0 +1,76 @@
+/*
+ * 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_AMRWB_ENCODER_H_
+
+#define SOFT_AMRWB_ENCODER_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+#include "voAMRWB.h"
+
+struct VO_AUDIO_CODECAPI;
+struct VO_MEM_OPERATOR;
+
+namespace android {
+
+struct SoftAMRWBEncoder : public SimpleSoftOMXComponent {
+ SoftAMRWBEncoder(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftAMRWBEncoder();
+
+ 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 = 320,
+ };
+
+ void *mEncoderHandle;
+ VO_AUDIO_CODECAPI *mApiHandle;
+ VO_MEM_OPERATOR *mMemOperator;
+
+ OMX_U32 mBitRate;
+ VOAMRWBMODE mMode;
+
+ size_t mInputSize;
+ int16_t mInputFrame[kNumSamplesPerFrame];
+ int64_t mInputTimeUs;
+
+ bool mSawInputEOS;
+ bool mSignalledError;
+
+ void initPorts();
+ status_t initEncoder();
+
+ DISALLOW_EVIL_CONSTRUCTORS(SoftAMRWBEncoder);
+};
+
+} // namespace android
+
+#endif // SOFT_AMRWB_ENCODER_H_
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index e202a2b..7533f07 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -24,8 +24,8 @@
#include "avcenc_int.h"
#include "OMX_Video.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -417,7 +417,7 @@ status_t AVCEncoder::read(
*out = NULL;
MediaBuffer *outputBuffer;
- CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+ CHECK_EQ((status_t)OK, mGroup->acquire_buffer(&outputBuffer));
uint8_t *outPtr = (uint8_t *) outputBuffer->data();
uint32_t dataLength = outputBuffer->size();
@@ -557,9 +557,9 @@ status_t AVCEncoder::read(
encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
if (encoderStatus == AVCENC_SUCCESS) {
outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
- CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
+ CHECK(NULL == PVAVCEncGetOverrunBuffer(mHandle));
} else if (encoderStatus == AVCENC_PICTURE_READY) {
- CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
+ CHECK(NULL == PVAVCEncGetOverrunBuffer(mHandle));
if (mIsIDRFrame) {
outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
mIsIDRFrame = 0;
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index d538603..20b0f8d 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -23,8 +23,8 @@
#include "mp4enc_api.h"
#include "OMX_Video.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -379,7 +379,7 @@ status_t M4vH263Encoder::read(
*out = NULL;
MediaBuffer *outputBuffer;
- CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+ CHECK_EQ((status_t)OK, mGroup->acquire_buffer(&outputBuffer));
uint8_t *outPtr = (uint8_t *) outputBuffer->data();
int32_t dataLength = outputBuffer->size();
@@ -467,7 +467,7 @@ status_t M4vH263Encoder::read(
mInputBuffer = NULL;
return UNKNOWN_ERROR;
}
- CHECK_EQ(NULL, PVGetOverrunBuffer(mHandle));
+ CHECK(NULL == PVGetOverrunBuffer(mHandle));
if (hintTrack.CodeType == 0) { // I-frame serves as sync frame
outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
}
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 5cc3f78..597167f 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -18,8 +18,8 @@
#define LOG_TAG "ColorConverter"
#include <utils/Log.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/ColorConverter.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaErrors.h>
namespace android {
@@ -144,8 +144,8 @@ status_t ColorConverter::convertCbYCrY(
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_ptr = (const uint8_t *)src.mBits
+ (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
@@ -182,11 +182,15 @@ status_t ColorConverter::convertCbYCrY(
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[b2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_ptr += src.mWidth * 2;
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
@@ -290,15 +294,14 @@ status_t ColorConverter::convertQCOMYUV420SemiPlanar(
const BitmapParams &src, const BitmapParams &dst) {
uint8_t *kAdjustedClip = initClip();
- if (!((dst.mWidth & 3) == 0
- && (src.mCropLeft & 1) == 0
+ if (!((src.mCropLeft & 1) == 0
&& src.cropWidth() == dst.cropWidth()
&& src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_y =
(const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
@@ -340,7 +343,11 @@ status_t ColorConverter::convertQCOMYUV420SemiPlanar(
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[r2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_y += src.mWidth;
@@ -349,7 +356,7 @@ status_t ColorConverter::convertQCOMYUV420SemiPlanar(
src_u += src.mWidth;
}
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
@@ -361,15 +368,14 @@ status_t ColorConverter::convertYUV420SemiPlanar(
uint8_t *kAdjustedClip = initClip();
- if (!((dst.mWidth & 3) == 0
- && (src.mCropLeft & 1) == 0
+ if (!((src.mCropLeft & 1) == 0
&& src.cropWidth() == dst.cropWidth()
&& src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_y =
(const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
@@ -411,7 +417,11 @@ status_t ColorConverter::convertYUV420SemiPlanar(
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[r2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_y += src.mWidth;
@@ -420,7 +430,7 @@ status_t ColorConverter::convertYUV420SemiPlanar(
src_u += src.mWidth;
}
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
@@ -430,15 +440,14 @@ status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
const BitmapParams &src, const BitmapParams &dst) {
uint8_t *kAdjustedClip = initClip();
- if (!((dst.mWidth & 3) == 0
- && (src.mCropLeft & 1) == 0
+ if (!((src.mCropLeft & 1) == 0
&& src.cropWidth() == dst.cropWidth()
&& src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_y = (const uint8_t *)src.mBits;
@@ -478,7 +487,11 @@ status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[b2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_y += src.mWidth;
@@ -487,7 +500,7 @@ status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
src_u += src.mWidth;
}
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index e892f92..b8516af 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -23,8 +23,7 @@
#include <binder/MemoryHeapPmem.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MetaData.h>
-#include <surfaceflinger/Surface.h>
-#include <ui/android_native_buffer.h>
+#include <system/window.h>
#include <ui/GraphicBufferMapper.h>
#include <gui/ISurfaceTexture.h>
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 0a6776e..9a00186 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -19,6 +19,7 @@
#include <ctype.h>
#include "AAtomizer.h"
+#include "ABuffer.h"
#include "ADebug.h"
#include "ALooperRoster.h"
#include "AString.h"
@@ -157,14 +158,23 @@ void AMessage::setString(
item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
}
-void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+void AMessage::setObjectInternal(
+ const char *name, const sp<RefBase> &obj, Type type) {
Item *item = allocateItem(name);
- item->mType = kTypeObject;
+ item->mType = type;
if (obj != NULL) { obj->incStrong(this); }
item->u.refValue = obj.get();
}
+void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+ setObjectInternal(name, obj, kTypeObject);
+}
+
+void AMessage::setBuffer(const char *name, const sp<ABuffer> &buffer) {
+ setObjectInternal(name, sp<RefBase>(buffer), kTypeBuffer);
+}
+
void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
Item *item = allocateItem(name);
item->mType = kTypeMessage;
@@ -203,6 +213,15 @@ bool AMessage::findObject(const char *name, sp<RefBase> *obj) const {
return false;
}
+bool AMessage::findBuffer(const char *name, sp<ABuffer> *buf) const {
+ const Item *item = findItem(name, kTypeBuffer);
+ if (item) {
+ *buf = (ABuffer *)(item->u.refValue);
+ return true;
+ }
+ return false;
+}
+
bool AMessage::findMessage(const char *name, sp<AMessage> *obj) const {
const Item *item = findItem(name, kTypeMessage);
if (item) {
@@ -542,4 +561,20 @@ void AMessage::writeToParcel(Parcel *parcel) const {
}
}
+size_t AMessage::countEntries() const {
+ return mNumItems;
+}
+
+const char *AMessage::getEntryNameAt(size_t index, Type *type) const {
+ if (index >= mNumItems) {
+ *type = kTypeInt32;
+
+ return NULL;
+ }
+
+ *type = mItems[index].mType;
+
+ return mItems[index].mName;
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 0df66f1..0cddd2e 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -215,7 +215,9 @@ void LiveSession::onDisconnect() {
mDisconnectPending = false;
}
-status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) {
+status_t LiveSession::fetchFile(
+ const char *url, sp<ABuffer> *out,
+ int64_t range_offset, int64_t range_length) {
*out = NULL;
sp<DataSource> source;
@@ -234,8 +236,18 @@ status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) {
}
}
- status_t err = mHTTPDataSource->connect(
- url, mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
+ KeyedVector<String8, String8> headers = mExtraHeaders;
+ if (range_offset > 0 || range_length >= 0) {
+ headers.add(
+ String8("Range"),
+ String8(
+ StringPrintf(
+ "bytes=%lld-%s",
+ range_offset,
+ range_length < 0
+ ? "" : StringPrintf("%lld", range_offset + range_length - 1).c_str()).c_str()));
+ }
+ status_t err = mHTTPDataSource->connect(url, &headers);
if (err != OK) {
return err;
@@ -270,9 +282,21 @@ status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) {
buffer = copy;
}
+ size_t maxBytesToRead = bufferRemaining;
+ if (range_length >= 0) {
+ int64_t bytesLeftInRange = range_length - buffer->size();
+ if (bytesLeftInRange < maxBytesToRead) {
+ maxBytesToRead = bytesLeftInRange;
+
+ if (bytesLeftInRange == 0) {
+ break;
+ }
+ }
+ }
+
ssize_t n = source->readAt(
buffer->size(), buffer->data() + buffer->size(),
- bufferRemaining);
+ maxBytesToRead);
if (n < 0) {
return n;
@@ -659,8 +683,15 @@ rinse_repeat:
explicitDiscontinuity = true;
}
+ int64_t range_offset, range_length;
+ if (!itemMeta->findInt64("range-offset", &range_offset)
+ || !itemMeta->findInt64("range-length", &range_length)) {
+ range_offset = 0;
+ range_length = -1;
+ }
+
sp<ABuffer> buffer;
- status_t err = fetchFile(uri.c_str(), &buffer);
+ status_t err = fetchFile(uri.c_str(), &buffer, range_offset, range_length);
if (err != OK) {
ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
mDataSource->queueEOS(err);
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 5e30488..7d3cf05 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -152,6 +152,7 @@ status_t M3UParser::parse(const void *_data, size_t size) {
const char *data = (const char *)_data;
size_t offset = 0;
+ uint64_t segmentRangeOffset = 0;
while (offset < size) {
size_t offsetLF = offset;
while (offsetLF < size && data[offsetLF] != '\n') {
@@ -218,6 +219,24 @@ status_t M3UParser::parse(const void *_data, size_t size) {
}
mIsVariantPlaylist = true;
err = parseStreamInf(line, &itemMeta);
+ } else if (line.startsWith("#EXT-X-BYTERANGE")) {
+ if (mIsVariantPlaylist) {
+ return ERROR_MALFORMED;
+ }
+
+ uint64_t length, offset;
+ err = parseByteRange(line, segmentRangeOffset, &length, &offset);
+
+ if (err == OK) {
+ if (itemMeta == NULL) {
+ itemMeta = new AMessage;
+ }
+
+ itemMeta->setInt64("range-offset", offset);
+ itemMeta->setInt64("range-length", length);
+
+ segmentRangeOffset = offset + length;
+ }
}
if (err != OK) {
@@ -447,6 +466,52 @@ status_t M3UParser::parseCipherInfo(
}
// static
+status_t M3UParser::parseByteRange(
+ const AString &line, uint64_t curOffset,
+ uint64_t *length, uint64_t *offset) {
+ ssize_t colonPos = line.find(":");
+
+ if (colonPos < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ ssize_t atPos = line.find("@", colonPos + 1);
+
+ AString lenStr;
+ if (atPos < 0) {
+ lenStr = AString(line, colonPos + 1, line.size() - colonPos - 1);
+ } else {
+ lenStr = AString(line, colonPos + 1, atPos - colonPos - 1);
+ }
+
+ lenStr.trim();
+
+ const char *s = lenStr.c_str();
+ char *end;
+ *length = strtoull(s, &end, 10);
+
+ if (s == end || *end != '\0') {
+ return ERROR_MALFORMED;
+ }
+
+ if (atPos >= 0) {
+ AString offStr = AString(line, atPos + 1, line.size() - atPos - 1);
+ offStr.trim();
+
+ const char *s = offStr.c_str();
+ *offset = strtoull(s, &end, 10);
+
+ if (s == end || *end != '\0') {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ *offset = curOffset;
+ }
+
+ return OK;
+}
+
+// static
status_t M3UParser::ParseInt32(const char *s, int32_t *x) {
char *end;
long lval = strtol(s, &end, 10);
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 23c8e44..ff35d4a 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \
testid3.cpp
LOCAL_SHARED_LIBRARIES := \
- libstagefright libutils libbinder
+ libstagefright libutils libbinder libstagefright_foundation
LOCAL_STATIC_LIBRARIES := \
libstagefright_id3
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 6dde9d8..2e92926 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -20,8 +20,8 @@
#include "../include/ID3.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/Utils.h>
#include <utils/String8.h>
#include <byteswap.h>
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index 0741045..bc4572c 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -23,7 +23,7 @@
#include <binder/ProcessState.h>
#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#define MAXPATHLEN 256
@@ -70,7 +70,7 @@ static void hexdump(const void *_data, size_t size) {
void scanFile(const char *path) {
sp<FileSource> file = new FileSource(path);
- CHECK_EQ(file->initCheck(), OK);
+ CHECK_EQ(file->initCheck(), (status_t)OK);
ID3 tag(file);
if (!tag.isValid()) {
diff --git a/media/libstagefright/include/AACExtractor.h b/media/libstagefright/include/AACExtractor.h
index 8e5657b..e98ca82 100644
--- a/media/libstagefright/include/AACExtractor.h
+++ b/media/libstagefright/include/AACExtractor.h
@@ -29,7 +29,7 @@ class String8;
class AACExtractor : public MediaExtractor {
public:
- AACExtractor(const sp<DataSource> &source);
+ AACExtractor(const sp<DataSource> &source, const sp<AMessage> &meta);
virtual size_t countTracks();
virtual sp<MediaSource> getTrack(size_t index);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 0985f47..4c7bfa6 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -41,7 +41,7 @@ struct ISurfaceTexture;
class DrmManagerClinet;
class DecryptHandle;
-class TimedTextPlayer;
+class TimedTextDriver;
struct WVMExtractor;
struct AwesomeRenderer : public RefBase {
@@ -232,7 +232,7 @@ private:
sp<DecryptHandle> mDecryptHandle;
int64_t mLastVideoTimeUs;
- TimedTextPlayer *mTextPlayer;
+ TimedTextDriver *mTextDriver;
mutable Mutex mTimedTextLock;
sp<WVMExtractor> mWVMExtractor;
@@ -258,7 +258,7 @@ private:
void setVideoSource(sp<MediaSource> source);
status_t initVideoDecoder(uint32_t flags = 0);
- void addTextSource(sp<MediaSource> source);
+ void addTextSource(const sp<MediaSource>& source);
void onStreamDone();
@@ -290,6 +290,7 @@ private:
bool isStreamingHTTP() const;
void sendCacheStats();
+ void checkDrmStatus(const sp<DataSource>& dataSource);
enum FlagMode {
SET,
@@ -325,4 +326,3 @@ private:
} // namespace android
#endif // AWESOME_PLAYER_H_
-
diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h
index 18f8913..82e08fd 100644
--- a/media/libstagefright/include/ChromiumHTTPDataSource.h
+++ b/media/libstagefright/include/ChromiumHTTPDataSource.h
@@ -43,7 +43,7 @@ struct ChromiumHTTPDataSource : public HTTPBase {
virtual status_t getSize(off64_t *size);
virtual uint32_t flags();
- virtual sp<DecryptHandle> DrmInitialization();
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime);
virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
diff --git a/media/libstagefright/include/DataUriSource.h b/media/libstagefright/include/DataUriSource.h
new file mode 100644
index 0000000..d223c06
--- /dev/null
+++ b/media/libstagefright/include/DataUriSource.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 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 DATA_URI_SOURCE_H_
+
+#define DATA_URI_SOURCE_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/foundation/AString.h>
+
+namespace android {
+
+class DataUriSource : public DataSource {
+public:
+ DataUriSource(const char *uri);
+
+ virtual status_t initCheck() const {
+ return mInited;
+ }
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+ virtual status_t getSize(off64_t *size) {
+ if (mInited != OK) {
+ return mInited;
+ }
+
+ *size = mData.size();
+ return OK;
+ }
+
+ virtual String8 getUri() {
+ return mDataUri;
+ }
+
+ virtual String8 getMIMEType() const {
+ return mMimeType;
+ }
+
+protected:
+ virtual ~DataUriSource() {
+ // Nothing to delete.
+ }
+
+private:
+ const String8 mDataUri;
+
+ String8 mMimeType;
+ // Use AString because individual bytes may not be valid UTF8 chars.
+ AString mData;
+ status_t mInited;
+
+ // Disallow copy and assign.
+ DataUriSource(const DataUriSource &);
+ DataUriSource &operator=(const DataUriSource &);
+};
+
+} // namespace android
+
+#endif // DATA_URI_SOURCE_H_
diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h
index 116ed0e..3a11612 100644
--- a/media/libstagefright/include/LiveSession.h
+++ b/media/libstagefright/include/LiveSession.h
@@ -120,7 +120,10 @@ private:
void onMonitorQueue();
void onSeek(const sp<AMessage> &msg);
- status_t fetchFile(const char *url, sp<ABuffer> *out);
+ status_t fetchFile(
+ const char *url, sp<ABuffer> *out,
+ int64_t range_offset = 0, int64_t range_length = -1);
+
sp<M3UParser> fetchPlaylist(const char *url, bool *unchanged);
size_t getBandwidthIndex();
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index 478582d..e30d6fd 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -72,6 +72,10 @@ private:
static status_t parseCipherInfo(
const AString &line, sp<AMessage> *meta, const AString &baseURI);
+ static status_t parseByteRange(
+ const AString &line, uint64_t curOffset,
+ uint64_t *length, uint64_t *offset);
+
static status_t ParseInt32(const char *s, int32_t *x);
static status_t ParseDouble(const char *s, double *x);
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 7a03e7e..c27a29b 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -40,7 +40,7 @@ struct NuCachedSource2 : public DataSource {
virtual status_t getSize(off64_t *size);
virtual uint32_t flags();
- virtual sp<DecryptHandle> DrmInitialization();
+ virtual sp<DecryptHandle> DrmInitialization(const char* mime);
virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
virtual String8 getUri();
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 53e764f..2c87b34 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -31,7 +31,7 @@ class OMX : public BnOMX,
public:
OMX();
- virtual bool livesLocally(pid_t pid);
+ virtual bool livesLocally(node_id node, pid_t pid);
virtual status_t listNodes(List<ComponentInfo> *list);
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 8f2ea95..7ab0042 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -20,7 +20,7 @@
#include <media/stagefright/ColorConverter.h>
#include <utils/RefBase.h>
-#include <ui/android_native_buffer.h>
+#include <system/window.h>
namespace android {
diff --git a/media/libstagefright/include/ThrottledSource.h b/media/libstagefright/include/ThrottledSource.h
index 8928a4a..7fe7c06 100644
--- a/media/libstagefright/include/ThrottledSource.h
+++ b/media/libstagefright/include/ThrottledSource.h
@@ -35,6 +35,11 @@ struct ThrottledSource : public DataSource {
virtual status_t getSize(off64_t *size);
virtual uint32_t flags();
+ virtual String8 getMIMEType() const {
+ return mSource->getMIMEType();
+ }
+
+
private:
Mutex mLock;
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index deecd25..9f763f9 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -23,6 +23,8 @@
namespace android {
+struct AMessage;
+class String8;
class DataSource;
class WVMLoadableExtractor : public MediaExtractor {
@@ -58,6 +60,8 @@ public:
// is used.
void setAdaptiveStreamingMode(bool adaptive);
+ static bool getVendorLibHandle();
+
protected:
virtual ~WVMExtractor();
@@ -69,6 +73,10 @@ private:
WVMExtractor &operator=(const WVMExtractor &);
};
+bool SniffWVM(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
} // namespace android
#endif // DRM_EXTRACTOR_H_
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 4fbf47e..a0db719 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -93,7 +93,10 @@ struct BlockIterator {
void advance();
void reset();
- void seek(int64_t seekTimeUs, bool seekToKeyFrame);
+
+ void seek(
+ int64_t seekTimeUs, bool seekToKeyFrame,
+ int64_t *actualFrameTimeUs);
const mkvparser::Block *block() const;
int64_t blockTimeUs() const;
@@ -303,23 +306,53 @@ void BlockIterator::reset() {
} while (!eos() && block()->GetTrackNumber() != mTrackNum);
}
-void BlockIterator::seek(int64_t seekTimeUs, bool seekToKeyFrame) {
+void BlockIterator::seek(
+ int64_t seekTimeUs, bool seekToKeyFrame,
+ int64_t *actualFrameTimeUs) {
Mutex::Autolock autoLock(mExtractor->mLock);
- mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
+ *actualFrameTimeUs = -1ll;
+
+ int64_t seekTimeNs = seekTimeUs * 1000ll;
+
+ mCluster = mExtractor->mSegment->FindCluster(seekTimeNs);
mBlockEntry = NULL;
mBlockEntryIndex = 0;
- do {
+ long prevKeyFrameBlockEntryIndex = -1;
+
+ for (;;) {
advance_l();
- }
- while (!eos() && block()->GetTrackNumber() != mTrackNum);
- if (seekToKeyFrame) {
- while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
- advance_l();
+ if (eos()) {
+ break;
+ }
+
+ if (block()->GetTrackNumber() != mTrackNum) {
+ continue;
+ }
+
+ if (block()->IsKey()) {
+ prevKeyFrameBlockEntryIndex = mBlockEntryIndex - 1;
+ }
+
+ int64_t timeNs = block()->GetTime(mCluster);
+
+ if (timeNs >= seekTimeNs) {
+ *actualFrameTimeUs = (timeNs + 500ll) / 1000ll;
+ break;
}
}
+
+ if (eos()) {
+ return;
+ }
+
+ if (seekToKeyFrame && !block()->IsKey()) {
+ CHECK_GE(prevKeyFrameBlockEntryIndex, 0);
+ mBlockEntryIndex = prevKeyFrameBlockEntryIndex;
+ advance_l();
+ }
}
const mkvparser::Block *BlockIterator::block() const {
@@ -397,6 +430,8 @@ status_t MatroskaSource::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
+ int64_t targetSampleTimeUs = -1ll;
+
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)
@@ -406,10 +441,14 @@ status_t MatroskaSource::read(
// Apparently keyframe indication in audio tracks is unreliable,
// fortunately in all our currently supported audio encodings every
// frame is effectively a keyframe.
- mBlockIter.seek(seekTimeUs, !mIsAudio);
+ int64_t actualFrameTimeUs;
+ mBlockIter.seek(seekTimeUs, !mIsAudio, &actualFrameTimeUs);
+
+ if (mode == ReadOptions::SEEK_CLOSEST) {
+ targetSampleTimeUs = actualFrameTimeUs;
+ }
}
-again:
while (mPendingFrames.empty()) {
status_t err = readBlock();
@@ -424,6 +463,11 @@ again:
mPendingFrames.erase(mPendingFrames.begin());
if (mType != AVC) {
+ if (targetSampleTimeUs >= 0ll) {
+ frame->meta_data()->setInt64(
+ kKeyTargetTime, targetSampleTimeUs);
+ }
+
*out = frame;
return OK;
@@ -506,6 +550,11 @@ again:
frame->release();
frame = NULL;
+ if (targetSampleTimeUs >= 0ll) {
+ buffer->meta_data()->setInt64(
+ kKeyTargetTime, targetSampleTimeUs);
+ }
+
*out = buffer;
return OK;
@@ -610,36 +659,41 @@ bool MatroskaExtractor::isLiveStreaming() const {
return mIsLiveStreaming;
}
-static void addESDSFromAudioSpecificInfo(
- const sp<MetaData> &meta, const void *asi, size_t asiSize) {
+static void addESDSFromCodecPrivate(
+ const sp<MetaData> &meta,
+ bool isAudio, const void *priv, size_t privSize) {
static const uint8_t kStaticESDS[] = {
0x03, 22,
0x00, 0x00, // ES_ID
0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
0x04, 17,
- 0x40, // Audio ISO/IEC 14496-3
+ 0x40, // ObjectTypeIndication
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x05,
- // AudioSpecificInfo (with size prefix) follows
+ // CodecSpecificInfo (with size prefix) follows
};
// Make sure all sizes can be coded in a single byte.
- CHECK(asiSize + 22 - 2 < 128);
- size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1;
+ CHECK(privSize + 22 - 2 < 128);
+ size_t esdsSize = sizeof(kStaticESDS) + privSize + 1;
uint8_t *esds = new uint8_t[esdsSize];
memcpy(esds, kStaticESDS, sizeof(kStaticESDS));
uint8_t *ptr = esds + sizeof(kStaticESDS);
- *ptr++ = asiSize;
- memcpy(ptr, asi, asiSize);
+ *ptr++ = privSize;
+ memcpy(ptr, priv, privSize);
// Increment by codecPrivateSize less 2 bytes that are accounted for
// already in lengths of 22/17
- esds[1] += asiSize - 2;
- esds[6] += asiSize - 2;
+ esds[1] += privSize - 2;
+ esds[6] += privSize - 2;
+
+ // Set ObjectTypeIndication.
+ esds[7] = isAudio ? 0x40 // Audio ISO/IEC 14496-3
+ : 0x20; // Visual ISO/IEC 14496-2
meta->setData(kKeyESDS, 0, esds, esdsSize);
@@ -707,9 +761,21 @@ void MatroskaExtractor::addTracks() {
if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
+ } else if (!strcmp("V_MPEG4/ISO/ASP", codecID)) {
+ if (codecPrivateSize > 0) {
+ meta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ addESDSFromCodecPrivate(
+ meta, false, codecPrivate, codecPrivateSize);
+ } else {
+ ALOGW("%s is detected, but does not have configuration.",
+ codecID);
+ continue;
+ }
} else if (!strcmp("V_VP8", codecID)) {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX);
} else {
+ ALOGW("%s is not supported.", codecID);
continue;
}
@@ -727,13 +793,16 @@ void MatroskaExtractor::addTracks() {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
CHECK(codecPrivateSize >= 2);
- addESDSFromAudioSpecificInfo(
- meta, codecPrivate, codecPrivateSize);
+ addESDSFromCodecPrivate(
+ meta, true, codecPrivate, codecPrivateSize);
} else if (!strcmp("A_VORBIS", codecID)) {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize);
+ } else if (!strcmp("A_MPEG/L3", codecID)) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
} else {
+ ALOGW("%s is not supported.", codecID);
continue;
}
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 03033f5..e1589b4 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -22,8 +22,8 @@
#include "include/LiveSession.h"
#include "include/NuCachedSource2.h"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 694b12d..f11fcd2 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -25,7 +25,7 @@
#include "../include/OMXNodeInstance.h"
#include <binder/IMemory.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <utils/threads.h>
#include "OMXMaster.h"
@@ -102,7 +102,7 @@ OMX::CallbackDispatcher::~CallbackDispatcher() {
if (status != WOULD_BLOCK) {
// Other than join to self, the only other error return codes are
// whatever readyToRun() returns, and we don't override that
- CHECK_EQ(status, NO_ERROR);
+ CHECK_EQ(status, (status_t)NO_ERROR);
}
}
@@ -185,7 +185,7 @@ void OMX::binderDied(const wp<IBinder> &the_late_who) {
instance->onObserverDied(mMaster);
}
-bool OMX::livesLocally(pid_t pid) {
+bool OMX::livesLocally(node_id node, pid_t pid) {
return pid == getpid();
}
diff --git a/media/libstagefright/omx/OMXComponentBase.cpp b/media/libstagefright/omx/OMXComponentBase.cpp
index 35227a0..7d11dce 100644
--- a/media/libstagefright/omx/OMXComponentBase.cpp
+++ b/media/libstagefright/omx/OMXComponentBase.cpp
@@ -18,7 +18,7 @@
#include <stdlib.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
namespace android {
@@ -33,7 +33,7 @@ OMXComponentBase::OMXComponentBase(
OMXComponentBase::~OMXComponentBase() {}
void OMXComponentBase::setComponentHandle(OMX_COMPONENTTYPE *handle) {
- CHECK_EQ(mComponentHandle, NULL);
+ CHECK(mComponentHandle == NULL);
mComponentHandle = handle;
}
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index d698939..6b6d0ab 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -24,7 +24,7 @@
#include <dlfcn.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
namespace android {
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 8938e33..099c4f5 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -24,8 +24,8 @@
#include <OMX_Component.h>
#include <binder/IMemory.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/HardwareAPI.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaErrors.h>
namespace android {
@@ -91,11 +91,11 @@ OMXNodeInstance::OMXNodeInstance(
}
OMXNodeInstance::~OMXNodeInstance() {
- CHECK_EQ(mHandle, NULL);
+ CHECK(mHandle == NULL);
}
void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) {
- CHECK_EQ(mHandle, NULL);
+ CHECK(mHandle == NULL);
mNodeID = node_id;
mHandle = handle;
}
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 0914f32..c79e01f 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -333,8 +333,9 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) {
void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {
Mutex::Autolock autoLock(mLock);
-
- switch (msg->what()) {
+ uint32_t msgType = msg->what();
+ ALOGV("msgType = %d", msgType);
+ switch (msgType) {
case kWhatSendCommand:
{
int32_t cmd, param;
@@ -354,27 +355,27 @@ void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {
CHECK(mState == OMX_StateExecuting && mTargetState == mState);
bool found = false;
- for (size_t i = 0; i < mPorts.size(); ++i) {
- PortInfo *port = &mPorts.editItemAt(i);
+ size_t portIndex = (kWhatEmptyThisBuffer == msgType)?
+ header->nInputPortIndex: header->nOutputPortIndex;
+ PortInfo *port = &mPorts.editItemAt(portIndex);
- for (size_t j = 0; j < port->mBuffers.size(); ++j) {
- BufferInfo *buffer = &port->mBuffers.editItemAt(j);
+ for (size_t j = 0; j < port->mBuffers.size(); ++j) {
+ BufferInfo *buffer = &port->mBuffers.editItemAt(j);
- if (buffer->mHeader == header) {
- CHECK(!buffer->mOwnedByUs);
+ if (buffer->mHeader == header) {
+ CHECK(!buffer->mOwnedByUs);
- buffer->mOwnedByUs = true;
+ buffer->mOwnedByUs = true;
- CHECK((msg->what() == kWhatEmptyThisBuffer
- && port->mDef.eDir == OMX_DirInput)
- || (port->mDef.eDir == OMX_DirOutput));
+ CHECK((msgType == kWhatEmptyThisBuffer
+ && port->mDef.eDir == OMX_DirInput)
+ || (port->mDef.eDir == OMX_DirOutput));
- port->mQueue.push_back(buffer);
- onQueueFilled(i);
+ port->mQueue.push_back(buffer);
+ onQueueFilled(portIndex);
- found = true;
- break;
- }
+ found = true;
+ break;
}
}
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index da3ae42..99ffe7d 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -35,8 +35,11 @@ static const struct {
} kComponents[] = {
{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
+ { "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
+ { "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
+ { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
{ "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
{ "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
{ "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
index bf69428..0c0a70c 100644
--- a/media/libstagefright/omx/tests/Android.mk
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -5,13 +5,15 @@ LOCAL_SRC_FILES = \
OMXHarness.cpp \
LOCAL_SHARED_LIBRARIES := \
- libstagefright libbinder libmedia libutils
+ libstagefright libbinder libmedia libutils libstagefright_foundation
-LOCAL_C_INCLUDES:= \
+LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE) \
frameworks/base/media/libstagefright \
$(TOP)/frameworks/base/include/media/stagefright/openmax
-LOCAL_MODULE:= omx_tests
+LOCAL_MODULE := omx_tests
+
+LOCAL_MODULE_TAGS := tests
include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 8faf544..fab1771 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -26,9 +26,9 @@
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
#include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaExtractor.h>
@@ -155,7 +155,7 @@ status_t Harness::dequeueMessageForNodeIgnoringBuffers(
if (err == TIMED_OUT) {
return err;
}
- CHECK_EQ(err, OK);
+ CHECK_EQ(err, (status_t)OK);
}
}
@@ -317,7 +317,7 @@ status_t Harness::testStateTransitions(
EXPECT_SUCCESS(err, "allocatePortBuffers(input)");
err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
- CHECK_EQ(err, TIMED_OUT);
+ CHECK_EQ(err, (status_t)TIMED_OUT);
Vector<Buffer> outputBuffers;
err = allocatePortBuffers(dealer, node, 1, &outputBuffers);
@@ -412,7 +412,7 @@ status_t Harness::testStateTransitions(
// Make sure node doesn't just transition to loaded before we are done
// freeing all input and output buffers.
err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
- CHECK_EQ(err, TIMED_OUT);
+ CHECK_EQ(err, (status_t)TIMED_OUT);
for (size_t i = 0; i < inputBuffers.size(); ++i) {
err = mOMX->freeBuffer(node, 0, inputBuffers[i].mID);
@@ -420,7 +420,7 @@ status_t Harness::testStateTransitions(
}
err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
- CHECK_EQ(err, TIMED_OUT);
+ CHECK_EQ(err, (status_t)TIMED_OUT);
for (size_t i = 0; i < outputBuffers.size(); ++i) {
err = mOMX->freeBuffer(node, 1, outputBuffers[i].mID);
@@ -584,7 +584,7 @@ status_t Harness::testSeek(
return UNKNOWN_ERROR;
}
- CHECK_EQ(seekSource->start(), OK);
+ CHECK_EQ(seekSource->start(), (status_t)OK);
sp<MediaSource> codec = OMXCodec::Create(
mOMX, source->getFormat(), false /* createEncoder */,
@@ -592,7 +592,7 @@ status_t Harness::testSeek(
CHECK(codec != NULL);
- CHECK_EQ(codec->start(), OK);
+ CHECK_EQ(codec->start(), (status_t)OK);
int64_t durationUs;
CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
@@ -638,7 +638,7 @@ status_t Harness::testSeek(
requestedSeekTimeUs, MediaSource::ReadOptions::SEEK_NEXT_SYNC);
if (seekSource->read(&buffer, &options) != OK) {
- CHECK_EQ(buffer, NULL);
+ CHECK(buffer == NULL);
actualSeekTimeUs = -1;
} else {
CHECK(buffer != NULL);
@@ -659,7 +659,7 @@ status_t Harness::testSeek(
err = codec->read(&buffer, &options);
options.clearSeekTo();
if (err == INFO_FORMAT_CHANGED) {
- CHECK_EQ(buffer, NULL);
+ CHECK(buffer == NULL);
continue;
}
if (err == OK) {
@@ -670,7 +670,7 @@ status_t Harness::testSeek(
continue;
}
} else {
- CHECK_EQ(buffer, NULL);
+ CHECK(buffer == NULL);
}
break;
@@ -679,7 +679,7 @@ status_t Harness::testSeek(
if (requestedSeekTimeUs < 0) {
// Linear read.
if (err != OK) {
- CHECK_EQ(buffer, NULL);
+ CHECK(buffer == NULL);
} else {
CHECK(buffer != NULL);
buffer->release();
@@ -694,8 +694,8 @@ status_t Harness::testSeek(
"We attempted to seek beyond EOS and expected "
"ERROR_END_OF_STREAM to be returned, but instead "
"we found some other error.");
- CHECK_EQ(err, ERROR_END_OF_STREAM);
- CHECK_EQ(buffer, NULL);
+ CHECK_EQ(err, (status_t)ERROR_END_OF_STREAM);
+ CHECK(buffer == NULL);
} else {
EXPECT(err == OK,
"Expected a valid buffer to be returned from "
@@ -715,7 +715,7 @@ status_t Harness::testSeek(
buffer->release();
buffer = NULL;
- CHECK_EQ(codec->stop(), OK);
+ CHECK_EQ(codec->stop(), (status_t)OK);
return UNKNOWN_ERROR;
}
@@ -725,7 +725,7 @@ status_t Harness::testSeek(
}
}
- CHECK_EQ(codec->stop(), OK);
+ CHECK_EQ(codec->stop(), (status_t)OK);
return OK;
}
@@ -841,7 +841,7 @@ int main(int argc, char **argv) {
srand(seed);
sp<Harness> h = new Harness;
- CHECK_EQ(h->initCheck(), OK);
+ CHECK_EQ(h->initCheck(), (status_t)OK);
if (argc == 0) {
h->testAll();
diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp
index 9d72b1f..fb8abc5 100644
--- a/media/libstagefright/rtsp/AAMRAssembler.cpp
+++ b/media/libstagefright/rtsp/AAMRAssembler.cpp
@@ -211,7 +211,7 @@ ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket(
}
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
queue->erase(queue->begin());
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index ed8b1df..7ea132e 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -345,7 +345,7 @@ void AAVCAssembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 498295c..ded70fa 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -166,7 +166,7 @@ void AH263Assembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index b0c7007..24c2f30 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -571,7 +571,7 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 2f2e2c2..687d72b 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -368,7 +368,7 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 8c9dd8d..44988a3 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -639,7 +639,7 @@ sp<ARTPSource> ARTPConnection::findSource(StreamInfo *info, uint32_t srcId) {
void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) {
sp<AMessage> msg = new AMessage(kWhatInjectPacket, id());
msg->setInt32("index", index);
- msg->setObject("buffer", buffer);
+ msg->setBuffer("buffer", buffer);
msg->post();
}
@@ -647,10 +647,8 @@ void ARTPConnection::onInjectPacket(const sp<AMessage> &msg) {
int32_t index;
CHECK(msg->findInt32("index", &index));
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
-
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
List<StreamInfo>::iterator it = mStreams.begin();
while (it != mStreams.end()
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 7a05b88..ba4e33c 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -145,10 +145,8 @@ void ARTPSession::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- sp<RefBase> obj;
- CHECK(msg->findObject("access-unit", &obj));
-
- sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("access-unit", &accessUnit));
uint64_t ntpTime;
CHECK(accessUnit->meta()->findInt64(
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 80a010e..539a888 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -612,7 +612,7 @@ bool ARTSPConnection::receiveRTSPReponse() {
if (mObserveBinaryMessage != NULL) {
sp<AMessage> notify = mObserveBinaryMessage->dup();
- notify->setObject("buffer", buffer);
+ notify->setBuffer("buffer", buffer);
notify->post();
} else {
ALOGW("received binary data, but no one cares.");
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.cpp b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
index 98bee82..0da5dd2 100644
--- a/media/libstagefright/rtsp/ARawAudioAssembler.cpp
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
@@ -94,7 +94,7 @@ ARTPAssembler::AssemblyStatus ARawAudioAssembler::addPacket(
}
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", buffer);
+ msg->setBuffer("access-unit", buffer);
msg->post();
queue->erase(queue->begin());
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 2391c5c..deee30f 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -122,6 +122,7 @@ struct MyHandler : public AHandler {
mSetupTracksSuccessful(false),
mSeekPending(false),
mFirstAccessUnit(true),
+ mAllTracksHaveTime(false),
mNTPAnchorUs(-1),
mMediaAnchorUs(-1),
mLastMediaTimeUs(0),
@@ -723,6 +724,7 @@ struct MyHandler : public AHandler {
mSetupTracksSuccessful = false;
mSeekPending = false;
mFirstAccessUnit = true;
+ mAllTracksHaveTime = false;
mNTPAnchorUs = -1;
mMediaAnchorUs = -1;
mNumAccessUnitsReceived = 0;
@@ -855,10 +857,8 @@ struct MyHandler : public AHandler {
return;
}
- sp<RefBase> obj;
- CHECK(msg->findObject("access-unit", &obj));
-
- sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("access-unit", &accessUnit));
uint32_t seqNum = (uint32_t)accessUnit->int32Data();
@@ -930,6 +930,7 @@ struct MyHandler : public AHandler {
info->mNTPAnchorUs = -1;
}
+ mAllTracksHaveTime = false;
mNTPAnchorUs = -1;
int64_t timeUs;
@@ -1002,9 +1003,8 @@ struct MyHandler : public AHandler {
case 'biny':
{
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
int32_t index;
CHECK(buffer->meta()->findInt32("index", &index));
@@ -1037,6 +1037,14 @@ struct MyHandler : public AHandler {
ALOGW("Never received any data, disconnecting.");
(new AMessage('abor', id()))->post();
}
+ } else {
+ if (!mAllTracksHaveTime) {
+ ALOGW("We received some RTCP packets, but time "
+ "could not be established on all tracks, now "
+ "using fake timestamps");
+
+ fakeTimestamps();
+ }
}
break;
}
@@ -1211,6 +1219,7 @@ private:
bool mSeekPending;
bool mFirstAccessUnit;
+ bool mAllTracksHaveTime;
int64_t mNTPAnchorUs;
int64_t mMediaAnchorUs;
int64_t mLastMediaTimeUs;
@@ -1357,6 +1366,7 @@ private:
}
void fakeTimestamps() {
+ mNTPAnchorUs = -1ll;
for (size_t i = 0; i < mTracks.size(); ++i) {
onTimeUpdate(i, 0, 0ll);
}
@@ -1377,6 +1387,21 @@ private:
mNTPAnchorUs = ntpTimeUs;
mMediaAnchorUs = mLastMediaTimeUs;
}
+
+ if (!mAllTracksHaveTime) {
+ bool allTracksHaveTime = true;
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ TrackInfo *track = &mTracks.editItemAt(i);
+ if (track->mNTPAnchorUs < 0) {
+ allTracksHaveTime = false;
+ break;
+ }
+ }
+ if (allTracksHaveTime) {
+ mAllTracksHaveTime = true;
+ ALOGI("Time now established for all tracks.");
+ }
+ }
}
void onAccessUnitComplete(
@@ -1403,7 +1428,7 @@ private:
TrackInfo *track = &mTracks.editItemAt(trackIndex);
- if (mNTPAnchorUs < 0 || mMediaAnchorUs < 0 || track->mNTPAnchorUs < 0) {
+ if (!mAllTracksHaveTime) {
ALOGV("storing accessUnit, no time established yet");
track->mPackets.push_back(accessUnit);
return;
@@ -1460,7 +1485,7 @@ private:
sp<AMessage> msg = mNotify->dup();
msg->setInt32("what", kWhatAccessUnit);
msg->setSize("trackIndex", trackIndex);
- msg->setObject("accessUnit", accessUnit);
+ msg->setBuffer("accessUnit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index 76b507f..3dcd9fc 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -26,16 +26,16 @@
#include <media/stagefright/SurfaceMediaSource.h>
#include <media/mediarecorder.h>
-#include <gui/SurfaceTextureClient.h>
#include <ui/GraphicBuffer.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/Surface.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceTextureClient.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <binder/ProcessState.h>
#include <ui/FramebufferNativeWindow.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
@@ -475,7 +475,7 @@ sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int video
mr->setVideoFrameRate(fps);
mr->prepare();
ALOGV("Starting MediaRecorder...");
- CHECK_EQ(OK, mr->start());
+ CHECK_EQ((status_t)OK, mr->start());
return mr;
}
@@ -757,7 +757,7 @@ TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaS
ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU));
ALOGV("Stopping MediaRecorder...");
- CHECK_EQ(OK, mr->stop());
+ CHECK_EQ((status_t)OK, mr->stop());
mr.clear();
close(fd);
}
@@ -886,7 +886,7 @@ TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) {
mEglSurface = EGL_NO_SURFACE;
ALOGV("Stopping MediaRecorder...");
- CHECK_EQ(OK, mr->stop());
+ CHECK_EQ((status_t)OK, mr->stop());
mr.clear();
close(fd);
}
@@ -929,7 +929,7 @@ TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) {
mEglSurface = EGL_NO_SURFACE;
ALOGV("Stopping MediaRecorder...");
- CHECK_EQ(OK, mr->stop());
+ CHECK_EQ((status_t)OK, mr->stop());
mr.clear();
close(fd);
}
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 59d0e15..dde2066 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -3,14 +3,16 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
TextDescriptions.cpp \
- TimedTextParser.cpp \
+ TimedTextDriver.cpp \
+ TimedText3GPPSource.cpp \
+ TimedTextSource.cpp \
+ TimedTextSRTSource.cpp \
TimedTextPlayer.cpp
LOCAL_CFLAGS += -Wno-multichar
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
- $(TOP)/frameworks/base/media/libstagefright \
- $(TOP)/frameworks/base/include/media/stagefright/openmax
+ $(TOP)/frameworks/base/media/libstagefright
LOCAL_MODULE:= libstagefright_timedtext
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
new file mode 100644
index 0000000..4a3bfd3
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
@@ -0,0 +1,113 @@
+ /*
+ * 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 "TimedText3GPPSource"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ADebug.h> // CHECK_XX macro
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+#include "TimedText3GPPSource.h"
+#include "TextDescriptions.h"
+
+namespace android {
+
+TimedText3GPPSource::TimedText3GPPSource(const sp<MediaSource>& mediaSource)
+ : mSource(mediaSource) {
+}
+
+TimedText3GPPSource::~TimedText3GPPSource() {
+}
+
+status_t TimedText3GPPSource::read(
+ int64_t *timeUs, Parcel *parcel, const MediaSource::ReadOptions *options) {
+ MediaBuffer *textBuffer = NULL;
+ status_t err = mSource->read(&textBuffer, options);
+ if (err != OK) {
+ return err;
+ }
+ CHECK(textBuffer != NULL);
+ textBuffer->meta_data()->findInt64(kKeyTime, timeUs);
+ // TODO: this is legacy code. when 'timeUs' can be <= 0?
+ if (*timeUs > 0) {
+ extractAndAppendLocalDescriptions(*timeUs, textBuffer, parcel);
+ }
+ textBuffer->release();
+ return OK;
+}
+
+// Each text sample consists of a string of text, optionally with sample
+// modifier description. The modifier description could specify a new
+// text style for the string of text. These descriptions are present only
+// if they are needed. This method is used to extract the modifier
+// description and append it at the end of the text.
+status_t TimedText3GPPSource::extractAndAppendLocalDescriptions(
+ int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) {
+ const void *data;
+ size_t size = 0;
+ int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+ CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
+
+ data = textBuffer->data();
+ size = textBuffer->size();
+
+ if (size > 0) {
+ parcel->freeData();
+ flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
+ }
+ return OK;
+}
+
+// To extract and send the global text descriptions for all the text samples
+// in the text track or text file.
+// TODO: send error message to application via notifyListener()...?
+status_t TimedText3GPPSource::extractGlobalDescriptions(Parcel *parcel) {
+ const void *data;
+ size_t size = 0;
+ int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+ CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
+
+ uint32_t type;
+ // get the 'tx3g' box content. This box contains the text descriptions
+ // used to render the text track
+ if (!mSource->getFormat()->findData(
+ kKeyTextFormatData, &type, &data, &size)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (size > 0) {
+ flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, 0, parcel);
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h
new file mode 100644
index 0000000..cb7e47c
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.h
@@ -0,0 +1,55 @@
+/*
+ * 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 TIMED_TEXT_3GPP_SOURCE_H_
+#define TIMED_TEXT_3GPP_SOURCE_H_
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+
+#include "TimedTextSource.h"
+
+namespace android {
+
+class MediaBuffer;
+class Parcel;
+
+class TimedText3GPPSource : public TimedTextSource {
+ public:
+ TimedText3GPPSource(const sp<MediaSource>& mediaSource);
+ virtual status_t start() { return mSource->start(); }
+ virtual status_t stop() { return mSource->stop(); }
+ virtual status_t read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options = NULL);
+ virtual status_t extractGlobalDescriptions(Parcel *parcel);
+
+ protected:
+ virtual ~TimedText3GPPSource();
+
+ private:
+ sp<MediaSource> mSource;
+
+ status_t extractAndAppendLocalDescriptions(
+ int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimedText3GPPSource);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_3GPP_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
new file mode 100644
index 0000000..9ec9415
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -0,0 +1,223 @@
+ /*
+ * 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 "TimedTextDriver"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+
+#include "TimedTextDriver.h"
+
+#include "TextDescriptions.h"
+#include "TimedTextPlayer.h"
+#include "TimedTextSource.h"
+
+namespace android {
+
+TimedTextDriver::TimedTextDriver(
+ const wp<MediaPlayerBase> &listener)
+ : mLooper(new ALooper),
+ mListener(listener),
+ mState(UNINITIALIZED) {
+ mLooper->setName("TimedTextDriver");
+ mLooper->start();
+ mPlayer = new TimedTextPlayer(listener);
+ mLooper->registerHandler(mPlayer);
+}
+
+TimedTextDriver::~TimedTextDriver() {
+ mTextInBandVector.clear();
+ mTextOutOfBandVector.clear();
+ mLooper->stop();
+}
+
+status_t TimedTextDriver::setTimedTextTrackIndex_l(int32_t index) {
+ if (index >=
+ (int)(mTextInBandVector.size() + mTextOutOfBandVector.size())) {
+ return BAD_VALUE;
+ }
+
+ sp<TimedTextSource> source;
+ if (index < mTextInBandVector.size()) {
+ source = mTextInBandVector.itemAt(index);
+ } else {
+ source = mTextOutOfBandVector.itemAt(index - mTextInBandVector.size());
+ }
+ mPlayer->setDataSource(source);
+ return OK;
+}
+
+status_t TimedTextDriver::start() {
+ Mutex::Autolock autoLock(mLock);
+ switch (mState) {
+ case UNINITIALIZED:
+ return INVALID_OPERATION;
+ case STOPPED:
+ mPlayer->start();
+ break;
+ case PLAYING:
+ return OK;
+ case PAUSED:
+ mPlayer->resume();
+ break;
+ default:
+ TRESPASS();
+ }
+ mState = PLAYING;
+ return OK;
+}
+
+status_t TimedTextDriver::stop() {
+ return pause();
+}
+
+// TODO: Test if pause() works properly.
+// Scenario 1: start - pause - resume
+// Scenario 2: start - seek
+// Scenario 3: start - pause - seek - resume
+status_t TimedTextDriver::pause() {
+ Mutex::Autolock autoLock(mLock);
+ switch (mState) {
+ case UNINITIALIZED:
+ return INVALID_OPERATION;
+ case STOPPED:
+ return OK;
+ case PLAYING:
+ mPlayer->pause();
+ break;
+ case PAUSED:
+ return OK;
+ default:
+ TRESPASS();
+ }
+ mState = PAUSED;
+ return OK;
+}
+
+status_t TimedTextDriver::resume() {
+ return start();
+}
+
+status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
+ mPlayer->seekToAsync(timeUs);
+ return OK;
+}
+
+status_t TimedTextDriver::setTimedTextTrackIndex(int32_t index) {
+ // TODO: This is current implementation for MediaPlayer::disableTimedText().
+ // Find better way for readability.
+ if (index < 0) {
+ mPlayer->pause();
+ return OK;
+ }
+
+ status_t ret = OK;
+ Mutex::Autolock autoLock(mLock);
+ switch (mState) {
+ case UNINITIALIZED:
+ ret = INVALID_OPERATION;
+ break;
+ case PAUSED:
+ ret = setTimedTextTrackIndex_l(index);
+ break;
+ case PLAYING:
+ mPlayer->pause();
+ ret = setTimedTextTrackIndex_l(index);
+ if (ret != OK) {
+ break;
+ }
+ mPlayer->start();
+ break;
+ case STOPPED:
+ // TODO: The only difference between STOPPED and PAUSED is this
+ // part. Revise the flow from "MediaPlayer::enableTimedText()" and
+ // remove one of the status, PAUSED and STOPPED, if possible.
+ ret = setTimedTextTrackIndex_l(index);
+ if (ret != OK) {
+ break;
+ }
+ mPlayer->start();
+ break;
+ defaut:
+ TRESPASS();
+ }
+ return ret;
+}
+
+status_t TimedTextDriver::addInBandTextSource(
+ const sp<MediaSource>& mediaSource) {
+ sp<TimedTextSource> source =
+ TimedTextSource::CreateTimedTextSource(mediaSource);
+ if (source == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+ Mutex::Autolock autoLock(mLock);
+ mTextInBandVector.add(source);
+ if (mState == UNINITIALIZED) {
+ mState = STOPPED;
+ }
+ return OK;
+}
+
+status_t TimedTextDriver::addOutOfBandTextSource(
+ const Parcel &request) {
+ // TODO: Define "TimedTextSource::CreateFromURI(uri)"
+ // and move below lines there..?
+
+ // String values written in Parcel are UTF-16 values.
+ const String16 uri16 = request.readString16();
+ String8 uri = String8(request.readString16());
+
+ uri.toLower();
+ // To support local subtitle file only for now
+ if (strncasecmp("file://", uri.string(), 7)) {
+ return ERROR_UNSUPPORTED;
+ }
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(uri);
+ if (dataSource == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<TimedTextSource> source;
+ if (uri.getPathExtension() == String8(".srt")) {
+ source = TimedTextSource::CreateTimedTextSource(
+ dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT);
+ }
+
+ if (source == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+
+ mTextOutOfBandVector.add(source);
+ if (mState == UNINITIALIZED) {
+ mState = STOPPED;
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextDriver.h b/media/libstagefright/timedtext/TimedTextDriver.h
new file mode 100644
index 0000000..efedb6e
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextDriver.h
@@ -0,0 +1,81 @@
+/*
+ * 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 TIMED_TEXT_DRIVER_H_
+#define TIMED_TEXT_DRIVER_H_
+
+#include <media/stagefright/foundation/ABase.h> // for DISALLOW_* macro
+#include <utils/Errors.h> // for status_t
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class ALooper;
+class MediaPlayerBase;
+class MediaSource;
+class Parcel;
+class TimedTextPlayer;
+class TimedTextSource;
+
+class TimedTextDriver {
+public:
+ TimedTextDriver(const wp<MediaPlayerBase> &listener);
+
+ ~TimedTextDriver();
+
+ // TODO: pause-resume pair seems equivalent to stop-start pair.
+ // Check if it is replaceable with stop-start.
+ status_t start();
+ status_t stop();
+ status_t pause();
+ status_t resume();
+
+ status_t seekToAsync(int64_t timeUs);
+
+ status_t addInBandTextSource(const sp<MediaSource>& source);
+ status_t addOutOfBandTextSource(const Parcel &request);
+
+ status_t setTimedTextTrackIndex(int32_t index);
+
+private:
+ Mutex mLock;
+
+ enum State {
+ UNINITIALIZED,
+ STOPPED,
+ PLAYING,
+ PAUSED,
+ };
+
+ sp<ALooper> mLooper;
+ sp<TimedTextPlayer> mPlayer;
+ wp<MediaPlayerBase> mListener;
+
+ // Variables to be guarded by mLock.
+ State mState;
+ Vector<sp<TimedTextSource> > mTextInBandVector;
+ Vector<sp<TimedTextSource> > mTextOutOfBandVector;
+ // -- End of variables to be guarded by mLock
+
+ status_t setTimedTextTrackIndex_l(int32_t index);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_DRIVER_H_
diff --git a/media/libstagefright/timedtext/TimedTextParser.h b/media/libstagefright/timedtext/TimedTextParser.h
deleted file mode 100644
index 44774c2..0000000
--- a/media/libstagefright/timedtext/TimedTextParser.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2011 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 TIMED_TEXT_PARSER_H_
-
-#define TIMED_TEXT_PARSER_H_
-
-#include <media/MediaPlayerInterface.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/MediaSource.h>
-
-namespace android {
-
-class DataSource;
-
-class TimedTextParser : public RefBase {
-public:
- TimedTextParser();
- virtual ~TimedTextParser();
-
- enum FileType {
- OUT_OF_BAND_FILE_SRT = 1,
- };
-
- status_t getText(AString *text, int64_t *startTimeUs, int64_t *endTimeUs,
- const MediaSource::ReadOptions *options = NULL);
- status_t init(const sp<DataSource> &dataSource, FileType fileType);
- void reset();
-
-private:
- Mutex mLock;
-
- sp<DataSource> mDataSource;
- off64_t mOffset;
-
- struct TextInfo {
- int64_t endTimeUs;
- // the offset of the text in the original file
- off64_t offset;
- int textLen;
- };
-
- int mIndex;
- FileType mFileType;
-
- // the key indicated the start time of the text
- KeyedVector<int64_t, TextInfo> mTextVector;
-
- status_t getNextInSrtFileFormat(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info);
- status_t readNextLine(off64_t *offset, AString *data);
-
- status_t scanFile();
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextParser);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_PARSER_H_
-
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index 3014b0b..bf7cbf6 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -18,399 +18,164 @@
#define LOG_TAG "TimedTextPlayer"
#include <utils/Log.h>
-#include <binder/IPCThreadState.h>
-
-#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/Utils.h>
+#include <media/MediaPlayerInterface.h>
-#include "include/AwesomePlayer.h"
#include "TimedTextPlayer.h"
-#include "TimedTextParser.h"
-#include "TextDescriptions.h"
-
-namespace android {
-struct TimedTextEvent : public TimedEventQueue::Event {
- TimedTextEvent(
- TimedTextPlayer *player,
- void (TimedTextPlayer::*method)())
- : mPlayer(player),
- mMethod(method) {
- }
-
-protected:
- virtual ~TimedTextEvent() {}
-
- virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
- (mPlayer->*mMethod)();
- }
+#include "TimedTextDriver.h"
+#include "TimedTextSource.h"
-private:
- TimedTextPlayer *mPlayer;
- void (TimedTextPlayer::*mMethod)();
+namespace android {
- TimedTextEvent(const TimedTextEvent &);
- TimedTextEvent &operator=(const TimedTextEvent &);
-};
+static const int64_t kAdjustmentProcessingTimeUs = 100000ll;
-TimedTextPlayer::TimedTextPlayer(
- AwesomePlayer *observer,
- const wp<MediaPlayerBase> &listener,
- TimedEventQueue *queue)
- : mSource(NULL),
- mOutOfBandSource(NULL),
- mSeekTimeUs(0),
- mStarted(false),
- mTextEventPending(false),
- mQueue(queue),
- mListener(listener),
- mObserver(observer),
- mTextBuffer(NULL),
- mTextParser(NULL),
- mTextType(kNoText) {
- mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
+TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener)
+ : mListener(listener),
+ mSource(NULL),
+ mSendSubtitleGeneration(0) {
}
TimedTextPlayer::~TimedTextPlayer() {
- if (mStarted) {
- reset();
+ if (mSource != NULL) {
+ mSource->stop();
+ mSource.clear();
+ mSource = NULL;
}
-
- mTextTrackVector.clear();
- mTextOutOfBandVector.clear();
}
-status_t TimedTextPlayer::start(uint8_t index) {
- CHECK(!mStarted);
-
- if (index >=
- mTextTrackVector.size() + mTextOutOfBandVector.size()) {
- ALOGE("Incorrect text track index: %d", index);
- return BAD_VALUE;
- }
-
- status_t err;
- if (index < mTextTrackVector.size()) { // start an in-band text
- mSource = mTextTrackVector.itemAt(index);
-
- err = mSource->start();
-
- if (err != OK) {
- return err;
- }
- mTextType = kInBandText;
- } else { // start an out-of-band text
- OutOfBandText text =
- mTextOutOfBandVector.itemAt(index - mTextTrackVector.size());
-
- mOutOfBandSource = text.source;
- TimedTextParser::FileType fileType = text.type;
-
- if (mTextParser == NULL) {
- mTextParser = new TimedTextParser();
- }
-
- if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) {
- return err;
- }
- mTextType = kOutOfBandText;
- }
-
- // send sample description format
- if ((err = extractAndSendGlobalDescriptions()) != OK) {
- return err;
- }
-
- int64_t positionUs;
- mObserver->getPosition(&positionUs);
- seekTo(positionUs);
-
- postTextEvent();
-
- mStarted = true;
-
- return OK;
+void TimedTextPlayer::start() {
+ sp<AMessage> msg = new AMessage(kWhatSeek, id());
+ msg->setInt64("seekTimeUs", -1);
+ msg->post();
}
void TimedTextPlayer::pause() {
- CHECK(mStarted);
-
- cancelTextEvent();
+ (new AMessage(kWhatPause, id()))->post();
}
void TimedTextPlayer::resume() {
- CHECK(mStarted);
-
- postTextEvent();
-}
-
-void TimedTextPlayer::reset() {
- CHECK(mStarted);
-
- // send an empty text to clear the screen
- notifyListener(MEDIA_TIMED_TEXT);
-
- cancelTextEvent();
-
- mSeeking = false;
- mStarted = false;
-
- if (mTextType == kInBandText) {
- if (mTextBuffer != NULL) {
- mTextBuffer->release();
- mTextBuffer = NULL;
- }
-
- if (mSource != NULL) {
- mSource->stop();
- mSource.clear();
- mSource = NULL;
- }
- } else {
- if (mTextParser != NULL) {
- mTextParser.clear();
- mTextParser = NULL;
- }
- if (mOutOfBandSource != NULL) {
- mOutOfBandSource.clear();
- mOutOfBandSource = NULL;
- }
- }
+ start();
}
-status_t TimedTextPlayer::seekTo(int64_t time_us) {
- Mutex::Autolock autoLock(mLock);
-
- mSeeking = true;
- mSeekTimeUs = time_us;
-
- postTextEvent();
-
- return OK;
+void TimedTextPlayer::seekToAsync(int64_t timeUs) {
+ sp<AMessage> msg = new AMessage(kWhatSeek, id());
+ msg->setInt64("seekTimeUs", timeUs);
+ msg->post();
}
-status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
- if (index >=
- (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) {
- return BAD_VALUE;
- }
-
- if (mStarted) {
- reset();
- }
-
- if (index >= 0) {
- return start(index);
- }
- return OK;
+void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) {
+ sp<AMessage> msg = new AMessage(kWhatSetSource, id());
+ msg->setObject("source", source);
+ msg->post();
}
-void TimedTextPlayer::onTextEvent() {
- Mutex::Autolock autoLock(mLock);
-
- if (!mTextEventPending) {
- return;
- }
- mTextEventPending = false;
-
- if (mData.dataSize() > 0) {
- notifyListener(MEDIA_TIMED_TEXT, &mData);
- mData.freeData();
- }
-
- MediaSource::ReadOptions options;
- if (mSeeking) {
- options.setSeekTo(mSeekTimeUs,
- MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- mSeeking = false;
-
- notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
- }
-
- int64_t positionUs, timeUs;
- mObserver->getPosition(&positionUs);
-
- if (mTextType == kInBandText) {
- if (mSource->read(&mTextBuffer, &options) != OK) {
- return;
+void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatPause: {
+ mSendSubtitleGeneration++;
+ break;
}
-
- mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
- } else {
- int64_t endTimeUs;
- if (mTextParser->getText(
- &mText, &timeUs, &endTimeUs, &options) != OK) {
- return;
- }
- }
-
- if (timeUs > 0) {
- extractAndAppendLocalDescriptions(timeUs);
- }
-
- if (mTextType == kInBandText) {
- if (mTextBuffer != NULL) {
- mTextBuffer->release();
- mTextBuffer = NULL;
+ case kWhatSeek: {
+ int64_t seekTimeUs = 0;
+ msg->findInt64("seekTimeUs", &seekTimeUs);
+ if (seekTimeUs < 0) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener != NULL) {
+ int32_t positionMs = 0;
+ listener->getCurrentPosition(&positionMs);
+ seekTimeUs = positionMs * 1000ll;
+ }
+ }
+ doSeekAndRead(seekTimeUs);
+ break;
+ }
+ case kWhatSendSubtitle: {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mSendSubtitleGeneration) {
+ // Drop obsolete msg.
+ break;
+ }
+ sp<RefBase> obj;
+ msg->findObject("subtitle", &obj);
+ if (obj != NULL) {
+ sp<ParcelEvent> parcelEvent;
+ parcelEvent = static_cast<ParcelEvent*>(obj.get());
+ notifyListener(MEDIA_TIMED_TEXT, &(parcelEvent->parcel));
+ } else {
+ notifyListener(MEDIA_TIMED_TEXT);
+ }
+ doRead();
+ break;
+ }
+ case kWhatSetSource: {
+ sp<RefBase> obj;
+ msg->findObject("source", &obj);
+ if (obj == NULL) break;
+ if (mSource != NULL) {
+ mSource->stop();
+ }
+ mSource = static_cast<TimedTextSource*>(obj.get());
+ mSource->start();
+ Parcel parcel;
+ if (mSource->extractGlobalDescriptions(&parcel) == OK &&
+ parcel.dataSize() > 0) {
+ notifyListener(MEDIA_TIMED_TEXT, &parcel);
+ } else {
+ notifyListener(MEDIA_TIMED_TEXT);
+ }
+ break;
}
- } else {
- mText.clear();
- }
-
- //send the text now
- if (timeUs <= positionUs + 100000ll) {
- postTextEvent();
- } else {
- postTextEvent(timeUs - positionUs - 100000ll);
}
}
-void TimedTextPlayer::postTextEvent(int64_t delayUs) {
- if (mTextEventPending) {
- return;
- }
-
- mTextEventPending = true;
- mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
-}
-
-void TimedTextPlayer::cancelTextEvent() {
- mQueue->cancelEvent(mTextEvent->eventID());
- mTextEventPending = false;
+void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) {
+ MediaSource::ReadOptions options;
+ options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
+ doRead(&options);
}
-void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
- Mutex::Autolock autoLock(mLock);
- mTextTrackVector.add(source);
+void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) {
+ int64_t timeUs = 0;
+ sp<ParcelEvent> parcelEvent = new ParcelEvent();
+ mSource->read(&timeUs, &(parcelEvent->parcel), options);
+ postTextEvent(parcelEvent, timeUs);
}
-status_t TimedTextPlayer::setParameter(int key, const Parcel &request) {
- Mutex::Autolock autoLock(mLock);
-
- if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) {
- const String16 uri16 = request.readString16();
- String8 uri = String8(uri16);
- KeyedVector<String8, String8> headers;
-
- // To support local subtitle file only for now
- if (strncasecmp("file://", uri.string(), 7)) {
- return INVALID_OPERATION;
- }
- sp<DataSource> dataSource =
- DataSource::CreateFromURI(uri, &headers);
- status_t err = dataSource->initCheck();
+void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener != NULL) {
+ int64_t positionUs, delayUs;
+ int32_t positionMs = 0;
+ listener->getCurrentPosition(&positionMs);
+ positionUs = positionMs * 1000;
- if (err != OK) {
- return err;
- }
-
- OutOfBandText text;
- text.source = dataSource;
- if (uri.getPathExtension() == String8(".srt")) {
- text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT;
+ if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) {
+ delayUs = 0;
} else {
- return ERROR_UNSUPPORTED;
+ delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs;
}
-
- mTextOutOfBandVector.add(text);
-
- return OK;
- }
- return INVALID_OPERATION;
-}
-
-void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
- if (mListener != NULL) {
- sp<MediaPlayerBase> listener = mListener.promote();
-
- if (listener != NULL) {
- if (parcel && (parcel->dataSize() > 0)) {
- listener->sendEvent(msg, 0, 0, parcel);
- } else { // send an empty timed text to clear the screen
- listener->sendEvent(msg);
- }
+ sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id());
+ msg->setInt32("generation", mSendSubtitleGeneration);
+ if (parcel != NULL) {
+ msg->setObject("subtitle", parcel);
}
+ msg->post(delayUs);
}
}
-// Each text sample consists of a string of text, optionally with sample
-// modifier description. The modifier description could specify a new
-// text style for the string of text. These descriptions are present only
-// if they are needed. This method is used to extract the modifier
-// description and append it at the end of the text.
-status_t TimedTextPlayer::extractAndAppendLocalDescriptions(int64_t timeUs) {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
-
- if (mTextType == kInBandText) {
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
-
- if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- data = mTextBuffer->data();
- size = mTextBuffer->size();
- } else {
- // support 3GPP only for now
- return ERROR_UNSUPPORTED;
+void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener != NULL) {
+ if (parcel != NULL && (parcel->dataSize() > 0)) {
+ listener->sendEvent(msg, 0, 0, parcel);
+ } else { // send an empty timed text to clear the screen
+ listener->sendEvent(msg);
}
- } else {
- data = mText.c_str();
- size = mText.size();
- flag |= TextDescriptions::OUT_OF_BAND_TEXT_SRT;
}
-
- if ((size > 0) && (flag != TextDescriptions::LOCAL_DESCRIPTIONS)) {
- mData.freeData();
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, &mData);
- }
-
- return OK;
}
-// To extract and send the global text descriptions for all the text samples
-// in the text track or text file.
-status_t TimedTextPlayer::extractAndSendGlobalDescriptions() {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
-
- if (mTextType == kInBandText) {
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
-
- // support 3GPP only for now
- if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
- uint32_t type;
- // get the 'tx3g' box content. This box contains the text descriptions
- // used to render the text track
- if (!mSource->getFormat()->findData(
- kKeyTextFormatData, &type, &data, &size)) {
- return ERROR_MALFORMED;
- }
-
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- }
- }
-
- if ((size > 0) && (flag != TextDescriptions::GLOBAL_DESCRIPTIONS)) {
- Parcel parcel;
- if (TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, 0, &parcel) == OK) {
- if (parcel.dataSize() > 0) {
- notifyListener(MEDIA_TIMED_TEXT, &parcel);
- }
- }
- }
-
- return OK;
-}
-}
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
index a744db5..837beeb 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ b/media/libstagefright/timedtext/TimedTextPlayer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -15,99 +15,61 @@
*/
#ifndef TIMEDTEXT_PLAYER_H_
-
#define TIMEDTEXT_PLAYER_H_
-#include <media/MediaPlayerInterface.h>
+#include <binder/Parcel.h>
#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/RefBase.h>
-#include "include/TimedEventQueue.h"
-#include "TimedTextParser.h"
+#include "TimedTextSource.h"
namespace android {
-class MediaSource;
-class AwesomePlayer;
-class MediaBuffer;
+class AMessage;
+class MediaPlayerBase;
+class TimedTextDriver;
+class TimedTextSource;
-class TimedTextPlayer {
+class TimedTextPlayer : public AHandler {
public:
- TimedTextPlayer(AwesomePlayer *observer,
- const wp<MediaPlayerBase> &listener,
- TimedEventQueue *queue);
+ TimedTextPlayer(const wp<MediaPlayerBase> &listener);
virtual ~TimedTextPlayer();
- // index: the index of the text track which will
- // be turned on
- status_t start(uint8_t index);
-
+ void start();
void pause();
-
void resume();
+ void seekToAsync(int64_t timeUs);
+ void setDataSource(sp<TimedTextSource> source);
- status_t seekTo(int64_t time_us);
-
- void addTextSource(sp<MediaSource> source);
-
- status_t setTimedTextTrackIndex(int32_t index);
- status_t setParameter(int key, const Parcel &request);
+protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg);
private:
- enum TextType {
- kNoText = 0,
- kInBandText = 1,
- kOutOfBandText = 2,
+ enum {
+ kWhatPause = 'paus',
+ kWhatSeek = 'seek',
+ kWhatSendSubtitle = 'send',
+ kWhatSetSource = 'ssrc',
};
- Mutex mLock;
-
- sp<MediaSource> mSource;
- sp<DataSource> mOutOfBandSource;
-
- bool mSeeking;
- int64_t mSeekTimeUs;
-
- bool mStarted;
-
- sp<TimedEventQueue::Event> mTextEvent;
- bool mTextEventPending;
-
- TimedEventQueue *mQueue;
-
- wp<MediaPlayerBase> mListener;
- AwesomePlayer *mObserver;
-
- MediaBuffer *mTextBuffer;
- Parcel mData;
-
- // for in-band timed text
- Vector<sp<MediaSource> > mTextTrackVector;
-
- // for out-of-band timed text
- struct OutOfBandText {
- TimedTextParser::FileType type;
- sp<DataSource> source;
+ // To add Parcel into an AMessage as an object, it should be 'RefBase'.
+ struct ParcelEvent : public RefBase {
+ Parcel parcel;
};
- Vector<OutOfBandText > mTextOutOfBandVector;
- sp<TimedTextParser> mTextParser;
- AString mText;
-
- TextType mTextType;
-
- void reset();
+ wp<MediaPlayerBase> mListener;
+ sp<TimedTextSource> mSource;
+ int32_t mSendSubtitleGeneration;
+ void doSeekAndRead(int64_t seekTimeUs);
+ void doRead(MediaSource::ReadOptions* options = NULL);
void onTextEvent();
- void postTextEvent(int64_t delayUs = -1);
- void cancelTextEvent();
-
+ void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
void notifyListener(int msg, const Parcel *parcel = NULL);
- status_t extractAndAppendLocalDescriptions(int64_t timeUs);
- status_t extractAndSendGlobalDescriptions();
-
DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
};
diff --git a/media/libstagefright/timedtext/TimedTextParser.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
index 0bada16..3752d34 100644
--- a/media/libstagefright/timedtext/TimedTextParser.cpp
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
@@ -1,5 +1,5 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
+ /*
+ * 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.
@@ -14,149 +14,126 @@
* limitations under the License.
*/
-#include "TimedTextParser.h"
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TimedTextSRTSource"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+
+#include "TimedTextSRTSource.h"
+#include "TextDescriptions.h"
namespace android {
-TimedTextParser::TimedTextParser()
- : mDataSource(NULL),
- mOffset(0),
- mIndex(0) {
+TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource)
+ : mSource(dataSource),
+ mIndex(0) {
}
-TimedTextParser::~TimedTextParser() {
- reset();
+TimedTextSRTSource::~TimedTextSRTSource() {
}
-status_t TimedTextParser::init(
- const sp<DataSource> &dataSource, FileType fileType) {
- mDataSource = dataSource;
- mFileType = fileType;
-
- status_t err;
- if ((err = scanFile()) != OK) {
+status_t TimedTextSRTSource::start() {
+ status_t err = scanFile();
+ if (err != OK) {
reset();
- return err;
}
-
- return OK;
+ return err;
}
-void TimedTextParser::reset() {
- mDataSource.clear();
+void TimedTextSRTSource::reset() {
mTextVector.clear();
- mOffset = 0;
mIndex = 0;
}
-// scan the text file to get start/stop time and the
-// offset of each piece of text content
-status_t TimedTextParser::scanFile() {
- if (mFileType != OUT_OF_BAND_FILE_SRT) {
- return ERROR_UNSUPPORTED;
+status_t TimedTextSRTSource::stop() {
+ reset();
+ return OK;
+}
+
+status_t TimedTextSRTSource::read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options) {
+ int64_t endTimeUs;
+ AString text;
+ status_t err = getText(options, &text, timeUs, &endTimeUs);
+ if (err != OK) {
+ return err;
+ }
+
+ if (*timeUs > 0) {
+ extractAndAppendLocalDescriptions(*timeUs, text, parcel);
}
+ return OK;
+}
+status_t TimedTextSRTSource::scanFile() {
off64_t offset = 0;
int64_t startTimeUs;
bool endOfFile = false;
while (!endOfFile) {
TextInfo info;
- status_t err = getNextInSrtFileFormat(&offset, &startTimeUs, &info);
-
- if (err != OK) {
- if (err == ERROR_END_OF_STREAM) {
+ status_t err = getNextSubtitleInfo(&offset, &startTimeUs, &info);
+ switch (err) {
+ case OK:
+ mTextVector.add(startTimeUs, info);
+ break;
+ case ERROR_END_OF_STREAM:
endOfFile = true;
- } else {
+ break;
+ default:
return err;
- }
- } else {
- mTextVector.add(startTimeUs, info);
}
}
-
if (mTextVector.isEmpty()) {
return ERROR_MALFORMED;
}
return OK;
}
-// read one line started from *offset and store it into data.
-status_t TimedTextParser::readNextLine(off64_t *offset, AString *data) {
- char character;
-
- data->clear();
-
- while (true) {
- ssize_t err;
- if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) {
- if (err == 0) {
- return ERROR_END_OF_STREAM;
- }
- return ERROR_IO;
- }
-
- (*offset) ++;
-
- // a line could end with CR, LF or CR + LF
- if (character == 10) {
- break;
- } else if (character == 13) {
- if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) {
- if (err == 0) { // end of the stream
- return OK;
- }
- return ERROR_IO;
- }
-
- (*offset) ++;
-
- if (character != 10) {
- (*offset) --;
- }
- break;
- }
-
- data->append(character);
- }
-
- return OK;
-}
-
/* SRT format:
- * Subtitle number
- * Start time --> End time
- * Text of subtitle (one or more lines)
- * Blank line
+ * Subtitle number
+ * Start time --> End time
+ * Text of subtitle (one or more lines)
+ * Blank lines
*
* .srt file example:
- * 1
- * 00:00:20,000 --> 00:00:24,400
- * Altocumulus clouds occur between six thousand
+ * 1
+ * 00:00:20,000 --> 00:00:24,400
+ * Altocumulus clouds occr between six thousand
*
- * 2
- * 00:00:24,600 --> 00:00:27,800
- * and twenty thousand feet above ground level.
+ * 2
+ * 00:00:24,600 --> 00:00:27,800
+ * and twenty thousand feet above ground level.
*/
-status_t TimedTextParser::getNextInSrtFileFormat(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info) {
+status_t TimedTextSRTSource::getNextSubtitleInfo(
+ off64_t *offset, int64_t *startTimeUs, TextInfo *info) {
AString data;
status_t err;
- if ((err = readNextLine(offset, &data)) != OK) {
- return err;
- }
- // to skip the first line
+ // To skip blank lines.
+ do {
+ if ((err = readNextLine(offset, &data)) != OK) {
+ return err;
+ }
+ data.trim();
+ } while (data.empty());
+
+ // Just ignore the first non-blank line which is subtitle sequence number.
if ((err = readNextLine(offset, &data)) != OK) {
return err;
}
-
int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2;
// the start time format is: hours:minutes:seconds,milliseconds
// 00:00:24,600 --> 00:00:27,800
if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
- &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) {
+ &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) {
return ERROR_MALFORMED;
}
@@ -167,7 +144,6 @@ status_t TimedTextParser::getNextInSrtFileFormat(
}
info->offset = *offset;
-
bool needMoreData = true;
while (needMoreData) {
if ((err = readNextLine(offset, &data)) != OK) {
@@ -186,25 +162,56 @@ status_t TimedTextParser::getNextInSrtFileFormat(
}
}
}
-
info->textLen = *offset - info->offset;
-
return OK;
}
-status_t TimedTextParser::getText(
- AString *text, int64_t *startTimeUs, int64_t *endTimeUs,
- const MediaSource::ReadOptions *options) {
- Mutex::Autolock autoLock(mLock);
+status_t TimedTextSRTSource::readNextLine(off64_t *offset, AString *data) {
+ data->clear();
+ while (true) {
+ ssize_t readSize;
+ char character;
+ if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
+ if (readSize == 0) {
+ return ERROR_END_OF_STREAM;
+ }
+ return ERROR_IO;
+ }
- text->clear();
+ (*offset)++;
+
+ // a line could end with CR, LF or CR + LF
+ if (character == 10) {
+ break;
+ } else if (character == 13) {
+ if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
+ if (readSize == 0) { // end of the stream
+ return OK;
+ }
+ return ERROR_IO;
+ }
+ (*offset)++;
+ if (character != 10) {
+ (*offset)--;
+ }
+ break;
+ }
+ data->append(character);
+ }
+ return OK;
+}
+
+status_t TimedTextSRTSource::getText(
+ const MediaSource::ReadOptions *options,
+ AString *text, int64_t *startTimeUs, int64_t *endTimeUs) {
+ text->clear();
int64_t seekTimeUs;
MediaSource::ReadOptions::SeekMode mode;
- if (options && options->getSeekTo(&seekTimeUs, &mode)) {
- int64_t lastEndTimeUs = mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
+ if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
+ int64_t lastEndTimeUs =
+ mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
int64_t firstStartTimeUs = mTextVector.keyAt(0);
-
if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) {
return ERROR_OUT_OF_RANGE;
} else if (seekTimeUs < firstStartTimeUs) {
@@ -227,31 +234,42 @@ status_t TimedTextParser::getText(
low = mid + 1;
} else {
if ((high == mid + 1)
- && (seekTimeUs < mTextVector.keyAt(high))) {
+ && (seekTimeUs < mTextVector.keyAt(high))) {
break;
}
high = mid - 1;
}
}
-
mIndex = mid;
}
}
-
- TextInfo info = mTextVector.valueAt(mIndex);
+ const TextInfo &info = mTextVector.valueAt(mIndex);
*startTimeUs = mTextVector.keyAt(mIndex);
*endTimeUs = info.endTimeUs;
- mIndex ++;
+ mIndex++;
char *str = new char[info.textLen];
- if (mDataSource->readAt(info.offset, str, info.textLen) < info.textLen) {
+ if (mSource->readAt(info.offset, str, info.textLen) < info.textLen) {
delete[] str;
return ERROR_IO;
}
-
text->append(str, info.textLen);
delete[] str;
return OK;
}
+status_t TimedTextSRTSource::extractAndAppendLocalDescriptions(
+ int64_t timeUs, const AString &text, Parcel *parcel) {
+ const void *data = text.c_str();
+ size_t size = text.size();
+ int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS |
+ TextDescriptions::OUT_OF_BAND_TEXT_SRT;
+
+ if (size > 0) {
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
+ }
+ return OK;
+}
+
} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h
new file mode 100644
index 0000000..a0734d9
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.h
@@ -0,0 +1,75 @@
+/*
+ * 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 TIMED_TEXT_SRT_SOURCE_H_
+#define TIMED_TEXT_SRT_SOURCE_H_
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/Compat.h> // off64_t
+
+#include "TimedTextSource.h"
+
+namespace android {
+
+class AString;
+class DataSource;
+class MediaBuffer;
+class Parcel;
+
+class TimedTextSRTSource : public TimedTextSource {
+ public:
+ TimedTextSRTSource(const sp<DataSource>& dataSource);
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options = NULL);
+
+ protected:
+ virtual ~TimedTextSRTSource();
+
+ private:
+ sp<DataSource> mSource;
+
+ struct TextInfo {
+ int64_t endTimeUs;
+ // The offset of the text in the original file.
+ off64_t offset;
+ int textLen;
+ };
+
+ int mIndex;
+ KeyedVector<int64_t, TextInfo> mTextVector;
+
+ void reset();
+ status_t scanFile();
+ status_t getNextSubtitleInfo(
+ off64_t *offset, int64_t *startTimeUs, TextInfo *info);
+ status_t readNextLine(off64_t *offset, AString *data);
+ status_t getText(
+ const MediaSource::ReadOptions *options,
+ AString *text, int64_t *startTimeUs, int64_t *endTimeUs);
+ status_t extractAndAppendLocalDescriptions(
+ int64_t timeUs, const AString &text, Parcel *parcel);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_SRT_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
new file mode 100644
index 0000000..ffbe1c3
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextSource.cpp
@@ -0,0 +1,62 @@
+ /*
+ * 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 "TimedTextSource"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ADebug.h> // CHECK_XX macro
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+#include "TimedTextSource.h"
+
+#include "TimedText3GPPSource.h"
+#include "TimedTextSRTSource.h"
+
+namespace android {
+
+// static
+sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
+ const sp<MediaSource>& mediaSource) {
+ const char *mime;
+ CHECK(mediaSource->getFormat()->findCString(kKeyMIMEType, &mime));
+ if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
+ return new TimedText3GPPSource(mediaSource);
+ }
+ ALOGE("Unsupported mime type for subtitle. : %s", mime);
+ return NULL;
+}
+
+// static
+sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
+ const sp<DataSource>& dataSource, FileType filetype) {
+ switch(filetype) {
+ case OUT_OF_BAND_FILE_SRT:
+ return new TimedTextSRTSource(dataSource);
+ case OUT_OF_BAND_FILE_SMI:
+ // TODO: Implement for SMI.
+ ALOGE("Supporting SMI is not implemented yet");
+ break;
+ default:
+ ALOGE("Undefined subtitle format. : %d", filetype);
+ }
+ return NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h
new file mode 100644
index 0000000..06bae71
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextSource.h
@@ -0,0 +1,61 @@
+/*
+ * 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 TIMED_TEXT_SOURCE_H_
+#define TIMED_TEXT_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h> // for DISALLOW_XXX macro.
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h> // for MediaSource::ReadOptions
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class Parcel;
+
+class TimedTextSource : public RefBase {
+ public:
+ enum FileType {
+ OUT_OF_BAND_FILE_SRT = 1,
+ OUT_OF_BAND_FILE_SMI = 2,
+ };
+ static sp<TimedTextSource> CreateTimedTextSource(
+ const sp<MediaSource>& source);
+ static sp<TimedTextSource> CreateTimedTextSource(
+ const sp<DataSource>& source, FileType filetype);
+ TimedTextSource() {}
+ virtual status_t start() = 0;
+ virtual status_t stop() = 0;
+ // Returns subtitle parcel and its start time.
+ virtual status_t read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options = NULL) = 0;
+ virtual status_t extractGlobalDescriptions(Parcel *parcel) {
+ return INVALID_OPERATION;
+ }
+
+ protected:
+ virtual ~TimedTextSource() { }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextSource);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_SOURCE_H_
diff --git a/media/libstagefright/yuv/YUVCanvas.cpp b/media/libstagefright/yuv/YUVCanvas.cpp
index 38aa779..4c9fee8 100644
--- a/media/libstagefright/yuv/YUVCanvas.cpp
+++ b/media/libstagefright/yuv/YUVCanvas.cpp
@@ -17,7 +17,7 @@
#define LOG_NDEBUG 0
#define LOG_TAG "YUVCanvas"
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/YUVCanvas.h>
#include <media/stagefright/YUVImage.h>
#include <ui/Rect.h>
diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp
index 0d67c96..7b9000b 100644
--- a/media/libstagefright/yuv/YUVImage.cpp
+++ b/media/libstagefright/yuv/YUVImage.cpp
@@ -17,9 +17,9 @@
#define LOG_NDEBUG 0
#define LOG_TAG "YUVImage"
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/YUVImage.h>
#include <ui/Rect.h>
-#include <media/stagefright/MediaDebug.h>
namespace android {