diff options
39 files changed, 612 insertions, 307 deletions
diff --git a/media/img_utils/include/img_utils/DngUtils.h b/media/img_utils/include/img_utils/DngUtils.h index 4389b02..3dcedc5 100644 --- a/media/img_utils/include/img_utils/DngUtils.h +++ b/media/img_utils/include/img_utils/DngUtils.h @@ -31,6 +31,7 @@ namespace android { namespace img_utils { #define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0]))) +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) /** * Utility class for building values for the OpcodeList tags specified @@ -107,13 +108,49 @@ class ANDROID_API OpcodeListBuilder : public LightRefBase<OpcodeListBuilder> { uint32_t mapPlanes, const float* mapGains); + /** + * Add WarpRectilinear opcode for the given metadata parameters. + * + * Returns OK on success, or a negative error code. + */ + virtual status_t addWarpRectilinearForMetadata(const float* kCoeffs, + uint32_t activeArrayWidth, + uint32_t activeArrayHeight, + float opticalCenterX, + float opticalCenterY); + + /** + * Add a WarpRectilinear opcode. + * + * numPlanes - Number of planes included in this opcode. + * opticalCenterX, opticalCenterY - Normalized x,y coordinates of the sensor optical + * center relative to the top,left pixel of the produced images (e.g. [0.5, 0.5] + * gives a sensor optical center in the image center. + * kCoeffs - A list of coefficients for the polynomial equation representing the distortion + * correction. For each plane, 6 coefficients must be included: + * {k_r0, k_r1, k_r2, k_r3, k_t0, k_t1}. See the DNG 1.4 specification for an + * outline of the polynomial used here. + * + * Returns OK on success, or a negative error code. + */ + virtual status_t addWarpRectilinear(uint32_t numPlanes, + double opticalCenterX, + double opticalCenterY, + const double* kCoeffs); + // TODO: Add other Opcode methods protected: static const uint32_t FLAG_OPTIONAL = 0x1u; static const uint32_t FLAG_OPTIONAL_FOR_PREVIEW = 0x2u; + // Opcode IDs enum { + WARP_RECTILINEAR_ID = 1, GAIN_MAP_ID = 9, + }; + + // LSM mosaic indices + enum { LSM_R_IND = 0, LSM_GE_IND = 1, LSM_GO_IND = 2, diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp index d3b4a35..b213403 100644 --- a/media/img_utils/src/DngUtils.cpp +++ b/media/img_utils/src/DngUtils.cpp @@ -16,6 +16,10 @@ #include <img_utils/DngUtils.h> +#include <inttypes.h> + +#include <math.h> + namespace android { namespace img_utils { @@ -229,7 +233,7 @@ status_t OpcodeListBuilder::addGainMap(uint32_t top, err = mEndianOut.write(version, 0, NELEMS(version)); if (err != OK) return err; - // Do not include optional flag for preview, as this can have a large effect on the output. + // Allow this opcode to be skipped if not supported uint32_t flags = FLAG_OPTIONAL; err = mEndianOut.write(&flags, 0, 1); @@ -278,5 +282,96 @@ status_t OpcodeListBuilder::addGainMap(uint32_t top, return OK; } +status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs, + uint32_t activeArrayWidth, + uint32_t activeArrayHeight, + float opticalCenterX, + float opticalCenterY) { + if (activeArrayWidth <= 1 || activeArrayHeight <= 1) { + ALOGE("%s: Cannot add opcode for active array with dimensions w=%" PRIu32 ", h=%" PRIu32, + __FUNCTION__, activeArrayWidth, activeArrayHeight); + return BAD_VALUE; + } + + double normalizedOCX = opticalCenterX / static_cast<double>(activeArrayWidth - 1); + double normalizedOCY = opticalCenterY / static_cast<double>(activeArrayHeight - 1); + + normalizedOCX = CLAMP(normalizedOCX, 0, 1); + normalizedOCY = CLAMP(normalizedOCY, 0, 1); + + // Conversion factors from Camera2 K factors to DNG spec. K factors: + // + // Note: these are necessary because our unit system assumes a + // normalized max radius of sqrt(2), whereas the DNG spec's + // WarpRectilinear opcode assumes a normalized max radius of 1. + // Thus, each K coefficient must include the domain scaling + // factor (the DNG domain is scaled by sqrt(2) to emulate the + // domain used by the Camera2 specification). + + const double c_0 = sqrt(2); + const double c_1 = 2 * sqrt(2); + const double c_2 = 4 * sqrt(2); + const double c_3 = 8 * sqrt(2); + const double c_4 = 2; + const double c_5 = 2; + + const double coeffs[] = { c_0 * kCoeffs[0], + c_1 * kCoeffs[1], + c_2 * kCoeffs[2], + c_3 * kCoeffs[3], + c_4 * kCoeffs[4], + c_5 * kCoeffs[5] }; + + + return addWarpRectilinear(/*numPlanes*/1, + /*opticalCenterX*/normalizedOCX, + /*opticalCenterY*/normalizedOCY, + coeffs); +} + +status_t OpcodeListBuilder::addWarpRectilinear(uint32_t numPlanes, + double opticalCenterX, + double opticalCenterY, + const double* kCoeffs) { + + uint32_t opcodeId = WARP_RECTILINEAR_ID; + + status_t err = mEndianOut.write(&opcodeId, 0, 1); + if (err != OK) return err; + + uint8_t version[] = {1, 3, 0, 0}; + err = mEndianOut.write(version, 0, NELEMS(version)); + if (err != OK) return err; + + // Allow this opcode to be skipped if not supported + uint32_t flags = FLAG_OPTIONAL; + + err = mEndianOut.write(&flags, 0, 1); + if (err != OK) return err; + + const uint32_t NUMBER_CENTER_ARGS = 2; + const uint32_t NUMBER_COEFFS = numPlanes * 6; + uint32_t totalSize = (NUMBER_CENTER_ARGS + NUMBER_COEFFS) * sizeof(double) + sizeof(uint32_t); + + err = mEndianOut.write(&totalSize, 0, 1); + if (err != OK) return err; + + err = mEndianOut.write(&numPlanes, 0, 1); + if (err != OK) return err; + + err = mEndianOut.write(kCoeffs, 0, NUMBER_COEFFS); + if (err != OK) return err; + + err = mEndianOut.write(&opticalCenterX, 0, 1); + if (err != OK) return err; + + err = mEndianOut.write(&opticalCenterY, 0, 1); + if (err != OK) return err; + + mCount++; + + return OK; +} + } /*namespace img_utils*/ } /*namespace android*/ diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp index 09137ef..7e89d7f 100644 --- a/media/libmedia/IMediaHTTPConnection.cpp +++ b/media/libmedia/IMediaHTTPConnection.cpp @@ -70,7 +70,7 @@ struct BpMediaHTTPConnection : public BpInterface<IMediaHTTPConnection> { int32_t exceptionCode = reply.readExceptionCode(); if (exceptionCode) { - return UNKNOWN_ERROR; + return false; } sp<IBinder> binder = reply.readStrongBinder(); diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index ca33aed..e8d495b 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -70,12 +70,6 @@ static player_type getDefaultPlayerType() { return STAGEFRIGHT_PLAYER; } - // TODO: remove this EXPERIMENTAL developer settings property - if (property_get("persist.sys.media.use-awesome", value, NULL) - && !strcasecmp("true", value)) { - return STAGEFRIGHT_PLAYER; - } - return NU_PLAYER; } diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 9a37302..a5a1fa5 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -191,10 +191,13 @@ status_t MetadataRetrieverClient::setDataSource( return ret; } +Mutex MetadataRetrieverClient::sLock; + sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option) { ALOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option); Mutex::Autolock lock(mLock); + Mutex::Autolock glock(sLock); mThumbnail.clear(); if (mRetriever == NULL) { ALOGE("retriever is not initialized"); diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h index e71a29e..fe7547c 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.h +++ b/media/libmediaplayerservice/MetadataRetrieverClient.h @@ -63,6 +63,7 @@ private: virtual ~MetadataRetrieverClient(); mutable Mutex mLock; + static Mutex sLock; sp<MediaMetadataRetrieverBase> mRetriever; pid_t mPid; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 8760cbb..ef96a28 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -1729,13 +1729,15 @@ status_t NuPlayer::getCurrentPosition(int64_t *mediaUs) { return renderer->getCurrentPosition(mediaUs); } -void NuPlayer::getStats(int64_t *numFramesTotal, int64_t *numFramesDropped) { - sp<DecoderBase> decoder = getDecoder(false /* audio */); - if (decoder != NULL) { - decoder->getStats(numFramesTotal, numFramesDropped); - } else { - *numFramesTotal = 0; - *numFramesDropped = 0; +void NuPlayer::getStats(Vector<sp<AMessage> > *mTrackStats) { + CHECK(mTrackStats != NULL); + + mTrackStats->clear(); + if (mVideoDecoder != NULL) { + mTrackStats->push_back(mVideoDecoder->getStats()); + } + if (mAudioDecoder != NULL) { + mTrackStats->push_back(mAudioDecoder->getStats()); } } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index d7aa830..298ea6d 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -77,7 +77,7 @@ struct NuPlayer : public AHandler { status_t getSelectedTrack(int32_t type, Parcel* reply) const; status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); status_t getCurrentPosition(int64_t *mediaUs); - void getStats(int64_t *mNumFramesTotal, int64_t *mNumFramesDropped); + void getStats(Vector<sp<AMessage> > *mTrackStats); sp<MetaData> getFileMeta(); float getFrameRate(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index c649c62..99a2a84 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -58,7 +58,10 @@ NuPlayer::Decoder::Decoder( mCCDecoder(ccDecoder), mSkipRenderingUntilMediaTimeUs(-1ll), mNumFramesTotal(0ll), - mNumFramesDropped(0ll), + mNumInputFramesDropped(0ll), + mNumOutputFramesDropped(0ll), + mVideoWidth(0), + mVideoHeight(0), mIsAudio(true), mIsVideoAVC(false), mIsSecure(false), @@ -77,11 +80,11 @@ NuPlayer::Decoder::~Decoder() { releaseAndResetMediaBuffers(); } -void NuPlayer::Decoder::getStats( - int64_t *numFramesTotal, - int64_t *numFramesDropped) const { - *numFramesTotal = mNumFramesTotal; - *numFramesDropped = mNumFramesDropped; +sp<AMessage> NuPlayer::Decoder::getStats() const { + mStats->setInt64("frames-total", mNumFramesTotal); + mStats->setInt64("frames-dropped-input", mNumInputFramesDropped); + mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped); + return mStats; } void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { @@ -237,6 +240,18 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); + mStats->setString("mime", mime.c_str()); + mStats->setString("component-name", mComponentName.c_str()); + + if (!mIsAudio) { + int32_t width, height; + if (mOutputFormat->findInt32("width", &width) + && mOutputFormat->findInt32("height", &height)) { + mStats->setInt32("width", width); + mStats->setInt32("height", height); + } + } + sp<AMessage> reply = new AMessage(kWhatCodecNotify, this); mCodec->setCallback(reply); @@ -520,6 +535,8 @@ bool NuPlayer::Decoder::handleAnOutputBuffer( mSkipRenderingUntilMediaTimeUs = -1; } + mNumFramesTotal += !mIsAudio; + // wait until 1st frame comes out to signal resume complete notifyResumeCompleteIfNecessary(); @@ -536,6 +553,12 @@ bool NuPlayer::Decoder::handleAnOutputBuffer( void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) { if (!mIsAudio) { + int32_t width, height; + if (format->findInt32("width", &width) + && format->findInt32("height", &height)) { + mStats->setInt32("width", width); + mStats->setInt32("height", height); + } sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatVideoSizeChanged); notify->setMessage("format", format); @@ -654,10 +677,6 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) { return ERROR_END_OF_STREAM; } - if (!mIsAudio) { - ++mNumFramesTotal; - } - dropAccessUnit = false; if (!mIsAudio && !mIsSecure @@ -665,7 +684,7 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) { && mIsVideoAVC && !IsAVCReferenceFrame(accessUnit)) { dropAccessUnit = true; - ++mNumFramesDropped; + ++mNumInputFramesDropped; } } while (dropAccessUnit); @@ -833,6 +852,7 @@ void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) { CHECK(msg->findInt64("timestampNs", ×tampNs)); err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs); } else { + mNumOutputFramesDropped += !mIsAudio; err = mCodec->releaseOutputBuffer(bufferIx); } if (err != OK) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 070d51a..ceccb7a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -30,9 +30,7 @@ struct NuPlayer::Decoder : public DecoderBase { const sp<Surface> &surface = NULL, const sp<CCDecoder> &ccDecoder = NULL); - virtual void getStats( - int64_t *mNumFramesTotal, - int64_t *mNumFramesDropped) const; + virtual sp<AMessage> getStats() const; protected: virtual ~Decoder(); @@ -77,7 +75,10 @@ private: int64_t mSkipRenderingUntilMediaTimeUs; int64_t mNumFramesTotal; - int64_t mNumFramesDropped; + int64_t mNumInputFramesDropped; + int64_t mNumOutputFramesDropped; + int32_t mVideoWidth; + int32_t mVideoHeight; bool mIsAudio; bool mIsVideoAVC; bool mIsSecure; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp index 9d509bf..7e76842 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp @@ -31,6 +31,7 @@ namespace android { NuPlayer::DecoderBase::DecoderBase(const sp<AMessage> ¬ify) : mNotify(notify), mBufferGeneration(0), + mStats(new AMessage), mRequestInputBuffersPending(false) { // Every decoder has its own looper because MediaCodec operations // are blocking, but NuPlayer needs asynchronous operations. diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h index b52e7f7..8f030f0 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h @@ -42,9 +42,9 @@ struct NuPlayer::DecoderBase : public AHandler { void signalResume(bool notifyComplete); void initiateShutdown(); - virtual void getStats( - int64_t *mNumFramesTotal, - int64_t *mNumFramesDropped) const = 0; + virtual sp<AMessage> getStats() const { + return mStats; + } enum { kWhatInputDiscontinuity = 'inDi', @@ -76,6 +76,7 @@ protected: sp<AMessage> mNotify; int32_t mBufferGeneration; + sp<AMessage> mStats; private: enum { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp index d7b070e..30146c4 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp @@ -59,12 +59,6 @@ NuPlayer::DecoderPassThrough::DecoderPassThrough( NuPlayer::DecoderPassThrough::~DecoderPassThrough() { } -void NuPlayer::DecoderPassThrough::getStats( - int64_t *numFramesTotal, int64_t *numFramesDropped) const { - *numFramesTotal = 0; - *numFramesDropped = 0; -} - void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) { ALOGV("[%s] onConfigure", mComponentName.c_str()); mCachedBytes = 0; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h index 2f6df2c..db33e87 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h @@ -29,10 +29,6 @@ struct NuPlayer::DecoderPassThrough : public DecoderBase { const sp<Source> &source, const sp<Renderer> &renderer); - virtual void getStats( - int64_t *mNumFramesTotal, - int64_t *mNumFramesDropped) const; - protected: virtual ~DecoderPassThrough(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 84ae25e..3882dcd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "NuPlayerDriver" #include <inttypes.h> #include <utils/Log.h> +#include <cutils/properties.h> #include "NuPlayerDriver.h" @@ -241,7 +242,7 @@ status_t NuPlayerDriver::prepareAsync() { } status_t NuPlayerDriver::start() { - ALOGD("start(%p)", this); + ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS); Mutex::Autolock autoLock(mLock); switch (mState) { @@ -484,6 +485,13 @@ status_t NuPlayerDriver::reset() { notifyListener_l(MEDIA_STOPPED); } + char value[PROPERTY_VALUE_MAX]; + if (property_get("persist.debug.sf.stats", value, NULL) && + (!strcmp("1", value) || !strcasecmp("true", value))) { + Vector<String16> args; + dump(-1, args); + } + mState = STATE_RESET_IN_PROGRESS; mPlayer->resetAsync(); @@ -652,22 +660,59 @@ void NuPlayerDriver::notifySeekComplete_l() { status_t NuPlayerDriver::dump( int fd, const Vector<String16> & /* args */) const { - int64_t numFramesTotal; - int64_t numFramesDropped; - mPlayer->getStats(&numFramesTotal, &numFramesDropped); - FILE *out = fdopen(dup(fd), "w"); + Vector<sp<AMessage> > trackStats; + mPlayer->getStats(&trackStats); + + AString logString(" NuPlayer\n"); + char buf[256] = {0}; + + for (size_t i = 0; i < trackStats.size(); ++i) { + const sp<AMessage> &stats = trackStats.itemAt(i); + + AString mime; + if (stats->findString("mime", &mime)) { + snprintf(buf, sizeof(buf), " mime(%s)\n", mime.c_str()); + logString.append(buf); + } + + AString name; + if (stats->findString("component-name", &name)) { + snprintf(buf, sizeof(buf), " decoder(%s)\n", name.c_str()); + logString.append(buf); + } - fprintf(out, " NuPlayer\n"); - fprintf(out, " numFramesTotal(%" PRId64 "), numFramesDropped(%" PRId64 "), " - "percentageDropped(%.2f)\n", - numFramesTotal, - numFramesDropped, - numFramesTotal == 0 - ? 0.0 : (double)numFramesDropped / numFramesTotal); + if (mime.startsWith("video/")) { + int32_t width, height; + if (stats->findInt32("width", &width) + && stats->findInt32("height", &height)) { + snprintf(buf, sizeof(buf), " resolution(%d x %d)\n", width, height); + logString.append(buf); + } - fclose(out); - out = NULL; + int64_t numFramesTotal = 0; + int64_t numFramesDropped = 0; + + stats->findInt64("frames-total", &numFramesTotal); + stats->findInt64("frames-dropped-output", &numFramesDropped); + snprintf(buf, sizeof(buf), " numFramesTotal(%lld), numFramesDropped(%lld), " + "percentageDropped(%.2f%%)\n", + (long long)numFramesTotal, + (long long)numFramesDropped, + numFramesTotal == 0 + ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal); + logString.append(buf); + } + } + + ALOGI("%s", logString.c_str()); + + if (fd >= 0) { + FILE *out = fdopen(dup(fd), "w"); + fprintf(out, "%s", logString.c_str()); + fclose(out); + out = NULL; + } return OK; } @@ -680,6 +725,7 @@ void NuPlayerDriver::notifyListener( void NuPlayerDriver::notifyListener_l( int msg, int ext1, int ext2, const Parcel *in) { + ALOGD("notifyListener_l(%p), (%d, %d, %d)", this, msg, ext1, ext2); switch (msg) { case MEDIA_PLAYBACK_COMPLETE: { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index fb2e767..7e55aac 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -771,6 +771,33 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) { return sizeCopied; } +void NuPlayer::Renderer::drainAudioQueueUntilLastEOS() { + List<QueueEntry>::iterator it = mAudioQueue.begin(), itEOS = it; + bool foundEOS = false; + while (it != mAudioQueue.end()) { + int32_t eos; + QueueEntry *entry = &*it++; + if (entry->mBuffer == NULL + || (entry->mNotifyConsumed->findInt32("eos", &eos) && eos != 0)) { + itEOS = it; + foundEOS = true; + } + } + + if (foundEOS) { + // post all replies before EOS and drop the samples + for (it = mAudioQueue.begin(); it != itEOS; it++) { + if (it->mBuffer == NULL) { + // delay doesn't matter as we don't even have an AudioTrack + notifyEOS(true /* audio */, it->mFinalResult); + } else { + it->mNotifyConsumed->post(); + } + } + mAudioQueue.erase(mAudioQueue.begin(), itEOS); + } +} + bool NuPlayer::Renderer::onDrainAudioQueue() { // TODO: This call to getPosition checks if AudioTrack has been created // in AudioSink before draining audio. If AudioTrack doesn't exist, then @@ -784,6 +811,13 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { // "vorbis_dsp_synthesis returned -135", along with RTSP. uint32_t numFramesPlayed; if (mAudioSink->getPosition(&numFramesPlayed) != OK) { + // When getPosition fails, renderer will not reschedule the draining + // unless new samples are queued. + // If we have pending EOS (or "eos" marker for discontinuities), we need + // to post these now as NuPlayerDecoder might be waiting for it. + drainAudioQueueUntilLastEOS(); + + ALOGW("onDrainAudioQueue(): audio sink is not ready"); return false; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index c2fea40..3e65649 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -208,6 +208,7 @@ private: size_t fillAudioBuffer(void *buffer, size_t size); bool onDrainAudioQueue(); + void drainAudioQueueUntilLastEOS(); int64_t getPendingAudioPlayoutDurationUs(int64_t nowUs); int64_t getPlayedOutAudioDurationUs(int64_t nowUs); void postDrainAudioQueue_l(int64_t delayUs = 0); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index a57d4cf..527e9cd 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -110,6 +110,8 @@ static void InitOMXParams(T *params) { struct MessageList : public RefBase { MessageList() { } + virtual ~MessageList() { + } std::list<sp<AMessage> > &getList() { return mList; } private: std::list<sp<AMessage> > mList; @@ -126,15 +128,19 @@ struct CodecObserver : public BnOMXObserver { // from IOMXObserver virtual void onMessages(const std::list<omx_message> &messages) { - sp<AMessage> notify; + if (messages.empty()) { + return; + } + + sp<AMessage> notify = mNotify->dup(); bool first = true; sp<MessageList> msgList = new MessageList(); for (std::list<omx_message>::const_iterator it = messages.cbegin(); it != messages.cend(); ++it) { const omx_message &omx_msg = *it; if (first) { - notify = mNotify->dup(); notify->setInt32("node", omx_msg.node); + first = false; } sp<AMessage> msg = new AMessage; @@ -1390,7 +1396,8 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) { status_t err = OK; - for (size_t i = mBuffers[portIndex].size(); i-- > 0;) { + for (size_t i = mBuffers[portIndex].size(); i > 0;) { + i--; status_t err2 = freeBuffer(portIndex, i); if (err == OK) { err = err2; @@ -1404,7 +1411,8 @@ status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) { status_t ACodec::freeOutputBuffersNotOwnedByComponent() { status_t err = OK; - for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { + for (size_t i = mBuffers[kPortIndexOutput].size(); i > 0;) { + i--; BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); @@ -5196,16 +5204,11 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { int64_t timestampNs = 0; if (!msg->findInt64("timestampNs", ×tampNs)) { - // TODO: it seems like we should use the timestamp - // in the (media)buffer as it potentially came from - // an input surface, but we did not propagate it prior to - // API 20. Perhaps check for target SDK version. -#if 0 + // use media timestamp if client did not request a specific render timestamp if (info->mData->meta()->findInt64("timeUs", ×tampNs)) { - ALOGV("using buffer PTS of %" PRId64, timestampNs); + ALOGV("using buffer PTS of %lld", (long long)timestampNs); timestampNs *= 1000; } -#endif } status_t err; diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp index 41f0175..e17fdf8 100644 --- a/media/libstagefright/CallbackDataSource.cpp +++ b/media/libstagefright/CallbackDataSource.cpp @@ -109,9 +109,26 @@ ssize_t TinyCacheSource::readAt(off64_t offset, void* data, size_t size) { } // Check if the cache satisfies the read. - if (offset >= mCachedOffset && offset + size <= mCachedOffset + mCachedSize) { - memcpy(data, &mCache[offset - mCachedOffset], size); - return size; + if (mCachedOffset <= offset + && offset < (off64_t) (mCachedOffset + mCachedSize)) { + if (offset + size <= mCachedOffset + mCachedSize) { + memcpy(data, &mCache[offset - mCachedOffset], size); + return size; + } else { + // If the cache hits only partially, flush the cache and read the + // remainder. + + // This value is guaranteed to be greater than 0 because of the + // enclosing if statement. + const ssize_t remaining = mCachedOffset + mCachedSize - offset; + memcpy(data, &mCache[offset - mCachedOffset], remaining); + const ssize_t readMore = readAt(offset + remaining, + (uint8_t*)data + remaining, size - remaining); + if (readMore < 0) { + return readMore; + } + return remaining + readMore; + } } // Fill the cache and copy to the caller. diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 62612c7..8bf47b1 100644..100755 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1665,7 +1665,18 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return err; } } - + if (mPath.size() >= 2 + && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'v')) { + // Check if the video is MPEG2 + ESDS esds(&buffer[4], chunk_data_size - 4); + + uint8_t objectTypeIndication; + if (esds.getObjectTypeIndication(&objectTypeIndication) == OK) { + if (objectTypeIndication >= 0x60 && objectTypeIndication <= 0x65) { + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2); + } + } + } break; } @@ -2847,6 +2858,7 @@ status_t MPEG4Extractor::verifyTrack(Track *track) { return ERROR_MALFORMED; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) + || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { if (!track->meta->findData(kKeyESDS, &type, &data, &size) || type != kTypeESDS) { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 7ee84a8..e5b7202 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -2452,18 +2452,12 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) { info->mData->meta()->findInt64("timeUs", &mediaTimeUs); int64_t renderTimeNs = 0; - if (msg->findInt64("timestampNs", &renderTimeNs)) { - info->mNotify->setInt64("timestampNs", renderTimeNs); - } else { - // TODO: it seems like we should use the timestamp - // in the (media)buffer as it potentially came from - // an input surface, but we did not propagate it prior to - // API 20. Perhaps check for target SDK version. -#if 0 - ALOGV("using buffer PTS of %" PRId64, timestampNs); + if (!msg->findInt64("timestampNs", &renderTimeNs)) { + // use media timestamp if client did not request a specific render timestamp + ALOGV("using buffer PTS of %lld", (long long)mediaTimeUs); renderTimeNs = mediaTimeUs * 1000; -#endif } + info->mNotify->setInt64("timestampNs", renderTimeNs); if (mSoftRenderer != NULL) { std::list<FrameRenderTracker::Info> doneFrames = mSoftRenderer->render( diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index d48ede9..a47bfc7 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -79,6 +79,8 @@ sp<IMediaCodecList> MediaCodecList::sCodecList; // static void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) { ALOGV("Enter profilerThreadWrapper."); + remove(kProfilingResults); // remove previous result so that it won't be loaded to + // the new MediaCodecList MediaCodecList *codecList = new MediaCodecList(); if (codecList->initCheck() != OK) { ALOGW("Failed to create a new MediaCodecList, skipping codec profiling."); @@ -218,7 +220,8 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_er } } - for (size_t i = mCodecInfos.size(); i-- > 0;) { + for (size_t i = mCodecInfos.size(); i > 0;) { + i--; const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get(); if (info.mCaps.size() == 0) { // No types supported by this component??? diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index e9566f2..7c554db 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -20,22 +20,35 @@ #include <inttypes.h> #include <utils/Log.h> +#include <gui/Surface.h> #include "include/StagefrightMetadataRetriever.h" +#include <media/ICrypto.h> #include <media/IMediaHTTPService.h> + +#include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/ColorConverter.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/FileSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaCodec.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> -#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/Utils.h> + #include <CharacterEncodingDetector.h> namespace android { +static const int64_t kBufferTimeOutUs = 30000ll; // 30 msec +static const size_t kRetryCount = 20; // must be >0 + StagefrightMetadataRetriever::StagefrightMetadataRetriever() : mParsedMetaData(false), mAlbumArt(NULL) { @@ -123,73 +136,52 @@ status_t StagefrightMetadataRetriever::setDataSource( return OK; } -static bool isYUV420PlanarSupported( - OMXClient *client, - const sp<MetaData> &trackMeta) { - - const char *mime; - CHECK(trackMeta->findCString(kKeyMIMEType, &mime)); - - Vector<CodecCapabilities> caps; - if (QueryCodecs(client->interface(), mime, - true, /* queryDecoders */ - true, /* hwCodecOnly */ - &caps) == OK) { - - for (size_t j = 0; j < caps.size(); ++j) { - CodecCapabilities cap = caps[j]; - for (size_t i = 0; i < cap.mColorFormats.size(); ++i) { - if (cap.mColorFormats[i] == OMX_COLOR_FormatYUV420Planar) { - return true; - } - } - } - } - return false; -} - -static VideoFrame *extractVideoFrameWithCodecFlags( - OMXClient *client, +static VideoFrame *extractVideoFrame( + const char *componentName, const sp<MetaData> &trackMeta, const sp<MediaSource> &source, - uint32_t flags, int64_t frameTimeUs, int seekMode) { sp<MetaData> format = source->getFormat(); - // XXX: - // Once all vendors support OMX_COLOR_FormatYUV420Planar, we can - // remove this check and always set the decoder output color format - if (isYUV420PlanarSupported(client, trackMeta)) { - format->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); - } + sp<AMessage> videoFormat; + convertMetaDataToMessage(trackMeta, &videoFormat); - sp<MediaSource> decoder = - OMXCodec::Create( - client->interface(), format, false, source, - NULL, flags | OMXCodec::kClientNeedsFramebuffer); + // TODO: Use Flexible color instead + videoFormat->setInt32("color-format", OMX_COLOR_FormatYUV420Planar); - if (decoder.get() == NULL) { - ALOGV("unable to instantiate video decoder."); + status_t err; + sp<ALooper> looper = new ALooper; + looper->start(); + sp<MediaCodec> decoder = MediaCodec::CreateByComponentName( + looper, componentName, &err); + if (decoder.get() == NULL || err != OK) { + ALOGW("Failed to instantiate decoder [%s]", componentName); return NULL; } - status_t err = decoder->start(); + err = decoder->configure(videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */); if (err != OK) { - ALOGW("OMXCodec::start returned error %d (0x%08x)\n", err, err); + ALOGW("configure returned error %d (%s)", err, asString(err)); + decoder->release(); return NULL; } - // Read one output buffer, ignore format change notifications - // and spurious empty buffers. + err = decoder->start(); + if (err != OK) { + ALOGW("start returned error %d (%s)", err, asString(err)); + decoder->release(); + return NULL; + } MediaSource::ReadOptions options; if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC || seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) { ALOGE("Unknown seek mode: %d", seekMode); + decoder->release(); return NULL; } @@ -208,64 +200,155 @@ static VideoFrame *extractVideoFrameWithCodecFlags( options.setSeekTo(frameTimeUs, mode); } - MediaBuffer *buffer = NULL; - do { - if (buffer != NULL) { - buffer->release(); - buffer = NULL; - } - err = decoder->read(&buffer, &options); - options.clearSeekTo(); - } while (err == INFO_FORMAT_CHANGED - || (buffer != NULL && buffer->range_length() == 0)); - + err = source->start(); if (err != OK) { - CHECK(buffer == NULL); + ALOGW("source failed to start: %d (%s)", err, asString(err)); + decoder->release(); + return NULL; + } - ALOGV("decoding frame failed."); - decoder->stop(); + Vector<sp<ABuffer> > inputBuffers; + err = decoder->getInputBuffers(&inputBuffers); + if (err != OK) { + ALOGW("failed to get input buffers: %d (%s)", err, asString(err)); + decoder->release(); + return NULL; + } + Vector<sp<ABuffer> > outputBuffers; + err = decoder->getOutputBuffers(&outputBuffers); + if (err != OK) { + ALOGW("failed to get output buffers: %d (%s)", err, asString(err)); + decoder->release(); return NULL; } - ALOGV("successfully decoded video frame."); + sp<AMessage> outputFormat = NULL; + bool haveMoreInputs = true; + size_t index, offset, size; + int64_t timeUs; + size_t retriesLeft = kRetryCount; + bool done = false; - int32_t unreadable; - if (buffer->meta_data()->findInt32(kKeyIsUnreadable, &unreadable) - && unreadable != 0) { - ALOGV("video frame is unreadable, decoder does not give us access " - "to the video data."); + do { + size_t inputIndex = -1; + int64_t ptsUs = 0ll; + uint32_t flags = 0; + sp<ABuffer> codecBuffer = NULL; + + while (haveMoreInputs) { + err = decoder->dequeueInputBuffer(&inputIndex, kBufferTimeOutUs); + if (err != OK) { + ALOGW("Timed out waiting for input"); + if (retriesLeft) { + err = OK; + } + break; + } + codecBuffer = inputBuffers[inputIndex]; - buffer->release(); - buffer = NULL; + MediaBuffer *mediaBuffer = NULL; - decoder->stop(); + err = source->read(&mediaBuffer, &options); + options.clearSeekTo(); + if (err != OK) { + ALOGW("Input Error or EOS"); + haveMoreInputs = false; + break; + } + + if (mediaBuffer->range_length() > codecBuffer->capacity()) { + ALOGE("buffer size (%zu) too large for codec input size (%zu)", + mediaBuffer->range_length(), codecBuffer->capacity()); + err = BAD_VALUE; + } else { + codecBuffer->setRange(0, mediaBuffer->range_length()); + + CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &ptsUs)); + memcpy(codecBuffer->data(), + (const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(), + mediaBuffer->range_length()); + } + + mediaBuffer->release(); + break; + } + + if (err == OK && inputIndex < inputBuffers.size()) { + ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x", + codecBuffer->size(), ptsUs, flags); + err = decoder->queueInputBuffer( + inputIndex, + codecBuffer->offset(), + codecBuffer->size(), + ptsUs, + flags); + + // we don't expect an output from codec config buffer + if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) { + continue; + } + } + + while (err == OK) { + // wait for a decoded buffer + err = decoder->dequeueOutputBuffer( + &index, + &offset, + &size, + &timeUs, + &flags, + kBufferTimeOutUs); + + if (err == INFO_FORMAT_CHANGED) { + ALOGV("Received format change"); + err = decoder->getOutputFormat(&outputFormat); + } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { + ALOGV("Output buffers changed"); + err = decoder->getOutputBuffers(&outputBuffers); + } else { + if (err == -EAGAIN /* INFO_TRY_AGAIN_LATER */ && --retriesLeft > 0) { + ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft); + err = OK; + } else if (err == OK) { + ALOGV("Received an output buffer"); + done = true; + } else { + ALOGW("Received error %d (%s) instead of output", err, asString(err)); + done = true; + } + break; + } + } + } while (err == OK && !done); + if (err != OK || size <= 0 || outputFormat == NULL) { + ALOGE("Failed to decode thumbnail frame"); + source->stop(); + decoder->stop(); + decoder->release(); return NULL; } - int64_t timeUs; - CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + ALOGV("successfully decoded video frame."); + sp<ABuffer> videoFrameBuffer = outputBuffers.itemAt(index); + if (thumbNailTime >= 0) { if (timeUs != thumbNailTime) { - const char *mime; - CHECK(trackMeta->findCString(kKeyMIMEType, &mime)); + AString mime; + CHECK(outputFormat->findString("mime", &mime)); - ALOGV("thumbNailTime = %" PRId64 " us, timeUs = %" PRId64 " us, mime = %s", - thumbNailTime, timeUs, mime); + ALOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s", + (long long)thumbNailTime, (long long)timeUs, mime.c_str()); } } - sp<MetaData> meta = decoder->getFormat(); - int32_t width, height; - CHECK(meta->findInt32(kKeyWidth, &width)); - CHECK(meta->findInt32(kKeyHeight, &height)); + CHECK(outputFormat->findInt32("width", &width)); + CHECK(outputFormat->findInt32("height", &height)); int32_t crop_left, crop_top, crop_right, crop_bottom; - if (!meta->findRect( - kKeyCropRect, - &crop_left, &crop_top, &crop_right, &crop_bottom)) { + if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) { crop_left = crop_top = 0; crop_right = width - 1; crop_bottom = height - 1; @@ -285,23 +368,21 @@ static VideoFrame *extractVideoFrameWithCodecFlags( frame->mData = new uint8_t[frame->mSize]; frame->mRotationAngle = rotationAngle; - int32_t displayWidth, displayHeight; - if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) { - frame->mDisplayWidth = displayWidth; - } - if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) { - frame->mDisplayHeight = displayHeight; + int32_t sarWidth, sarHeight; + if (trackMeta->findInt32(kKeySARWidth, &sarWidth) + && trackMeta->findInt32(kKeySARHeight, &sarHeight) + && sarHeight != 0) { + frame->mDisplayWidth = (frame->mDisplayWidth * sarWidth) / sarHeight; } int32_t srcFormat; - CHECK(meta->findInt32(kKeyColorFormat, &srcFormat)); + CHECK(outputFormat->findInt32("color-format", &srcFormat)); - ColorConverter converter( - (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565); + ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565); if (converter.isValid()) { err = converter.convert( - (const uint8_t *)buffer->data() + buffer->range_offset(), + (const uint8_t *)videoFrameBuffer->data(), width, height, crop_left, crop_top, crop_right, crop_bottom, frame->mData, @@ -309,17 +390,16 @@ static VideoFrame *extractVideoFrameWithCodecFlags( frame->mHeight, 0, 0, frame->mWidth - 1, frame->mHeight - 1); } else { - ALOGE("Unable to instantiate color conversion from format 0x%08x to " - "RGB565", - srcFormat); + ALOGE("Unable to convert from format 0x%08x to RGB565", srcFormat); err = ERROR_UNSUPPORTED; } - buffer->release(); - buffer = NULL; - + videoFrameBuffer.clear(); + source->stop(); + decoder->releaseOutputBuffer(index); decoder->stop(); + decoder->release(); if (err != OK) { ALOGE("Colorconverter failed to convert frame."); @@ -390,20 +470,29 @@ VideoFrame *StagefrightMetadataRetriever::getFrameAtTime( mAlbumArt = MediaAlbumArt::fromData(dataSize, data); } - VideoFrame *frame = - extractVideoFrameWithCodecFlags( - &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs, - timeUs, option); - - if (frame == NULL) { - ALOGV("Software decoder failed to extract thumbnail, " - "trying hardware decoder."); + const char *mime; + CHECK(trackMeta->findCString(kKeyMIMEType, &mime)); - frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0, - timeUs, option); + Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs; + OMXCodec::findMatchingCodecs( + mime, + false, /* encoder */ + NULL, /* matchComponentName */ + OMXCodec::kPreferSoftwareCodecs, + &matchingCodecs); + + for (size_t i = 0; i < matchingCodecs.size(); ++i) { + const char *componentName = matchingCodecs[i].mName.string(); + VideoFrame *frame = + extractVideoFrame(componentName, trackMeta, source, timeUs, option); + + if (frame != NULL) { + return frame; + } + ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName); } - return frame; + return NULL; } MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() { diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp index 8ac337a..f3af777 100755 --- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp @@ -787,7 +787,7 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { } if (s_dec_op.u4_output_present) { - outHeader->nFilledLen = (mWidth * mHeight * 3) / 2; + outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts]; mTimeStampsValid[s_dec_op.u4_ts] = false; diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index a35909e..e161fb8 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -146,8 +146,8 @@ bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *por } outHeader->nOffset = 0; - outHeader->nFilledLen = (width * height * 3) / 2; outHeader->nFlags = 0; + outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv; uint8_t *dst = outHeader->pBuffer; diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp index 7ff9ee7..cb10bce 100644 --- a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp +++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp @@ -34,6 +34,12 @@ namespace android { static const int kRate = 48000; +// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies +// mappings for up to 8 channels. This information is part of the Vorbis I +// Specification: +// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html +static const int kMaxChannels = 8; + template<class T> static void InitOMXParams(T *params) { params->nSize = sizeof(T); @@ -101,7 +107,7 @@ void SoftOpus::initPorts() { def.eDir = OMX_DirOutput; def.nBufferCountMin = kNumBuffers; def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t); + def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t) * kMaxChannels; def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainAudio; @@ -225,12 +231,6 @@ static uint16_t ReadLE16(const uint8_t *data, size_t data_size, return val; } -// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies -// mappings for up to 8 channels. This information is part of the Vorbis I -// Specification: -// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html -static const int kMaxChannels = 8; - // Maximum packet size used in Xiph's opusdec. static const int kMaxOpusOutputPacketSizeSamples = 960 * 6; diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp index 5f7c70d..b837f66 100644 --- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp +++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp @@ -109,7 +109,8 @@ void AHierarchicalStateMachine::changeState(const sp<AState> &state) { A.editItemAt(i)->stateExited(); } - for (size_t i = B.size(); i-- > 0;) { + for (size_t i = B.size(); i > 0;) { + i--; B.editItemAt(i)->stateEntered(); } } diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp index 473ce1b..9ed53e7 100644 --- a/media/libstagefright/foundation/ALooperRoster.cpp +++ b/media/libstagefright/foundation/ALooperRoster.cpp @@ -79,7 +79,8 @@ void ALooperRoster::unregisterStaleHandlers() { { Mutex::Autolock autoLock(mLock); - for (size_t i = mHandlers.size(); i-- > 0;) { + for (size_t i = mHandlers.size(); i > 0;) { + i--; const HandlerInfo &info = mHandlers.valueAt(i); sp<ALooper> looper = info.mLooper.promote(); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index db429f6..e3c3e80 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -694,7 +694,8 @@ status_t ATSParser::Stream::parse( status_t err = flush(event); if (err != OK) { - return err; + ALOGW("Error (%08x) happened while flushing; we simply discard " + "the PES packet and continue.", err); } } @@ -996,10 +997,6 @@ status_t ATSParser::Stream::parsePES(ABitReader *br, SyncEvent *event) { return ERROR_MALFORMED; } - if (br->numBitsLeft() < dataLength * 8) { - return ERROR_MALFORMED; - } - onPayloadData( PTS_DTS_flags, PTS, DTS, br->data(), dataLength, event); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 692667f..6ee1a77 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -1423,7 +1423,9 @@ void OMXNodeInstance::onMessages(std::list<omx_message> &messages) { } } - mObserver->onMessages(messages); + if (!messages.empty()) { + mObserver->onMessages(messages); + } } void OMXNodeInstance::onObserverDied(OMXMaster *master) { diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 78dfbb1..b6de0d9 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -11,8 +11,7 @@ endif include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - main_mediaserver.cpp \ - icuutils.cpp + main_mediaserver.cpp LOCAL_SHARED_LIBRARIES := \ libaudioflinger \ @@ -33,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \ libradioservice LOCAL_STATIC_LIBRARIES := \ + libicuandroid_utils \ libregistermsext LOCAL_C_INCLUDES := \ diff --git a/media/mediaserver/IcuUtils.h b/media/mediaserver/IcuUtils.h deleted file mode 100644 index 52fab6d..0000000 --- a/media/mediaserver/IcuUtils.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2015 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 ICU_UTILS_H -#define ICU_UTILS_H - -// Initializes ICU or dies trying. This must be called when the process -// is single threaded. -void initializeIcuOrDie(); - -#endif // ICU_UTILS_H - diff --git a/media/mediaserver/icuutils.cpp b/media/mediaserver/icuutils.cpp deleted file mode 100644 index 4015849..0000000 --- a/media/mediaserver/icuutils.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 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 "IcuUtils.h" - -#include "unicode/putil.h" -#include "unicode/uclean.h" -#include "unicode/utypes.h" -#include "utils/Log.h" - -#include <stdlib.h> - -void initializeIcuOrDie() { - const char* systemPathPrefix = getenv("ANDROID_ROOT"); - LOG_ALWAYS_FATAL_IF(systemPathPrefix == NULL, "ANDROID_ROOT environment variable not set"); - - char buf[256]; - const int num_written = snprintf(buf, sizeof(buf), "%s/usr/icu/", systemPathPrefix); - LOG_ALWAYS_FATAL_IF((num_written < 0 || static_cast<size_t>(num_written) >= sizeof(buf)), - "Unable to construct ICU path."); - - u_setDataDirectory(buf); - UErrorCode status = U_ZERO_ERROR; - - // u_setDataDirectory doesn't try doing anything with the directory we gave - // it, so we'll have to call u_init to make sure it was successful. - u_init(&status); - LOG_ALWAYS_FATAL_IF(!U_SUCCESS(status), "Failed to initialize ICU %s", u_errorName(status)); -} diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index 27a40b2..4a485ed 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -125,7 +125,7 @@ int main(int argc __unused, char** argv) prctl(PR_SET_PDEATHSIG, SIGKILL); // if parent media.log dies before me, kill me also setpgid(0, 0); // but if I die first, don't kill my parent } - initializeIcuOrDie(); + InitializeIcuOrDie(); sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm = defaultServiceManager(); ALOGI("ServiceManager: %p", sm.get()); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index ad445a5..489f2d4 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4459,6 +4459,17 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr } } +void AudioFlinger::DirectOutputThread::onAddNewTrack_l() +{ + sp<Track> previousTrack = mPreviousTrack.promote(); + sp<Track> latestTrack = mLatestActiveTrack.promote(); + + if (previousTrack != 0 && latestTrack != 0 && + (previousTrack->sessionId() != latestTrack->sessionId())) { + mFlushPending = true; + } + PlaybackThread::onAddNewTrack_l(); +} AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l( Vector< sp<Track> > *tracksToRemove @@ -4468,7 +4479,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep mixer_state mixerStatus = MIXER_IDLE; bool doHwPause = false; bool doHwResume = false; - bool flushPending = false; // find out which tracks need to be processed for (size_t i = 0; i < count; i++) { @@ -4478,6 +4488,12 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep continue; } + if (t->isInvalid()) { + ALOGW("An invalidated track shouldn't be in active list"); + tracksToRemove->add(t); + continue; + } + Track* const track = t.get(); audio_track_cblk_t* cblk = track->cblk(); // Only consider last track started for volume and mixer state control. @@ -4497,7 +4513,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep } else if (track->isFlushPending()) { track->flushAck(); if (last) { - flushPending = true; + mFlushPending = true; } } else if (track->isResumePending()) { track->resumeAck(); @@ -4538,6 +4554,21 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep // compute volume for this track processVolume_l(track, last); if (last) { + sp<Track> previousTrack = mPreviousTrack.promote(); + if (previousTrack != 0) { + if (track != previousTrack.get()) { + // Flush any data still being written from last track + mBytesRemaining = 0; + // flush data already sent if changing audio session as audio + // comes from a different source. Also invalidate previous track to force a + // seek when resuming. + if (previousTrack->sessionId() != track->sessionId()) { + previousTrack->invalidate(); + } + } + } + mPreviousTrack = track; + // reset retry count track->mRetryCount = kMaxTrackRetriesDirect; mActiveTrack = t; @@ -4604,11 +4635,11 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep } // if an active track did not command a flush, check for pending flush on stopped tracks - if (!flushPending) { + if (!mFlushPending) { for (size_t i = 0; i < mTracks.size(); i++) { if (mTracks[i]->isFlushPending()) { mTracks[i]->flushAck(); - flushPending = true; + mFlushPending = true; } } } @@ -4618,10 +4649,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep // before flush and then resume HW. This can happen in case of pause/flush/resume // if resume is received before pause is executed. if (mHwSupportsPause && !mStandby && - (doHwPause || (flushPending && !mHwPaused && (count != 0)))) { + (doHwPause || (mFlushPending && !mHwPaused && (count != 0)))) { mOutput->stream->pause(mOutput->stream); } - if (flushPending) { + if (mFlushPending) { flushHw_l(); } if (mHwSupportsPause && !mStandby && doHwResume) { @@ -4680,14 +4711,13 @@ void AudioFlinger::DirectOutputThread::threadLoop_exit() { { Mutex::Autolock _l(mLock); - bool flushPending = false; for (size_t i = 0; i < mTracks.size(); i++) { if (mTracks[i]->isFlushPending()) { mTracks[i]->flushAck(); - flushPending = true; + mFlushPending = true; } } - if (flushPending) { + if (mFlushPending) { flushHw_l(); } } @@ -4825,6 +4855,7 @@ void AudioFlinger::DirectOutputThread::flushHw_l() { mOutput->flush(); mHwPaused = false; + mFlushPending = false; } // ---------------------------------------------------------------------------- @@ -5146,7 +5177,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr } if (mFlushPending) { flushHw_l(); - mFlushPending = false; } if (!mStandby && doHwResume) { mOutput->stream->resume(mOutput->stream); @@ -5194,18 +5224,6 @@ void AudioFlinger::OffloadThread::flushHw_l() } } -void AudioFlinger::OffloadThread::onAddNewTrack_l() -{ - sp<Track> previousTrack = mPreviousTrack.promote(); - sp<Track> latestTrack = mLatestActiveTrack.promote(); - - if (previousTrack != 0 && latestTrack != 0 && - (previousTrack->sessionId() != latestTrack->sessionId())) { - mFlushPending = true; - } - PlaybackThread::onAddNewTrack_l(); -} - // ---------------------------------------------------------------------------- AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 7b4fb14..4ebe615 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -941,6 +941,8 @@ protected: virtual void threadLoop_exit(); virtual bool shouldStandby_l(); + virtual void onAddNewTrack_l(); + // volumes last sent to audio HAL with stream->set_volume() float mLeftVolFloat; float mRightVolFloat; @@ -952,6 +954,9 @@ protected: // prepareTracks_l() tells threadLoop_mix() the name of the single active track sp<Track> mActiveTrack; + + wp<Track> mPreviousTrack; // used to detect track switch + public: virtual bool hasFastMixer() const { return false; } }; @@ -971,12 +976,10 @@ protected: virtual bool waitingAsyncCallback(); virtual bool waitingAsyncCallback_l(); - virtual void onAddNewTrack_l(); private: size_t mPausedWriteLength; // length in bytes of write interrupted by pause size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume - wp<Track> mPreviousTrack; // used to detect track switch }; class AsyncCallbackThread : public Thread { diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 1b03060..f7da209 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1951,7 +1951,9 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( ((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE), type), mOverflow(false), - mFramesToDrop(0) + mFramesToDrop(0), + mResamplerBufferProvider(NULL), // initialize in case of early constructor exit + mRecordBufferConverter(NULL) { if (mCblk == NULL) { return; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index bded309..deebc23 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -789,6 +789,11 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( // only allow deep buffering for music stream type if (stream != AUDIO_STREAM_MUSIC) { flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER); + } else if (/* stream == AUDIO_STREAM_MUSIC && */ + flags == AUDIO_OUTPUT_FLAG_NONE && + property_get_bool("audio.deep_buffer.media", false /* default_value */)) { + // use DEEP_BUFFER as default output for music stream type + flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_DEEP_BUFFER; } if (stream == AUDIO_STREAM_TTS) { flags = AUDIO_OUTPUT_FLAG_TTS; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index 3bda70c..0f485ca 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -27,8 +27,7 @@ namespace android { -struct CameraDeviceClientBase : - public CameraService::BasicClient, public BnCameraDeviceUser +struct CameraDeviceClientBase : public CameraService::BasicClient, public BnCameraDeviceUser { typedef ICameraDeviceCallbacks TCamCallbacks; @@ -167,7 +166,7 @@ private: // Find the closest dimensions for a given format in available stream configurations with // a width <= ROUNDING_WIDTH_CAP - static const int32_t ROUNDING_WIDTH_CAP = 1080; + static const int32_t ROUNDING_WIDTH_CAP = 1920; static bool roundBufferDimensionNearest(int32_t width, int32_t height, int32_t format, android_dataspace dataSpace, const CameraMetadata& info, /*out*/int32_t* outWidth, /*out*/int32_t* outHeight); |