summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/stagefright/MediaBufferGroup.h9
-rw-r--r--include/media/stagefright/MediaCodecList.h14
-rw-r--r--include/media/stagefright/MediaSource.h5
-rw-r--r--include/media/stagefright/foundation/AMessage.h3
-rw-r--r--include/media/stagefright/foundation/AString.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp77
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.h10
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp55
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h1
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp122
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h6
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerSource.h8
-rw-r--r--media/libstagefright/ACodec.cpp13
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp2
-rw-r--r--media/libstagefright/MediaBufferGroup.cpp8
-rw-r--r--media/libstagefright/MediaCodecList.cpp297
-rw-r--r--media/libstagefright/MediaSource.cpp13
-rw-r--r--media/libstagefright/OggExtractor.cpp8
-rw-r--r--media/libstagefright/SampleTable.cpp189
-rw-r--r--media/libstagefright/foundation/AMessage.cpp19
-rw-r--r--media/libstagefright/include/SampleTable.h10
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.cpp5
-rw-r--r--media/libstagefright/omx/OMX.cpp2
-rw-r--r--services/audioflinger/Tracks.cpp5
25 files changed, 742 insertions, 145 deletions
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
index 0488292..a006f7f 100644
--- a/include/media/stagefright/MediaBufferGroup.h
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -34,9 +34,12 @@ public:
void add_buffer(MediaBuffer *buffer);
- // Blocks until a buffer is available and returns it to the caller,
- // the returned buffer will have a reference count of 1.
- status_t acquire_buffer(MediaBuffer **buffer);
+ // If nonBlocking is false, it blocks until a buffer is available and
+ // passes it to the caller in *buffer, while returning OK.
+ // The returned buffer will have a reference count of 1.
+ // If nonBlocking is true and a buffer is not immediately available,
+ // buffer is set to NULL and it returns WOULD_BLOCK.
+ status_t acquire_buffer(MediaBuffer **buffer, bool nonBlocking = false);
protected:
virtual void signalBufferReturned(MediaBuffer *buffer);
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index 01a5daf..c11fcc9 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -25,9 +25,12 @@
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
+#include <utils/StrongPointer.h>
namespace android {
+struct AMessage;
+
struct MediaCodecList {
static const MediaCodecList *getInstance();
@@ -51,15 +54,19 @@ struct MediaCodecList {
size_t index, const char *type,
Vector<ProfileLevel> *profileLevels,
Vector<uint32_t> *colorFormats,
- uint32_t *flags) const;
+ uint32_t *flags,
+ // TODO default argument is only for compatibility with existing JNI
+ sp<AMessage> *capabilities = NULL) const;
private:
enum Section {
SECTION_TOPLEVEL,
SECTION_DECODERS,
SECTION_DECODER,
+ SECTION_DECODER_TYPE,
SECTION_ENCODERS,
SECTION_ENCODER,
+ SECTION_ENCODER_TYPE,
SECTION_INCLUDE,
};
@@ -67,7 +74,10 @@ private:
AString mName;
bool mIsEncoder;
uint32_t mTypes;
+ uint32_t mSoleType;
uint32_t mQuirks;
+ KeyedVector<uint32_t, sp<AMessage> > mCaps;
+ sp<AMessage> mCurrentCaps;
};
static MediaCodecList *sCodecList;
@@ -103,6 +113,8 @@ private:
status_t addQuirk(const char **attrs);
status_t addTypeFromAttributes(const char **attrs);
+ status_t addLimit(const char **attrs);
+ status_t addFeature(const char **attrs);
void addType(const char *name);
DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList);
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
index 204d1c6..a653db9 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -82,6 +82,10 @@ struct MediaSource : public virtual RefBase {
void setLateBy(int64_t lateness_us);
int64_t getLateBy() const;
+ void setNonBlocking();
+ void clearNonBlocking();
+ bool getNonBlocking() const;
+
private:
enum Options {
kSeekTo_Option = 1,
@@ -91,6 +95,7 @@ struct MediaSource : public virtual RefBase {
int64_t mSeekTimeUs;
SeekMode mSeekMode;
int64_t mLatenessUs;
+ bool mNonBlocking;
};
// Causes this source to suspend pulling data from its upstream source
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 7e823eb..5846d6b 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -50,6 +50,7 @@ struct AMessage : public RefBase {
void setDouble(const char *name, double value);
void setPointer(const char *name, void *value);
void setString(const char *name, const char *s, ssize_t len = -1);
+ void setString(const char *name, const AString &s);
void setObject(const char *name, const sp<RefBase> &obj);
void setBuffer(const char *name, const sp<ABuffer> &buffer);
void setMessage(const char *name, const sp<AMessage> &obj);
@@ -58,6 +59,8 @@ struct AMessage : public RefBase {
const char *name,
int32_t left, int32_t top, int32_t right, int32_t bottom);
+ bool contains(const char *name) const;
+
bool findInt32(const char *name, int32_t *value) const;
bool findInt64(const char *name, int64_t *value) const;
bool findSize(const char *name, size_t *value) const;
diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
index 0edaa1c..4be3c6d 100644
--- a/include/media/stagefright/foundation/AString.h
+++ b/include/media/stagefright/foundation/AString.h
@@ -70,6 +70,9 @@ struct AString {
size_t hash() const;
bool operator==(const AString &other) const;
+ bool operator!=(const AString &other) const {
+ return !operator==(other);
+ }
bool operator<(const AString &other) const;
bool operator>(const AString &other) const;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index cc0cb01..d75408d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -28,6 +28,7 @@
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
+#include "../../libstagefright/include/WVMExtractor.h"
namespace android {
@@ -35,10 +36,16 @@ NuPlayer::GenericSource::GenericSource(
const sp<AMessage> &notify,
const sp<IMediaHTTPService> &httpService,
const char *url,
- const KeyedVector<String8, String8> *headers)
+ const KeyedVector<String8, String8> *headers,
+ bool isWidevine,
+ bool uidValid,
+ uid_t uid)
: Source(notify),
mDurationUs(0ll),
- mAudioIsVorbis(false) {
+ mAudioIsVorbis(false),
+ mIsWidevine(isWidevine),
+ mUIDValid(uidValid),
+ mUID(uid) {
DataSource::RegisterDefaultSniffers();
sp<DataSource> dataSource =
@@ -63,7 +70,31 @@ NuPlayer::GenericSource::GenericSource(
void NuPlayer::GenericSource::initFromDataSource(
const sp<DataSource> &dataSource) {
- sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+ sp<MediaExtractor> extractor;
+
+ if (mIsWidevine) {
+ String8 mimeType;
+ float confidence;
+ sp<AMessage> dummy;
+ bool success;
+
+ success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
+ if (!success
+ || strcasecmp(
+ mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
+ ALOGE("unsupported widevine mime: %s", mimeType.string());
+ return;
+ }
+
+ sp<WVMExtractor> wvmExtractor = new WVMExtractor(dataSource);
+ wvmExtractor->setAdaptiveStreamingMode(true);
+ if (mUIDValid) {
+ wvmExtractor->setUID(mUID);
+ }
+ extractor = wvmExtractor;
+ } else {
+ extractor = MediaExtractor::Create(dataSource);
+ }
CHECK(extractor != NULL);
@@ -113,6 +144,13 @@ void NuPlayer::GenericSource::initFromDataSource(
}
}
+status_t NuPlayer::GenericSource::setBuffers(bool audio, Vector<MediaBuffer *> &buffers) {
+ if (mIsWidevine && !audio) {
+ return mVideoTrack.mSource->setBuffers(buffers);
+ }
+ return INVALID_OPERATION;
+}
+
NuPlayer::GenericSource::~GenericSource() {
}
@@ -128,7 +166,8 @@ void NuPlayer::GenericSource::prepareAsync() {
}
notifyFlagsChanged(
- FLAG_CAN_PAUSE
+ (mIsWidevine ? FLAG_SECURE : 0)
+ | FLAG_CAN_PAUSE
| FLAG_CAN_SEEK_BACKWARD
| FLAG_CAN_SEEK_FORWARD
| FLAG_CAN_SEEK);
@@ -180,9 +219,14 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit(
return -EWOULDBLOCK;
}
+ if (mIsWidevine && !audio) {
+ // try to read a buffer as we may not have been able to the last time
+ readBuffer(audio, -1ll);
+ }
+
status_t finalResult;
if (!track->mPackets->hasBufferAvailable(&finalResult)) {
- return finalResult == OK ? -EWOULDBLOCK : finalResult;
+ return (finalResult == OK ? -EWOULDBLOCK : finalResult);
}
status_t result = track->mPackets->dequeueAccessUnit(accessUnit);
@@ -280,6 +324,10 @@ void NuPlayer::GenericSource::readBuffer(
seeking = true;
}
+ if (mIsWidevine && !audio) {
+ options.setNonBlocking();
+ }
+
for (;;) {
MediaBuffer *mbuf;
status_t err = track->mSource->read(&mbuf, &options);
@@ -293,11 +341,18 @@ void NuPlayer::GenericSource::readBuffer(
outLength += sizeof(int32_t);
}
- sp<ABuffer> buffer = new ABuffer(outLength);
-
- memcpy(buffer->data(),
- (const uint8_t *)mbuf->data() + mbuf->range_offset(),
- mbuf->range_length());
+ sp<ABuffer> buffer;
+ if (mIsWidevine && !audio) {
+ // data is already provided in the buffer
+ buffer = new ABuffer(NULL, mbuf->range_length());
+ buffer->meta()->setPointer("mediaBuffer", mbuf);
+ mbuf->add_ref();
+ } else {
+ buffer = new ABuffer(outLength);
+ memcpy(buffer->data(),
+ (const uint8_t *)mbuf->data() + mbuf->range_offset(),
+ mbuf->range_length());
+ }
if (audio && mAudioIsVorbis) {
int32_t numPageSamples;
@@ -332,6 +387,8 @@ void NuPlayer::GenericSource::readBuffer(
track->mPackets->queueAccessUnit(buffer);
break;
+ } else if (err == WOULD_BLOCK) {
+ break;
} else if (err == INFO_FORMAT_CHANGED) {
#if 0
track->mPackets->queueDiscontinuity(
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index e0cd20f..8e0209d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -35,7 +35,10 @@ struct NuPlayer::GenericSource : public NuPlayer::Source {
const sp<AMessage> &notify,
const sp<IMediaHTTPService> &httpService,
const char *url,
- const KeyedVector<String8, String8> *headers);
+ const KeyedVector<String8, String8> *headers,
+ bool isWidevine = false,
+ bool uidValid = false,
+ uid_t uid = 0);
GenericSource(
const sp<AMessage> &notify,
@@ -54,6 +57,8 @@ struct NuPlayer::GenericSource : public NuPlayer::Source {
virtual sp<AMessage> getTrackInfo(size_t trackIndex) const;
virtual status_t seekTo(int64_t seekTimeUs);
+ virtual status_t setBuffers(bool audio, Vector<MediaBuffer *> &buffers);
+
protected:
virtual ~GenericSource();
@@ -73,6 +78,9 @@ private:
int64_t mDurationUs;
bool mAudioIsVorbis;
+ bool mIsWidevine;
+ bool mUIDValid;
+ uid_t mUID;
void initFromDataSource(const sp<DataSource> &dataSource);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 88c59bf..6ccd27a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -36,6 +36,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -221,6 +222,10 @@ void NuPlayer::setDataSourceAsync(
|| strstr(url, ".sdp?"))) {
source = new RTSPSource(
notify, httpService, url, headers, mUIDValid, mUID, true);
+ } else if ((!strncasecmp(url, "widevine://", 11))) {
+ source = new GenericSource(notify, httpService, url, headers,
+ true /* isWidevine */, mUIDValid, mUID);
+ mSourceFlags |= Source::FLAG_SECURE;
} else {
source = new GenericSource(notify, httpService, url, headers);
}
@@ -512,6 +517,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
mNumFramesDropped = 0;
mStarted = true;
+ /* instantiate decoders now for secure playback */
+ if (mSourceFlags & Source::FLAG_SECURE) {
+ if (mNativeWindow != NULL) {
+ instantiateDecoder(false, &mVideoDecoder);
+ }
+
+ if (mAudioSink != NULL) {
+ instantiateDecoder(true, &mAudioDecoder);
+ }
+ }
+
mSource->start();
uint32_t flags = 0;
@@ -540,7 +556,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
new AMessage(kWhatRendererNotify, id()),
flags);
- looper()->registerHandler(mRenderer);
+ mRendererLooper = new ALooper;
+ mRendererLooper->setName("NuPlayerRenderer");
+ mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ mRendererLooper->registerHandler(mRenderer);
postScanSources();
break;
@@ -1055,6 +1074,10 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id());
mCCDecoder = new CCDecoder(ccNotify);
+
+ if (mSourceFlags & Source::FLAG_SECURE) {
+ format->setInt32("secure", true);
+ }
}
sp<AMessage> notify =
@@ -1073,6 +1096,28 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
(*decoder)->init();
(*decoder)->configure(format);
+ // allocate buffers to decrypt widevine source buffers
+ if (!audio && (mSourceFlags & Source::FLAG_SECURE)) {
+ Vector<sp<ABuffer> > inputBufs;
+ CHECK_EQ((*decoder)->getInputBuffers(&inputBufs), (status_t)OK);
+
+ Vector<MediaBuffer *> mediaBufs;
+ for (size_t i = 0; i < inputBufs.size(); i++) {
+ const sp<ABuffer> &buffer = inputBufs[i];
+ MediaBuffer *mbuf = new MediaBuffer(buffer->data(), buffer->size());
+ mediaBufs.push(mbuf);
+ }
+
+ status_t err = mSource->setBuffers(audio, mediaBufs);
+ if (err != OK) {
+ for (size_t i = 0; i < mediaBufs.size(); ++i) {
+ mediaBufs[i]->release();
+ }
+ mediaBufs.clear();
+ ALOGE("Secure source didn't support secure mediaBufs.");
+ return err;
+ }
+ }
return OK;
}
@@ -1184,6 +1229,7 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
dropAccessUnit = false;
if (!audio
+ && !(mSourceFlags & Source::FLAG_SECURE)
&& mVideoLateByUs > 100000ll
&& mVideoIsAVC
&& !IsAVCReferenceFrame(accessUnit)) {
@@ -1497,6 +1543,13 @@ void NuPlayer::performReset() {
++mScanSourcesGeneration;
mScanSourcesPending = false;
+ if (mRendererLooper != NULL) {
+ if (mRenderer != NULL) {
+ mRendererLooper->unregisterHandler(mRenderer->id());
+ }
+ mRendererLooper->stop();
+ mRendererLooper.clear();
+ }
mRenderer.clear();
if (mSource != NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index d7c00aa..c04e277 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -125,6 +125,7 @@ private:
sp<Decoder> mAudioDecoder;
sp<CCDecoder> mCCDecoder;
sp<Renderer> mRenderer;
+ sp<ALooper> mRendererLooper;
List<sp<Action> > mDeferredActions;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index dd73cc4..1b9bafb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -26,6 +26,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
@@ -54,6 +55,22 @@ NuPlayer::Decoder::Decoder(
NuPlayer::Decoder::~Decoder() {
}
+static
+status_t 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;
+}
+
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK(mCodec == NULL);
@@ -72,8 +89,20 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get());
mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */);
+ int32_t secure = 0;
+ if (format->findInt32("secure", &secure) && secure != 0) {
+ if (mCodec != NULL) {
+ mCodec->getName(&mComponentName);
+ mComponentName.append(".secure");
+ mCodec->release();
+ ALOGI("[%s] creating", mComponentName.c_str());
+ mCodec = MediaCodec::CreateByComponentName(
+ mCodecLooper, mComponentName.c_str());
+ }
+ }
if (mCodec == NULL) {
- ALOGE("Failed to create %s decoder", mime.c_str());
+ ALOGE("Failed to create %s%s decoder",
+ (secure ? "secure " : ""), mime.c_str());
handleError(UNKNOWN_ERROR);
return;
}
@@ -107,6 +136,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
// the following should work after start
CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
+ releaseAndResetMediaBuffers();
CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
ALOGV("[%s] got %zu input and %zu output buffers",
mComponentName.c_str(),
@@ -117,6 +147,18 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
mPaused = false;
}
+void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
+ for (size_t i = 0; i < mMediaBuffers.size(); i++) {
+ if (mMediaBuffers[i] != NULL) {
+ mMediaBuffers[i]->release();
+ mMediaBuffers.editItemAt(i) = NULL;
+ }
+ }
+ mMediaBuffers.resize(mInputBuffers.size());
+ mInputBufferIsDequeued.clear();
+ mInputBufferIsDequeued.resize(mInputBuffers.size());
+}
+
void NuPlayer::Decoder::requestCodecNotification() {
if (mCodec != NULL) {
sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
@@ -141,6 +183,14 @@ void NuPlayer::Decoder::configure(const sp<AMessage> &format) {
msg->post();
}
+status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id());
+ msg->setPointer("buffers", buffers);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
void NuPlayer::Decoder::handleError(int32_t err)
{
sp<AMessage> notify = mNotify->dup();
@@ -163,6 +213,12 @@ bool NuPlayer::Decoder::handleAnInputBuffer() {
CHECK_LT(bufferIx, mInputBuffers.size());
+ if (mMediaBuffers[bufferIx] != NULL) {
+ mMediaBuffers[bufferIx]->release();
+ mMediaBuffers.editItemAt(bufferIx) = NULL;
+ }
+ mInputBufferIsDequeued.editItemAt(bufferIx) = true;
+
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
reply->setSize("buffer-ix", bufferIx);
reply->setInt32("generation", mBufferGeneration);
@@ -183,6 +239,44 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
sp<ABuffer> buffer;
bool hasBuffer = msg->findBuffer("buffer", &buffer);
+
+ // handle widevine classic source - that fills an arbitrary input buffer
+ MediaBuffer *mediaBuffer = NULL;
+ if (hasBuffer && buffer->meta()->findPointer(
+ "mediaBuffer", (void **)&mediaBuffer)) {
+ if (mediaBuffer == NULL) {
+ // received no actual buffer
+ ALOGW("[%s] received null MediaBuffer %s",
+ mComponentName.c_str(), msg->debugString().c_str());
+ buffer = NULL;
+ } else {
+ // likely filled another buffer than we requested: adjust buffer index
+ size_t ix;
+ for (ix = 0; ix < mInputBuffers.size(); ix++) {
+ const sp<ABuffer> &buf = mInputBuffers[ix];
+ if (buf->data() == mediaBuffer->data()) {
+ // all input buffers are dequeued on start, hence the check
+ CHECK(mInputBufferIsDequeued[ix]);
+ ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
+ mComponentName.c_str(), ix, bufferIx);
+
+ // TRICKY: need buffer for the metadata, so instead, set
+ // codecBuffer to the same (though incorrect) buffer to
+ // avoid a memcpy into the codecBuffer
+ codecBuffer = buffer;
+ codecBuffer->setRange(
+ mediaBuffer->range_offset(),
+ mediaBuffer->range_length());
+ bufferIx = ix;
+ break;
+ }
+ }
+ CHECK(ix < mInputBuffers.size());
+ }
+ }
+
+ mInputBufferIsDequeued.editItemAt(bufferIx) = false;
+
if (buffer == NULL /* includes !hasBuffer */) {
int32_t streamErr = ERROR_END_OF_STREAM;
CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
@@ -236,6 +330,11 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
mComponentName.c_str(), err);
handleError(err);
}
+
+ if (mediaBuffer != NULL) {
+ CHECK(mMediaBuffers[bufferIx] == NULL);
+ mMediaBuffers.editItemAt(bufferIx) = mediaBuffer;
+ }
}
}
@@ -352,6 +451,8 @@ void NuPlayer::Decoder::onFlush() {
return;
}
+ releaseAndResetMediaBuffers();
+
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFlushCompleted);
notify->post();
@@ -379,6 +480,8 @@ void NuPlayer::Decoder::onShutdown() {
mComponentName = "decoder";
}
+ releaseAndResetMediaBuffers();
+
if (err != OK) {
ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
handleError(err);
@@ -403,6 +506,23 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatGetInputBuffers:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ Vector<sp<ABuffer> > *dstBuffers;
+ CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
+
+ dstBuffers->clear();
+ for (size_t i = 0; i < mInputBuffers.size(); i++) {
+ dstBuffers->push(mInputBuffers[i]);
+ }
+
+ (new AMessage)->postReply(replyID);
+ break;
+ }
+
case kWhatCodecNotify:
{
if (!isStaleReply(msg)) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 4fa0dbd..c6fc237 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -26,6 +26,7 @@ namespace android {
struct ABuffer;
struct MediaCodec;
+struct MediaBuffer;
struct NuPlayer::Decoder : public AHandler {
Decoder(const sp<AMessage> &notify,
@@ -34,6 +35,7 @@ struct NuPlayer::Decoder : public AHandler {
virtual void configure(const sp<AMessage> &format);
virtual void init();
+ status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const;
virtual void signalFlush();
virtual void signalResume();
virtual void initiateShutdown();
@@ -60,6 +62,7 @@ private:
enum {
kWhatCodecNotify = 'cdcN',
kWhatConfigure = 'conf',
+ kWhatGetInputBuffers = 'gInB',
kWhatInputBufferFilled = 'inpF',
kWhatRenderBuffer = 'rndr',
kWhatFlush = 'flus',
@@ -77,11 +80,14 @@ private:
Vector<sp<ABuffer> > mInputBuffers;
Vector<sp<ABuffer> > mOutputBuffers;
+ Vector<bool> mInputBufferIsDequeued;
+ Vector<MediaBuffer *> mMediaBuffers;
void handleError(int32_t err);
bool handleAnInputBuffer();
bool handleAnOutputBuffer();
+ void releaseAndResetMediaBuffers();
void requestCodecNotification();
bool isStaleReply(const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index f520ff7..8592ec2 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -26,6 +26,8 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <inttypes.h>
+
namespace android {
// static
@@ -502,6 +504,7 @@ void NuPlayer::Renderer::postDrainVideoQueue() {
}
}
+ ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
msg->post(delayUs);
mDrainVideoQueuePending = true;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 632c4a6..259925f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -21,11 +21,14 @@
#include "NuPlayer.h"
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/Vector.h>
namespace android {
struct ABuffer;
struct MetaData;
+struct MediaBuffer;
struct NuPlayer::Source : public AHandler {
enum Flags {
@@ -34,6 +37,7 @@ struct NuPlayer::Source : public AHandler {
FLAG_CAN_SEEK_FORWARD = 4, // the "10 sec forward button"
FLAG_CAN_SEEK = 8, // the "seek bar"
FLAG_DYNAMIC_DURATION = 16,
+ FLAG_SECURE = 32,
};
enum {
@@ -89,6 +93,10 @@ struct NuPlayer::Source : public AHandler {
return INVALID_OPERATION;
}
+ virtual status_t setBuffers(bool /* audio */, Vector<MediaBuffer *> &/* buffers */) {
+ return INVALID_OPERATION;
+ }
+
virtual bool isRealTime() const {
return false;
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 9c64d72..6cb1c64 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -3989,6 +3989,8 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
if (err == OK) {
break;
+ } else {
+ ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
}
node = NULL;
@@ -4504,11 +4506,14 @@ void ACodec::ExecutingState::resume() {
submitOutputBuffers();
- // Post the first input buffer.
+ // Post all available input buffers
CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
- BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0);
-
- postFillThisBuffer(info);
+ for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); i++) {
+ BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);
+ if (info->mStatus == BufferInfo::OWNED_BY_US) {
+ postFillThisBuffer(info);
+ }
+ }
mActive = true;
}
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 207acc8..19da6ee 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -3665,7 +3665,7 @@ status_t MPEG4Source::read(
uint32_t sampleIndex;
status_t err = mSampleTable->findSampleAtTime(
- seekTimeUs * mTimescale / 1000000,
+ seekTimeUs, 1000000, mTimescale,
&sampleIndex, findFlags);
if (mode == ReadOptions::SEEK_CLOSEST) {
diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
index 80aae51..6ac6d4a 100644
--- a/media/libstagefright/MediaBufferGroup.cpp
+++ b/media/libstagefright/MediaBufferGroup.cpp
@@ -55,7 +55,8 @@ void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
mLastBuffer = buffer;
}
-status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) {
+status_t MediaBufferGroup::acquire_buffer(
+ MediaBuffer **out, bool nonBlocking) {
Mutex::Autolock autoLock(mLock);
for (;;) {
@@ -70,6 +71,11 @@ status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) {
}
}
+ if (nonBlocking) {
+ *out = NULL;
+ return WOULD_BLOCK;
+ }
+
// All buffers are in use. Block until one of them is returned to us.
mCondition.wait(mLock);
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index cd51582..8f54343 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -21,6 +21,7 @@
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
@@ -79,6 +80,19 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
info->mName.c_str());
mCodecInfos.removeAt(i);
+#if LOG_NDEBUG == 0
+ } else {
+ for (size_t type_ix = 0; type_ix < mTypes.size(); ++type_ix) {
+ uint32_t typeMask = 1ul << mTypes.valueAt(type_ix);
+ if (info->mTypes & typeMask) {
+ AString mime = mTypes.keyAt(type_ix);
+ uint32_t bit = mTypes.valueAt(type_ix);
+
+ ALOGV("%s codec info for %s: %s", info->mName.c_str(), mime.c_str(),
+ info->mCaps.editValueFor(bit)->debugString().c_str());
+ }
+ }
+#endif
}
}
@@ -217,6 +231,8 @@ void MediaCodecList::startElementHandler(
return;
}
+ bool inType = true;
+
if (!strcmp(name, "Include")) {
mInitCheck = includeXMLFile(attrs);
if (mInitCheck == OK) {
@@ -267,6 +283,26 @@ void MediaCodecList::startElementHandler(
mInitCheck = addQuirk(attrs);
} else if (!strcmp(name, "Type")) {
mInitCheck = addTypeFromAttributes(attrs);
+ mCurrentSection =
+ (mCurrentSection == SECTION_DECODER
+ ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
+ }
+ }
+ inType = false;
+ // fall through
+
+ case SECTION_DECODER_TYPE:
+ case SECTION_ENCODER_TYPE:
+ {
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ // ignore limits and features specified outside of type
+ bool outside = !inType && info->mSoleType == 0;
+ if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
+ ALOGW("ignoring %s specified outside of a Type", name);
+ } else if (!strcmp(name, "Limit")) {
+ mInitCheck = addLimit(attrs);
+ } else if (!strcmp(name, "Feature")) {
+ mInitCheck = addFeature(attrs);
}
break;
}
@@ -300,10 +336,27 @@ void MediaCodecList::endElementHandler(const char *name) {
break;
}
+ case SECTION_DECODER_TYPE:
+ case SECTION_ENCODER_TYPE:
+ {
+ if (!strcmp(name, "Type")) {
+ mCurrentSection =
+ (mCurrentSection == SECTION_DECODER_TYPE
+ ? SECTION_DECODER : SECTION_ENCODER);
+
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ info->mCurrentCaps = NULL;
+ }
+ break;
+ }
+
case SECTION_DECODER:
{
if (!strcmp(name, "MediaCodec")) {
mCurrentSection = SECTION_DECODERS;
+
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ info->mCurrentCaps = NULL;
}
break;
}
@@ -312,6 +365,9 @@ void MediaCodecList::endElementHandler(const char *name) {
{
if (!strcmp(name, "MediaCodec")) {
mCurrentSection = SECTION_ENCODERS;
+
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ info->mCurrentCaps = NULL;
}
break;
}
@@ -373,11 +429,16 @@ void MediaCodecList::addMediaCodec(
CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
info->mName = name;
info->mIsEncoder = encoder;
+ info->mSoleType = 0;
info->mTypes = 0;
info->mQuirks = 0;
+ info->mCurrentCaps = NULL;
if (type != NULL) {
addType(type);
+ // if type was specified in attributes, we do not allow
+ // subsequent types
+ info->mSoleType = info->mTypes;
}
}
@@ -427,6 +488,12 @@ status_t MediaCodecList::addQuirk(const char **attrs) {
status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
const char *name = NULL;
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ if (info->mSoleType != 0) {
+ ALOGE("Codec '%s' already had its type specified", info->mName.c_str());
+ return -EINVAL;
+ }
+
size_t i = 0;
while (attrs[i] != NULL) {
if (!strcmp(attrs[i], "name")) {
@@ -469,6 +536,11 @@ void MediaCodecList::addType(const char *name) {
CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
info->mTypes |= 1ul << bit;
+ if (info->mCaps.indexOfKey(bit) < 0) {
+ AMessage *msg = new AMessage();
+ info->mCaps.add(bit, msg);
+ }
+ info->mCurrentCaps = info->mCaps.editValueFor(bit);
}
ssize_t MediaCodecList::findCodecByType(
@@ -494,6 +566,216 @@ ssize_t MediaCodecList::findCodecByType(
return -ENOENT;
}
+static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) {
+ ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
+ (found ? "" : "no "), attr);
+ return -EINVAL;
+}
+
+static status_t limitError(AString name, const char *msg) {
+ ALOGE("limit '%s' %s", name.c_str(), msg);
+ return -EINVAL;
+}
+
+static status_t limitInvalidAttr(AString name, const char *attr, AString value) {
+ ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
+ attr, value.c_str());
+ return -EINVAL;
+}
+
+status_t MediaCodecList::addLimit(const char **attrs) {
+ sp<AMessage> msg = new AMessage();
+
+ size_t i = 0;
+ while (attrs[i] != NULL) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+
+ // attributes with values
+ if (!strcmp(attrs[i], "name")
+ || !strcmp(attrs[i], "default")
+ || !strcmp(attrs[i], "in")
+ || !strcmp(attrs[i], "max")
+ || !strcmp(attrs[i], "min")
+ || !strcmp(attrs[i], "range")
+ || !strcmp(attrs[i], "ranges")
+ || !strcmp(attrs[i], "scale")
+ || !strcmp(attrs[i], "value")) {
+ msg->setString(attrs[i], attrs[i + 1]);
+ ++i;
+ } else {
+ return -EINVAL;
+ }
+ ++i;
+ }
+
+ AString name;
+ if (!msg->findString("name", &name)) {
+ ALOGE("limit with no 'name' attribute");
+ return -EINVAL;
+ }
+
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+
+ // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range
+ // quality: range + default + [scale]
+ // complexity: range + default
+ bool found;
+ if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
+ || name == "blocks-per-second" || name == "complexity"
+ || name == "frame-rate" || name == "quality" || name == "size") {
+ AString min, max;
+ if (msg->findString("min", &min) && msg->findString("max", &max)) {
+ min.append("-");
+ min.append(max);
+ if (msg->contains("range") || msg->contains("value")) {
+ return limitError(name, "has 'min' and 'max' as well as 'range' or "
+ "'value' attributes");
+ }
+ msg->setString("range", min);
+ } else if (msg->contains("min") || msg->contains("max")) {
+ return limitError(name, "has only 'min' or 'max' attribute");
+ } else if (msg->findString("value", &max)) {
+ min = max;
+ min.append("-");
+ min.append(max);
+ if (msg->contains("range")) {
+ return limitError(name, "has both 'range' and 'value' attributes");
+ }
+ msg->setString("range", min);
+ }
+
+ AString range, scale = "linear", def, in_;
+ if (!msg->findString("range", &range)) {
+ return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
+ }
+
+ if ((name == "quality" || name == "complexity") ^
+ (found = msg->findString("default", &def))) {
+ return limitFoundMissingAttr(name, "default", found);
+ }
+ if (name != "quality" && msg->findString("scale", &scale)) {
+ return limitFoundMissingAttr(name, "scale");
+ }
+ if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
+ return limitFoundMissingAttr(name, "in", found);
+ }
+
+ if (name == "aspect-ratio") {
+ if (!(in_ == "pixels") && !(in_ == "blocks")) {
+ return limitInvalidAttr(name, "in", in_);
+ }
+ in_.erase(5, 1); // (pixel|block)-aspect-ratio
+ in_.append("-");
+ in_.append(name);
+ name = in_;
+ }
+ if (name == "quality") {
+ info->mCurrentCaps->setString("quality-scale", scale);
+ }
+ if (name == "quality" || name == "complexity") {
+ AString tag = name;
+ tag.append("-default");
+ info->mCurrentCaps->setString(tag.c_str(), def);
+ }
+ AString tag = name;
+ tag.append("-range");
+ info->mCurrentCaps->setString(tag.c_str(), range);
+ } else {
+ AString max, value, ranges;
+ if (msg->contains("default")) {
+ return limitFoundMissingAttr(name, "default");
+ } else if (msg->contains("in")) {
+ return limitFoundMissingAttr(name, "in");
+ } else if ((name == "channel-count") ^
+ (found = msg->findString("max", &max))) {
+ return limitFoundMissingAttr(name, "max", found);
+ } else if (msg->contains("min")) {
+ return limitFoundMissingAttr(name, "min");
+ } else if (msg->contains("range")) {
+ return limitFoundMissingAttr(name, "range");
+ } else if ((name == "sample-rate") ^
+ (found = msg->findString("ranges", &ranges))) {
+ return limitFoundMissingAttr(name, "ranges", found);
+ } else if (msg->contains("scale")) {
+ return limitFoundMissingAttr(name, "scale");
+ } else if ((name == "alignment" || name == "block-size") ^
+ (found = msg->findString("value", &value))) {
+ return limitFoundMissingAttr(name, "value", found);
+ }
+
+ if (max.size()) {
+ AString tag = "max-";
+ tag.append(name);
+ info->mCurrentCaps->setString(tag.c_str(), max);
+ } else if (value.size()) {
+ info->mCurrentCaps->setString(name.c_str(), value);
+ } else if (ranges.size()) {
+ AString tag = name;
+ tag.append("-ranges");
+ info->mCurrentCaps->setString(tag.c_str(), ranges);
+ } else {
+ ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
+ }
+ }
+ return OK;
+}
+
+static bool parseBoolean(const char *s) {
+ if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
+ return true;
+ }
+ char *end;
+ unsigned long res = strtoul(s, &end, 10);
+ return *s != '\0' && *end == '\0' && res > 0;
+}
+
+status_t MediaCodecList::addFeature(const char **attrs) {
+ size_t i = 0;
+ const char *name = NULL;
+ int32_t optional = -1;
+ int32_t required = -1;
+
+ while (attrs[i] != NULL) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+
+ // attributes with values
+ if (!strcmp(attrs[i], "name")) {
+ name = attrs[i + 1];
+ ++i;
+ } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
+ int value = (int)parseBoolean(attrs[i + 1]);
+ if (!strcmp(attrs[i], "optional")) {
+ optional = value;
+ } else {
+ required = value;
+ }
+ ++i;
+ } else {
+ return -EINVAL;
+ }
+ ++i;
+ }
+ if (name == NULL) {
+ ALOGE("feature with no 'name' attribute");
+ return -EINVAL;
+ }
+
+ if (optional == required && optional != -1) {
+ ALOGE("feature '%s' is both/neither optional and required", name);
+ return -EINVAL;
+ }
+
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ AString tag = "feature-";
+ tag.append(name);
+ info->mCurrentCaps->setInt32(tag.c_str(), (required == 1) || (optional == 0));
+ return OK;
+}
+
ssize_t MediaCodecList::findCodecByName(const char *name) const {
for (size_t i = 0; i < mCodecInfos.size(); ++i) {
const CodecInfo &info = mCodecInfos.itemAt(i);
@@ -571,7 +853,8 @@ status_t MediaCodecList::getCodecCapabilities(
size_t index, const char *type,
Vector<ProfileLevel> *profileLevels,
Vector<uint32_t> *colorFormats,
- uint32_t *flags) const {
+ uint32_t *flags,
+ sp<AMessage> *capabilities) const {
profileLevels->clear();
colorFormats->clear();
@@ -581,6 +864,13 @@ status_t MediaCodecList::getCodecCapabilities(
const CodecInfo &info = mCodecInfos.itemAt(index);
+ ssize_t typeIndex = mTypes.indexOfKey(type);
+ if (typeIndex < 0) {
+ return -EINVAL;
+ }
+ // essentially doing valueFor without the CHECK abort
+ typeIndex = mTypes.valueAt(typeIndex);
+
OMXClient client;
status_t err = client.connect();
if (err != OK) {
@@ -611,6 +901,11 @@ status_t MediaCodecList::getCodecCapabilities(
*flags = caps.mFlags;
+ // TODO this check will be removed once JNI side is merged
+ if (capabilities != NULL) {
+ *capabilities = info.mCaps.valueFor(typeIndex);
+ }
+
return OK;
}
diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp
index fd0e79c..576471a 100644
--- a/media/libstagefright/MediaSource.cpp
+++ b/media/libstagefright/MediaSource.cpp
@@ -32,6 +32,19 @@ void MediaSource::ReadOptions::reset() {
mOptions = 0;
mSeekTimeUs = 0;
mLatenessUs = 0;
+ mNonBlocking = false;
+}
+
+void MediaSource::ReadOptions::setNonBlocking() {
+ mNonBlocking = true;
+}
+
+void MediaSource::ReadOptions::clearNonBlocking() {
+ mNonBlocking = false;
+}
+
+bool MediaSource::ReadOptions::getNonBlocking() const {
+ return mNonBlocking;
}
void MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 8c15929..0838004 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -320,14 +320,14 @@ status_t MyVorbisExtractor::seekToTime(int64_t timeUs) {
}
size_t left = 0;
- size_t right = mTableOfContents.size();
- while (left < right) {
- size_t center = left / 2 + right / 2 + (left & right & 1);
+ size_t right = mTableOfContents.size() - 1;
+ while (left <= right) {
+ size_t center = left + (right - left) / 2;
const TOCEntry &entry = mTableOfContents.itemAt(center);
if (timeUs < entry.mTimeUs) {
- right = center;
+ right = center - 1;
} else if (timeUs > entry.mTimeUs) {
left = center + 1;
} else {
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 9a92805..c7e24fc 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -520,83 +520,72 @@ void SampleTable::buildSampleEntriesTable() {
}
status_t SampleTable::findSampleAtTime(
- uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+ uint64_t req_time, uint64_t scale_num, uint64_t scale_den,
+ uint32_t *sample_index, uint32_t flags) {
buildSampleEntriesTable();
uint32_t left = 0;
- uint32_t right = mNumSampleSizes;
- while (left < right) {
- uint32_t center = (left + right) / 2;
- uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime;
+ uint32_t right = mNumSampleSizes - 1;
+ while (left <= right) {
+ uint32_t center = left + (right - left) / 2;
+ uint64_t centerTime =
+ getSampleTime(center, scale_num, scale_den);
if (req_time < centerTime) {
- right = center;
+ right = center - 1;
} else if (req_time > centerTime) {
left = center + 1;
} else {
- left = center;
- break;
+ *sample_index = mSampleTimeEntries[center].mSampleIndex;
+ return OK;
}
}
- if (left == mNumSampleSizes) {
+ uint32_t closestIndex = left;
+
+ if (closestIndex == mNumSampleSizes) {
if (flags == kFlagAfter) {
return ERROR_OUT_OF_RANGE;
}
-
- --left;
+ flags = kFlagBefore;
+ } else if (closestIndex == 0) {
+ if (flags == kFlagBefore) {
+ // normally we should return out of range, but that is
+ // treated as end-of-stream. instead return first sample
+ //
+ // return ERROR_OUT_OF_RANGE;
+ }
+ flags = kFlagAfter;
}
- uint32_t closestIndex = left;
-
switch (flags) {
case kFlagBefore:
{
- while (closestIndex > 0
- && mSampleTimeEntries[closestIndex].mCompositionTime
- > req_time) {
- --closestIndex;
- }
+ --closestIndex;
break;
}
case kFlagAfter:
{
- while (closestIndex + 1 < mNumSampleSizes
- && mSampleTimeEntries[closestIndex].mCompositionTime
- < req_time) {
- ++closestIndex;
- }
+ // nothing to do
break;
}
default:
{
CHECK(flags == kFlagClosest);
-
- if (closestIndex > 0) {
- // Check left neighbour and pick closest.
- uint32_t absdiff1 =
- abs_difference(
- mSampleTimeEntries[closestIndex].mCompositionTime,
- req_time);
-
- uint32_t absdiff2 =
- abs_difference(
- mSampleTimeEntries[closestIndex - 1].mCompositionTime,
- req_time);
-
- if (absdiff1 > absdiff2) {
- closestIndex = closestIndex - 1;
- }
+ // pick closest based on timestamp. use abs_difference for safety
+ if (abs_difference(
+ getSampleTime(closestIndex, scale_num, scale_den), req_time) >
+ abs_difference(
+ req_time, getSampleTime(closestIndex - 1, scale_num, scale_den))) {
+ --closestIndex;
}
-
break;
}
}
*sample_index = mSampleTimeEntries[closestIndex].mSampleIndex;
-
return OK;
}
@@ -618,109 +607,85 @@ status_t SampleTable::findSyncSampleNear(
}
uint32_t left = 0;
- uint32_t right = mNumSyncSamples;
- while (left < right) {
+ uint32_t right = mNumSyncSamples - 1;
+ while (left <= right) {
uint32_t center = left + (right - left) / 2;
uint32_t x = mSyncSamples[center];
if (start_sample_index < x) {
- right = center;
+ right = center - 1;
} else if (start_sample_index > x) {
left = center + 1;
} else {
- left = center;
- break;
+ *sample_index = x;
+ return OK;
}
}
+
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;
+ flags = kFlagBefore;
}
+ else if (left == 0) {
+ if (flags == kFlagBefore) {
+ ALOGE("tried to find a sync frame before the first one: %d", 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];
-
- if (left + 1 < mNumSyncSamples) {
- uint32_t y = mSyncSamples[left + 1];
-
- // our sample lies between sync samples x and y.
-
- status_t err = mSampleIterator->seekTo(start_sample_index);
- if (err != OK) {
- return err;
- }
-
- uint32_t sample_time = mSampleIterator->getSampleTime();
-
- err = mSampleIterator->seekTo(x);
- if (err != OK) {
- return err;
- }
- uint32_t x_time = mSampleIterator->getSampleTime();
-
- err = mSampleIterator->seekTo(y);
- if (err != OK) {
- return err;
- }
-
- uint32_t y_time = mSampleIterator->getSampleTime();
-
- if (abs_difference(x_time, sample_time)
- > abs_difference(y_time, sample_time)) {
- // Pick the sync sample closest (timewise) to the start-sample.
- x = y;
- ++left;
+ // normally we should return out of range, but that is
+ // treated as end-of-stream. instead seek to first sync
+ //
+ // return ERROR_OUT_OF_RANGE;
}
+ flags = kFlagAfter;
}
+ // Now ssi[left - 1] <(=) start_sample_index <= ssi[left]
switch (flags) {
case kFlagBefore:
{
- if (x > start_sample_index) {
- CHECK(left > 0);
-
- x = mSyncSamples[left - 1];
-
- if (x > start_sample_index) {
- // The table of sync sample indices was not sorted
- // properly.
- return ERROR_MALFORMED;
- }
- }
+ --left;
break;
}
-
case kFlagAfter:
{
- if (x < start_sample_index) {
- if (left + 1 >= mNumSyncSamples) {
- return ERROR_OUT_OF_RANGE;
- }
-
- x = mSyncSamples[left + 1];
-
- if (x < start_sample_index) {
- // The table of sync sample indices was not sorted
- // properly.
- return ERROR_MALFORMED;
- }
- }
-
+ // nothing to do
break;
}
-
default:
+ {
+ // this route is not used, but implement it nonetheless
+ CHECK(flags == kFlagClosest);
+
+ status_t err = mSampleIterator->seekTo(start_sample_index);
+ if (err != OK) {
+ return err;
+ }
+ uint32_t sample_time = mSampleIterator->getSampleTime();
+
+ err = mSampleIterator->seekTo(mSyncSamples[left]);
+ if (err != OK) {
+ return err;
+ }
+ uint32_t upper_time = mSampleIterator->getSampleTime();
+
+ err = mSampleIterator->seekTo(mSyncSamples[left - 1]);
+ if (err != OK) {
+ return err;
+ }
+ uint32_t lower_time = mSampleIterator->getSampleTime();
+
+ // use abs_difference for safety
+ if (abs_difference(upper_time, sample_time) >
+ abs_difference(sample_time, lower_time)) {
+ --left;
+ }
break;
+ }
}
- *sample_index = x;
-
+ *sample_index = mSyncSamples[left];
return OK;
}
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index dc42f91..d268aa4 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -127,6 +127,20 @@ const AMessage::Item *AMessage::findItem(
return NULL;
}
+bool AMessage::contains(const char *name) const {
+ name = AAtomizer::Atomize(name);
+
+ for (size_t i = 0; i < mNumItems; ++i) {
+ const Item *item = &mItems[i];
+
+ if (item->mName == name) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \
void AMessage::set##NAME(const char *name, TYPENAME value) { \
Item *item = allocateItem(name); \
@@ -160,6 +174,11 @@ void AMessage::setString(
item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
}
+void AMessage::setString(
+ const char *name, const AString &s) {
+ setString(name, s.c_str(), s.size());
+}
+
void AMessage::setObjectInternal(
const char *name, const sp<RefBase> &obj, Type type) {
Item *item = allocateItem(name);
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index fe146f2..d06df7b 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -75,7 +75,8 @@ public:
kFlagClosest
};
status_t findSampleAtTime(
- uint32_t req_time, uint32_t *sample_index, uint32_t flags);
+ uint64_t req_time, uint64_t scale_num, uint64_t scale_den,
+ uint32_t *sample_index, uint32_t flags);
status_t findSyncSampleNear(
uint32_t start_sample_index, uint32_t *sample_index,
@@ -138,6 +139,13 @@ private:
friend struct SampleIterator;
+ // normally we don't round
+ inline uint64_t getSampleTime(
+ size_t sample_index, uint64_t scale_num, uint64_t scale_den) const {
+ return (mSampleTimeEntries[sample_index].mCompositionTime
+ * scale_num) / scale_den;
+ }
+
status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
uint32_t getCompositionTimeOffset(uint32_t sampleIndex);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 871824a..a0319ab 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -230,6 +230,11 @@ void AnotherPacketSource::queueDiscontinuity(
int32_t oldDiscontinuityType;
if (!oldBuffer->meta()->findInt32(
"discontinuity", &oldDiscontinuityType)) {
+ MediaBuffer *mbuf = NULL;
+ oldBuffer->meta()->findPointer("mediaBuffer", (void**)&mbuf);
+ if (mbuf != NULL) {
+ mbuf->release();
+ }
it = mBuffers.erase(it);
continue;
}
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 22b12d9..cc4770a 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -233,7 +233,7 @@ status_t OMX::allocateNode(
instance, &handle);
if (err != OMX_ErrorNone) {
- ALOGV("FAILED to allocate omx component '%s'", name);
+ ALOGE("FAILED to allocate omx component '%s'", name);
instance->onGetHandleFailed();
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index eb3e6b4..af761e4 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1630,12 +1630,11 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
frameCount, mChannelMask);
// since client and server are in the same process,
// the buffer has the same virtual address on both sides
- mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
+ mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
+ true /*clientInServer*/);
mClientProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY);
mClientProxy->setSendLevel(0.0);
mClientProxy->setSampleRate(sampleRate);
- mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
- true /*clientInServer*/);
} else {
ALOGW("Error creating output track on thread %p", playbackThread);
}