diff options
Diffstat (limited to 'media')
54 files changed, 756 insertions, 262 deletions
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk index 5d0a87c..2bb6dbe 100644 --- a/media/libeffects/downmix/Android.mk +++ b/media/libeffects/downmix/Android.mk @@ -13,7 +13,7 @@ LOCAL_MODULE:= libdownmix LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) LOCAL_LDLIBS += -ldl diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c index f8d6041..6d30d64 100644 --- a/media/libeffects/factory/EffectsFactory.c +++ b/media/libeffects/factory/EffectsFactory.c @@ -368,27 +368,21 @@ int EffectRelease(effect_handle_t handle) } if (e1 == NULL) { ret = -ENOENT; - pthread_mutex_unlock(&gLibLock); goto exit; } // release effect in library if (fx->lib == NULL) { ALOGW("EffectRelease() fx %p library already unloaded", handle); - pthread_mutex_unlock(&gLibLock); } else { pthread_mutex_lock(&fx->lib->lock); - // Releasing the gLibLock here as the list access is over as the - // effect is removed from the list. - // If the gLibLock is not released, we will have a deadlock situation - // since we call the sub effect release inside the EffectRelease of Proxy - pthread_mutex_unlock(&gLibLock); fx->lib->desc->release_effect(fx->subItfe); pthread_mutex_unlock(&fx->lib->lock); } free(fx); exit: + pthread_mutex_unlock(&gLibLock); return ret; } @@ -404,8 +398,8 @@ int EffectIsNullUuid(const effect_uuid_t *uuid) // is pointed by the first argument. It searches the gSubEffectList for the // matching uuid and then copies the corresponding sub effect descriptors // to the inout param -int EffectGetSubEffects(const effect_uuid_t *uuid, - effect_descriptor_t *pDescriptors, size_t size) +int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube, + size_t size) { ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X" "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, @@ -413,8 +407,7 @@ int EffectGetSubEffects(const effect_uuid_t *uuid, uuid->node[3],uuid->node[4],uuid->node[5]); // Check if the size of the desc buffer is large enough for 2 subeffects - if ((uuid == NULL) || (pDescriptors == NULL) || - (size < 2*sizeof(effect_descriptor_t))) { + if ((uuid == NULL) || (pSube == NULL) || (size < 2)) { ALOGW("NULL pointer or insufficient memory. Cannot query subeffects"); return -EINVAL; } @@ -432,11 +425,10 @@ int EffectGetSubEffects(const effect_uuid_t *uuid, list_elem_t *subefx = e->sub_elem; while (subefx != NULL) { subeffect = (sub_effect_entry_t*)subefx->object; - d = (effect_descriptor_t*)(subeffect->object); - pDescriptors[count++] = *d; + pSube[count++] = subeffect; subefx = subefx->next; } - ALOGV("EffectGetSubEffects end - copied the sub effect descriptors"); + ALOGV("EffectGetSubEffects end - copied the sub effect structures"); return count; } e = e->next; diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h index 147ff18..560b485 100644 --- a/media/libeffects/factory/EffectsFactory.h +++ b/media/libeffects/factory/EffectsFactory.h @@ -20,7 +20,7 @@ #include <cutils/log.h> #include <pthread.h> #include <dirent.h> -#include <media/EffectsFactoryApi.h> +#include <hardware/audio_effect.h> #if __cplusplus extern "C" { @@ -66,6 +66,32 @@ typedef struct sub_effect_entry_s { void *object; } sub_effect_entry_t; + +//////////////////////////////////////////////////////////////////////////////// +// +// Function: EffectGetSubEffects +// +// Description: Returns the descriptors of the sub effects of the effect +// whose uuid is pointed to by first argument. +// +// Input: +// pEffectUuid: pointer to the effect uuid. +// size: max number of sub_effect_entry_t * in pSube. +// +// Input/Output: +// pSube: address where to return the sub effect structures. +// Output: +// returned value: 0 successful operation. +// -ENODEV factory failed to initialize +// -EINVAL invalid pEffectUuid or pDescriptor +// -ENOENT no effect with this uuid found +// *pDescriptor: updated with the sub effect descriptors. +// +//////////////////////////////////////////////////////////////////////////////// +int EffectGetSubEffects(const effect_uuid_t *pEffectUuid, + sub_effect_entry_t **pSube, + size_t size); + #if __cplusplus } // extern "C" #endif diff --git a/media/libeffects/loudness/Android.mk b/media/libeffects/loudness/Android.mk index dcb7b27..edf964e 100644 --- a/media/libeffects/loudness/Android.mk +++ b/media/libeffects/loudness/Android.mk @@ -14,7 +14,7 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libstlport -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_MODULE:= libldnhncr LOCAL_C_INCLUDES := \ diff --git a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp index 91ed677..3c2b320 100644 --- a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp +++ b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp @@ -453,13 +453,13 @@ const struct effect_interface_s gLEInterface = { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { - tag : AUDIO_EFFECT_LIBRARY_TAG, - version : EFFECT_LIBRARY_API_VERSION, - name : "Loudness Enhancer Library", - implementor : "The Android Open Source Project", - create_effect : LELib_Create, - release_effect : LELib_Release, - get_descriptor : LELib_GetDescriptor, + .tag = AUDIO_EFFECT_LIBRARY_TAG, + .version = EFFECT_LIBRARY_API_VERSION, + .name = "Loudness Enhancer Library", + .implementor = "The Android Open Source Project", + .create_effect = LELib_Create, + .release_effect = LELib_Release, + .get_descriptor = LELib_GetDescriptor, }; }; // extern "C" diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk index f1af389..68ba34c 100644 --- a/media/libeffects/lvm/wrapper/Android.mk +++ b/media/libeffects/lvm/wrapper/Android.mk @@ -13,7 +13,7 @@ LOCAL_CFLAGS += -fvisibility=hidden LOCAL_MODULE:= libbundlewrapper -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_STATIC_LIBRARIES += libmusicbundle @@ -42,7 +42,7 @@ LOCAL_CFLAGS += -fvisibility=hidden LOCAL_MODULE:= libreverbwrapper -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_STATIC_LIBRARIES += libreverb diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk index c344352..9e8cb83 100644 --- a/media/libeffects/preprocessing/Android.mk +++ b/media/libeffects/preprocessing/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE:= libaudiopreprocessing LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_SRC_FILES:= \ PreProcessing.cpp diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk index 01b3be1..b438796 100644 --- a/media/libeffects/proxy/Android.mk +++ b/media/libeffects/proxy/Android.mk @@ -15,7 +15,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= libeffectproxy -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_MODULE_TAGS := optional @@ -28,7 +28,8 @@ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libdl libeffects LOCAL_C_INCLUDES := \ system/media/audio_effects/include \ - bionic/libc/include + bionic/libc/include \ + frameworks/av/media/libeffects/factory include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp index dd4ad08..62d3fd3 100644 --- a/media/libeffects/proxy/EffectProxy.cpp +++ b/media/libeffects/proxy/EffectProxy.cpp @@ -56,6 +56,8 @@ int EffectProxyCreate(const effect_uuid_t *uuid, effect_handle_t *pHandle) { effect_descriptor_t* desc; + audio_effect_library_t** aeli; + sub_effect_entry_t** sube; EffectContext* pContext; if (pHandle == NULL || uuid == NULL) { ALOGE("EffectProxyCreate() called with NULL pointer"); @@ -74,31 +76,52 @@ int EffectProxyCreate(const effect_uuid_t *uuid, // Get the HW and SW sub effect descriptors from the effects factory desc = new effect_descriptor_t[SUB_FX_COUNT]; + aeli = new audio_effect_library_t*[SUB_FX_COUNT]; + sube = new sub_effect_entry_t*[SUB_FX_COUNT]; + pContext->sube = new sub_effect_entry_t*[SUB_FX_COUNT]; pContext->desc = new effect_descriptor_t[SUB_FX_COUNT]; - int retValue = EffectGetSubEffects(uuid, desc, - sizeof(effect_descriptor_t) * SUB_FX_COUNT); + pContext->aeli = new audio_effect_library_t*[SUB_FX_COUNT]; + int retValue = EffectGetSubEffects(uuid, sube, SUB_FX_COUNT); // EffectGetSubEffects returns the number of sub-effects copied. if (retValue != SUB_FX_COUNT) { ALOGE("EffectCreate() could not get the sub effects"); - delete desc; - delete pContext->desc; + delete[] sube; + delete[] desc; + delete[] aeli; + delete[] pContext->sube; + delete[] pContext->desc; + delete[] pContext->aeli; return -EINVAL; } // Check which is the HW descriptor and copy the descriptors // to the Context desc array // Also check if there is only one HW and one SW descriptor. // HW descriptor alone has the HW_TUNNEL flag. + desc[0] = *(effect_descriptor_t*)(sube[0])->object; + desc[1] = *(effect_descriptor_t*)(sube[1])->object; + aeli[0] = sube[0]->lib->desc; + aeli[1] = sube[1]->lib->desc; if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && !(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { + pContext->sube[SUB_FX_OFFLOAD] = sube[0]; pContext->desc[SUB_FX_OFFLOAD] = desc[0]; + pContext->aeli[SUB_FX_OFFLOAD] = aeli[0]; + pContext->sube[SUB_FX_HOST] = sube[1]; pContext->desc[SUB_FX_HOST] = desc[1]; + pContext->aeli[SUB_FX_HOST] = aeli[1]; } else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) && !(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) { + pContext->sube[SUB_FX_HOST] = sube[0]; pContext->desc[SUB_FX_HOST] = desc[0]; + pContext->aeli[SUB_FX_HOST] = aeli[0]; + pContext->sube[SUB_FX_OFFLOAD] = sube[1]; pContext->desc[SUB_FX_OFFLOAD] = desc[1]; + pContext->aeli[SUB_FX_OFFLOAD] = aeli[1]; } - delete desc; + delete[] desc; + delete[] aeli; + delete[] sube; #if (LOG_NDEBUG == 0) effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid; ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X" @@ -128,13 +151,15 @@ int EffectProxyRelease(effect_handle_t handle) { return -EINVAL; } ALOGV("EffectRelease"); - delete pContext->desc; + delete[] pContext->desc; free(pContext->replyData); if (pContext->eHandle[SUB_FX_HOST]) - EffectRelease(pContext->eHandle[SUB_FX_HOST]); + pContext->aeli[SUB_FX_HOST]->release_effect(pContext->eHandle[SUB_FX_HOST]); if (pContext->eHandle[SUB_FX_OFFLOAD]) - EffectRelease(pContext->eHandle[SUB_FX_OFFLOAD]); + pContext->aeli[SUB_FX_OFFLOAD]->release_effect(pContext->eHandle[SUB_FX_OFFLOAD]); + delete[] pContext->aeli; + delete[] pContext->sube; delete pContext; pContext = NULL; return 0; @@ -187,7 +212,8 @@ int Effect_command(effect_handle_t self, } if (pContext->eHandle[SUB_FX_HOST] == NULL) { ALOGV("Effect_command() Calling HOST EffectCreate"); - status = EffectCreate(&pContext->desc[SUB_FX_HOST].uuid, + status = pContext->aeli[SUB_FX_HOST]->create_effect( + &pContext->desc[SUB_FX_HOST].uuid, pContext->sessionId, pContext->ioId, &(pContext->eHandle[SUB_FX_HOST])); if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) { @@ -197,11 +223,13 @@ int Effect_command(effect_handle_t self, } if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) { ALOGV("Effect_command() Calling OFFLOAD EffectCreate"); - status = EffectCreate(&pContext->desc[SUB_FX_OFFLOAD].uuid, + status = pContext->aeli[SUB_FX_OFFLOAD]->create_effect( + &pContext->desc[SUB_FX_OFFLOAD].uuid, pContext->sessionId, pContext->ioId, &(pContext->eHandle[SUB_FX_OFFLOAD])); if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) { ALOGV("Effect_command() Error creating HW effect"); + pContext->eHandle[SUB_FX_OFFLOAD] = NULL; // Do not return error here as SW effect is created // Return error if the CMD_OFFLOAD sends the index as OFFLOAD } @@ -233,11 +261,17 @@ int Effect_command(effect_handle_t self, // Update the DSP wrapper with the new ioHandle. // Pass the OFFLOAD command to the wrapper. // The DSP wrapper needs to handle this CMD - if (pContext->eHandle[SUB_FX_OFFLOAD]) - status = (*pContext->eHandle[SUB_FX_OFFLOAD])->command( - pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, - pCmdData, replySize, pReplyData); - return status; + if (pContext->eHandle[SUB_FX_OFFLOAD]) { + ALOGV("Effect_command: Calling OFFLOAD command"); + return (*pContext->eHandle[SUB_FX_OFFLOAD])->command( + pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + } + *(int*)pReplyData = NO_ERROR; + ALOGV("Effect_command OFFLOAD return 0, replyData %d", + *(int*)pReplyData); + + return NO_ERROR; } int index = pContext->index; @@ -329,11 +363,11 @@ int Effect_getDescriptor(effect_handle_t self, __attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { - tag : AUDIO_EFFECT_LIBRARY_TAG, - version : EFFECT_LIBRARY_API_VERSION, - name : "Effect Proxy", - implementor : "AOSP", - create_effect : android::EffectProxyCreate, - release_effect : android::EffectProxyRelease, - get_descriptor : android::EffectProxyGetDescriptor, + .tag = AUDIO_EFFECT_LIBRARY_TAG, + .version = EFFECT_LIBRARY_API_VERSION, + .name = "Effect Proxy", + .implementor = "AOSP", + .create_effect = android::EffectProxyCreate, + .release_effect = android::EffectProxyRelease, + .get_descriptor = android::EffectProxyGetDescriptor, }; diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h index acbe17e..046b93e 100644 --- a/media/libeffects/proxy/EffectProxy.h +++ b/media/libeffects/proxy/EffectProxy.h @@ -16,6 +16,8 @@ #include <hardware/audio.h> #include <hardware/audio_effect.h> +#include "EffectsFactory.h" + namespace android { enum { SUB_FX_HOST, // Index of HOST in the descriptor and handle arrays @@ -62,7 +64,9 @@ const struct effect_interface_s gEffectInterface = { struct EffectContext { const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy + sub_effect_entry_t** sube; // Points to the sub effects effect_descriptor_t* desc; // Points to the sub effect descriptors + audio_effect_library_t** aeli; // Points to the sub effect aeli effect_handle_t eHandle[SUB_FX_COUNT]; // The effect handles of the sub effects int index; // The index that is currently active - HOST or OFFLOAD int32_t sessionId; // The sessiond in which the effect is created. diff --git a/media/libeffects/testlibs/Android.mk_ b/media/libeffects/testlibs/Android.mk_ index 2954908..672ebba 100644 --- a/media/libeffects/testlibs/Android.mk_ +++ b/media/libeffects/testlibs/Android.mk_ @@ -11,7 +11,7 @@ LOCAL_CFLAGS+= -O2 LOCAL_SHARED_LIBRARIES := \ libcutils -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_MODULE:= libreverbtest ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) @@ -47,7 +47,7 @@ LOCAL_CFLAGS+= -O2 LOCAL_SHARED_LIBRARIES := \ libcutils -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_MODULE:= libequalizertest ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk index e196eb2..dd2d306 100644 --- a/media/libeffects/visualizer/Android.mk +++ b/media/libeffects/visualizer/Android.mk @@ -13,7 +13,7 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libdl -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_MODULE:= libvisualizer LOCAL_C_INCLUDES := \ diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index fe5cd9e..0609a22 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -101,7 +101,8 @@ AudioTrack::AudioTrack( int notificationFrames, int sessionId, transfer_type transferType, - const audio_offload_info_t *offloadInfo) + const audio_offload_info_t *offloadInfo, + int uid) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), @@ -109,7 +110,8 @@ AudioTrack::AudioTrack( { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, - 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo); + 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType, + offloadInfo, uid); } AudioTrack::AudioTrack( @@ -124,7 +126,8 @@ AudioTrack::AudioTrack( int notificationFrames, int sessionId, transfer_type transferType, - const audio_offload_info_t *offloadInfo) + const audio_offload_info_t *offloadInfo, + int uid) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), @@ -132,7 +135,7 @@ AudioTrack::AudioTrack( { mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, - sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo); + sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, uid); } AudioTrack::~AudioTrack() @@ -169,7 +172,8 @@ status_t AudioTrack::set( bool threadCanCallJava, int sessionId, transfer_type transferType, - const audio_offload_info_t *offloadInfo) + const audio_offload_info_t *offloadInfo, + int uid) { switch (transferType) { case TRANSFER_DEFAULT: @@ -313,6 +317,11 @@ status_t AudioTrack::set( mNotificationFramesReq = notificationFrames; mNotificationFramesAct = 0; mSessionId = sessionId; + if (uid == -1 || (IPCThreadState::self()->getCallingPid() != getpid())) { + mClientUid = IPCThreadState::self()->getCallingUid(); + } else { + mClientUid = uid; + } mAuxEffectId = 0; mFlags = flags; mCbf = cbf; @@ -594,6 +603,19 @@ uint32_t AudioTrack::getSampleRate() const } AutoMutex lock(mLock); + + // sample rate can be updated during playback by the offloaded decoder so we need to + // query the HAL and update if needed. +// FIXME use Proxy return channel to update the rate from server and avoid polling here + if (isOffloaded()) { + if (mOutput != 0) { + uint32_t sampleRate = 0; + status_t status = AudioSystem::getSamplingRate(mOutput, mStreamType, &sampleRate); + if (status == NO_ERROR) { + mSampleRate = sampleRate; + } + } + } return mSampleRate; } @@ -857,7 +879,8 @@ status_t AudioTrack::createTrack_l( ALOGV("createTrack_l() output %d afLatency %d", output, afLatency); // The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where - // n = 1 fast track; nBuffering is ignored + // n = 1 fast track with single buffering; nBuffering is ignored + // n = 2 fast track with double buffering // n = 2 normal track, no sample rate conversion // n = 3 normal track, with sample rate conversion // (pessimistic; some non-1:1 conversion ratios don't actually need triple-buffering) @@ -962,6 +985,7 @@ status_t AudioTrack::createTrack_l( tid, &mSessionId, mName, + mClientUid, &status); if (track == 0) { @@ -996,9 +1020,11 @@ status_t AudioTrack::createTrack_l( ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount); mAwaitBoost = true; if (sharedBuffer == 0) { - // double-buffering is not required for fast tracks, due to tighter scheduling - if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) { - mNotificationFramesAct = frameCount; + // Theoretically double-buffering is not required for fast tracks, + // due to tighter scheduling. But in practice, to accommodate kernels with + // scheduling jitter, and apps with computation jitter, we use double-buffering. + if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/nBuffering) { + mNotificationFramesAct = frameCount/nBuffering; } } } else { @@ -1665,7 +1691,6 @@ status_t AudioTrack::restoreTrack_l(const char *from) // take the frames that will be lost by track recreation into account in saved position size_t position = mProxy->getPosition() + mProxy->getFramesFilled(); - mNewPosition = position + mUpdatePeriod; size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0; result = createTrack_l(mStreamType, mSampleRate, diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index da73d65..caa7900 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -506,7 +506,7 @@ ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCo { } -status_t ServerProxy::obtainBuffer(Buffer* buffer) +status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush) { LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0); if (mIsShutdown) { @@ -579,7 +579,11 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer) buffer->mRaw = part1 > 0 ? &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL; buffer->mNonContig = availToServer - part1; - mUnreleased = part1; + // After flush(), allow releaseBuffer() on a previously obtained buffer; + // see "Acknowledge any pending flush()" in audioflinger/Tracks.cpp. + if (!ackFlush) { + mUnreleased = part1; + } return part1 > 0 ? NO_ERROR : WOULD_BLOCK; } no_init: @@ -761,7 +765,7 @@ ssize_t StaticAudioTrackServerProxy::pollPosition() return (ssize_t) position; } -status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer) +status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush) { if (mIsShutdown) { buffer->mFrameCount = 0; diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 448a82e..acfaea0 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -96,6 +96,7 @@ public: pid_t tid, int *sessionId, String8& name, + int clientUid, status_t *status) { Parcel data, reply; @@ -121,6 +122,7 @@ public: lSessionId = *sessionId; } data.writeInt32(lSessionId); + data.writeInt32(clientUid); status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply); if (lStatus != NO_ERROR) { ALOGE("createTrack error: %s", strerror(-lStatus)); @@ -762,6 +764,7 @@ status_t BnAudioFlinger::onTransact( audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); + int clientUid = data.readInt32(); String8 name; status_t status; sp<IAudioTrack> track; @@ -773,7 +776,7 @@ status_t BnAudioFlinger::onTransact( track = createTrack( (audio_stream_type_t) streamType, sampleRate, format, channelMask, frameCount, &flags, buffer, output, tid, - &sessionId, name, &status); + &sessionId, name, clientUid, &status); } reply->writeInt32(flags); reply->writeInt32(sessionId); diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp index 6a108ae..f004ca4 100644 --- a/media/libmedia/MemoryLeakTrackUtil.cpp +++ b/media/libmedia/MemoryLeakTrackUtil.cpp @@ -49,7 +49,7 @@ struct MyString8 { } void append(const char *s) { - strcat(mPtr, s); + strncat(mPtr, s, MAX_SIZE - size() - 1); } const char *string() const { @@ -60,6 +60,10 @@ struct MyString8 { return strlen(mPtr); } + void clear() { + *mPtr = '\0'; + } + private: char *mPtr; @@ -139,6 +143,9 @@ void dumpMemoryAddresses(int fd) } } while (moved); + write(fd, result.string(), result.size()); + result.clear(); + for (size_t i = 0; i < count; i++) { AllocEntry *e = &entries[i]; @@ -152,13 +159,14 @@ void dumpMemoryAddresses(int fd) result.append(buffer); } result.append("\n"); + + write(fd, result.string(), result.size()); + result.clear(); } delete[] entries; free_malloc_leak_info(info); } - - write(fd, result.string(), result.size()); } #else diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 9553458..9ac9105 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -590,7 +590,7 @@ sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre( } if (!p->hardwareOutput()) { - mAudioOutput = new AudioOutput(mAudioSessionId); + mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid()); static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput); } @@ -1296,12 +1296,13 @@ Exit: #undef LOG_TAG #define LOG_TAG "AudioSink" -MediaPlayerService::AudioOutput::AudioOutput(int sessionId) +MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid) : mCallback(NULL), mCallbackCookie(NULL), mCallbackData(NULL), mBytesWritten(0), mSessionId(sessionId), + mUid(uid), mFlags(AUDIO_OUTPUT_FLAG_NONE) { ALOGV("AudioOutput(%d)", sessionId); mStreamType = AUDIO_STREAM_MUSIC; @@ -1549,7 +1550,8 @@ status_t MediaPlayerService::AudioOutput::open( 0, // notification frames mSessionId, AudioTrack::TRANSFER_CALLBACK, - offloadInfo); + offloadInfo, + mUid); } else { t = new AudioTrack( mStreamType, @@ -1558,10 +1560,13 @@ status_t MediaPlayerService::AudioOutput::open( channelMask, frameCount, flags, - NULL, - NULL, - 0, - mSessionId); + NULL, // callback + NULL, // user data + 0, // notification frames + mSessionId, + AudioTrack::TRANSFER_DEFAULT, + NULL, // offload info + mUid); } if ((t == 0) || (t->initCheck() != NO_ERROR)) { @@ -1808,6 +1813,12 @@ int MediaPlayerService::AudioOutput::getSessionId() const return mSessionId; } +uint32_t MediaPlayerService::AudioOutput::getSampleRate() const +{ + if (mTrack == 0) return 0; + return mTrack->getSampleRate(); +} + #undef LOG_TAG #define LOG_TAG "AudioCache" MediaPlayerService::AudioCache::AudioCache(const sp<IMemoryHeap>& heap) : @@ -2010,6 +2021,14 @@ int MediaPlayerService::AudioCache::getSessionId() const return 0; } +uint32_t MediaPlayerService::AudioCache::getSampleRate() const +{ + if (mMsecsPerFrame == 0) { + return 0; + } + return (uint32_t)(1.e3 / mMsecsPerFrame); +} + void MediaPlayerService::addBatteryData(uint32_t params) { Mutex::Autolock lock(mLock); diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 21f4117..9c084e1 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -72,7 +72,7 @@ class MediaPlayerService : public BnMediaPlayerService class CallbackData; public: - AudioOutput(int sessionId); + AudioOutput(int sessionId, int uid); virtual ~AudioOutput(); virtual bool ready() const { return mTrack != 0; } @@ -86,6 +86,7 @@ class MediaPlayerService : public BnMediaPlayerService virtual status_t getPosition(uint32_t *position) const; virtual status_t getFramesWritten(uint32_t *frameswritten) const; virtual int getSessionId() const; + virtual uint32_t getSampleRate() const; virtual status_t open( uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, @@ -100,7 +101,10 @@ class MediaPlayerService : public BnMediaPlayerService virtual void flush(); virtual void pause(); virtual void close(); - void setAudioStreamType(audio_stream_type_t streamType) { mStreamType = streamType; } + void setAudioStreamType(audio_stream_type_t streamType) { + mStreamType = streamType; } + virtual audio_stream_type_t getAudioStreamType() const { return mStreamType; } + void setVolume(float left, float right); virtual status_t setPlaybackRatePermille(int32_t ratePermille); status_t setAuxEffectSendLevel(float level); @@ -135,6 +139,7 @@ class MediaPlayerService : public BnMediaPlayerService uint32_t mSampleRateHz; // sample rate of the content, as set in open() float mMsecsPerFrame; int mSessionId; + int mUid; float mSendLevel; int mAuxEffectId; static bool mIsOnEmulator; @@ -191,6 +196,7 @@ class MediaPlayerService : public BnMediaPlayerService virtual status_t getPosition(uint32_t *position) const; virtual status_t getFramesWritten(uint32_t *frameswritten) const; virtual int getSessionId() const; + virtual uint32_t getSampleRate() const; virtual status_t open( uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask, @@ -206,6 +212,9 @@ class MediaPlayerService : public BnMediaPlayerService virtual void pause() {} virtual void close() {} void setAudioStreamType(audio_stream_type_t streamType) {} + // stream type is not used for AudioCache + virtual audio_stream_type_t getAudioStreamType() const { return AUDIO_STREAM_DEFAULT; } + void setVolume(float left, float right) {} virtual status_t setPlaybackRatePermille(int32_t ratePermille) { return INVALID_OPERATION; } uint32_t sampleRate() const { return mSampleRate; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 095d5ca..f9d9020 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -70,8 +70,9 @@ StagefrightRecorder::StagefrightRecorder() mOutputFd(-1), mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), - mStarted(false), mSurfaceMediaSource(NULL), - mCaptureTimeLapse(false) { + mCaptureTimeLapse(false), + mStarted(false), + mSurfaceMediaSource(NULL) { ALOGV("Constructor"); reset(); @@ -1089,7 +1090,22 @@ void StagefrightRecorder::clipVideoFrameWidth() { } } -status_t StagefrightRecorder::checkVideoEncoderCapabilities() { +status_t StagefrightRecorder::checkVideoEncoderCapabilities( + bool *supportsCameraSourceMetaDataMode) { + /* hardware codecs must support camera source meta data mode */ + Vector<CodecCapabilities> codecs; + OMXClient client; + CHECK_EQ(client.connect(), (status_t)OK); + QueryCodecs( + client.interface(), + (mVideoEncoder == VIDEO_ENCODER_H263 ? MEDIA_MIMETYPE_VIDEO_H263 : + mVideoEncoder == VIDEO_ENCODER_MPEG_4_SP ? MEDIA_MIMETYPE_VIDEO_MPEG4 : + mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : ""), + false /* decoder */, true /* hwCodec */, &codecs); + *supportsCameraSourceMetaDataMode = codecs.size() > 0; + ALOGV("encoder %s camera source meta-data mode", + *supportsCameraSourceMetaDataMode ? "supports" : "DOES NOT SUPPORT"); + if (!mCaptureTimeLapse) { // Dont clip for time lapse capture as encoder will have enough // time to encode because of slow capture rate of time lapse. @@ -1307,7 +1323,9 @@ status_t StagefrightRecorder::setupSurfaceMediaSource() { status_t StagefrightRecorder::setupCameraSource( sp<CameraSource> *cameraSource) { status_t err = OK; - if ((err = checkVideoEncoderCapabilities()) != OK) { + bool encoderSupportsCameraSourceMetaDataMode; + if ((err = checkVideoEncoderCapabilities( + &encoderSupportsCameraSourceMetaDataMode)) != OK) { return err; } Size videoSize; @@ -1323,13 +1341,14 @@ status_t StagefrightRecorder::setupCameraSource( mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera( mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, videoSize, mFrameRate, mPreviewSurface, - mTimeBetweenTimeLapseFrameCaptureUs); + mTimeBetweenTimeLapseFrameCaptureUs, + encoderSupportsCameraSourceMetaDataMode); *cameraSource = mCameraSourceTimeLapse; } else { *cameraSource = CameraSource::CreateFromCamera( mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, videoSize, mFrameRate, - mPreviewSurface, true /*storeMetaDataInVideoBuffers*/); + mPreviewSurface, encoderSupportsCameraSourceMetaDataMode); } mCamera.clear(); mCameraProxy.clear(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index c864207..31f09e0 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -139,7 +139,8 @@ private: status_t startRTPRecording(); status_t startMPEG2TSRecording(); sp<MediaSource> createAudioSource(); - status_t checkVideoEncoderCapabilities(); + status_t checkVideoEncoderCapabilities( + bool *supportsCameraSourceMetaDataMode); status_t checkAudioEncoderCapabilities(); // Generic MediaSource set-up. Returns the appropriate // source (CameraSource or SurfaceMediaSource) diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index d8b35d7..f1782cc 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -201,7 +201,16 @@ void NuPlayer::HTTPLiveSource::onSessionNotify(const sp<AMessage> &msg) { switch (what) { case LiveSession::kWhatPrepared: { - notifyVideoSizeChanged(0, 0); + // notify the current size here if we have it, otherwise report an initial size of (0,0) + sp<AMessage> format = getFormat(false /* audio */); + int32_t width; + int32_t height; + if (format != NULL && + format->findInt32("width", &width) && format->findInt32("height", &height)) { + notifyVideoSizeChanged(width, height); + } else { + notifyVideoSizeChanged(0, 0); + } uint32_t flags = FLAG_CAN_PAUSE; if (mLiveSession->isSeekable()) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 750287f..3669a5b 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -32,6 +32,8 @@ #include "ATSParser.h" +#include "SoftwareRenderer.h" + #include <cutils/properties.h> // for property_get #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> @@ -146,6 +148,7 @@ NuPlayer::NuPlayer() : mUIDValid(false), mSourceFlags(0), mVideoIsAVC(false), + mNeedsSwRenderer(false), mAudioEOS(false), mVideoEOS(false), mScanSourcesPending(false), @@ -444,6 +447,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("kWhatStart"); mVideoIsAVC = false; + mNeedsSwRenderer = false; mAudioEOS = false; mVideoEOS = false; mSkipRenderingAudioUntilMediaTimeUs = -1; @@ -680,6 +684,20 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { notifyListener( MEDIA_SET_VIDEO_SIZE, displayWidth, displayHeight); + + if (mNeedsSwRenderer && mNativeWindow != NULL) { + int32_t colorFormat; + CHECK(codecRequest->findInt32("color-format", &colorFormat)); + + sp<MetaData> meta = new MetaData; + meta->setInt32(kKeyWidth, width); + meta->setInt32(kKeyHeight, height); + meta->setRect(kKeyCropRect, cropLeft, cropTop, cropRight, cropBottom); + meta->setInt32(kKeyColorFormat, colorFormat); + + mRenderer->setSoftRenderer( + new SoftwareRenderer(mNativeWindow->getNativeWindow(), meta)); + } } } else if (what == ACodec::kWhatShutdownCompleted) { ALOGV("%s shutdown completed", audio ? "audio" : "video"); @@ -703,8 +721,13 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { mRenderer->queueEOS(audio, UNKNOWN_ERROR); } else if (what == ACodec::kWhatDrainThisBuffer) { renderBuffer(audio, codecRequest); - } else if (what != ACodec::kWhatComponentAllocated - && what != ACodec::kWhatComponentConfigured + } else if (what == ACodec::kWhatComponentAllocated) { + if (!audio) { + AString name; + CHECK(codecRequest->findString("componentName", &name)); + mNeedsSwRenderer = name.startsWith("OMX.google."); + } + } else if (what != ACodec::kWhatComponentConfigured && what != ACodec::kWhatBuffersAllocated) { ALOGV("Unhandled codec notification %d '%c%c%c%c'.", what, diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 13350f3..590e1f2 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -116,6 +116,7 @@ private: sp<MediaPlayerBase::AudioSink> mAudioSink; sp<Decoder> mVideoDecoder; bool mVideoIsAVC; + bool mNeedsSwRenderer; sp<Decoder> mAudioDecoder; sp<Renderer> mRenderer; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 3b2784b..bf5271e 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -20,6 +20,8 @@ #include "NuPlayerRenderer.h" +#include "SoftwareRenderer.h" + #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -34,6 +36,7 @@ NuPlayer::Renderer::Renderer( const sp<AMessage> ¬ify, uint32_t flags) : mAudioSink(sink), + mSoftRenderer(NULL), mNotify(notify), mFlags(flags), mNumFramesWritten(0), @@ -57,6 +60,12 @@ NuPlayer::Renderer::Renderer( } NuPlayer::Renderer::~Renderer() { + delete mSoftRenderer; +} + +void NuPlayer::Renderer::setSoftRenderer(SoftwareRenderer *softRenderer) { + delete mSoftRenderer; + mSoftRenderer = softRenderer; } void NuPlayer::Renderer::queueBuffer( @@ -413,7 +422,12 @@ void NuPlayer::Renderer::onDrainVideoQueue() { ALOGV("video late by %lld us (%.2f secs)", mVideoLateByUs, mVideoLateByUs / 1E6); } else { - ALOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6); + ALOGV("rendering video at media time %.2f secs", + (mFlags & FLAG_REAL_TIME ? realTimeUs : + (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6); + if (mSoftRenderer != NULL) { + mSoftRenderer->render(entry->mBuffer->data(), entry->mBuffer->size(), NULL); + } } entry->mNotifyConsumed->setInt32("render", !tooLate); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 94a05ea..9124e03 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -23,6 +23,7 @@ namespace android { struct ABuffer; +class SoftwareRenderer; struct NuPlayer::Renderer : public AHandler { enum Flags { @@ -56,6 +57,8 @@ struct NuPlayer::Renderer : public AHandler { kWhatMediaRenderingStart = 'mdrd', }; + void setSoftRenderer(SoftwareRenderer *softRenderer); + protected: virtual ~Renderer(); @@ -83,6 +86,7 @@ private: static const int64_t kMinPositionUpdateDelayUs; sp<MediaPlayerBase::AudioSink> mAudioSink; + SoftwareRenderer *mSoftRenderer; sp<AMessage> mNotify; uint32_t mFlags; List<QueueEntry> mAudioQueue; diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp index d31d947..2aae4dd 100644 --- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp +++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp @@ -86,7 +86,7 @@ struct StreamSource : public FragmentedMP4Parser::Source { total += n; } - ALOGV("read %ld bytes at offset %lld", n, mPosition); + ALOGV("read %ld bytes at offset %lld", total, mPosition); mPosition += total; diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp index de0ad28..3c61b60 100644 --- a/media/libnbaio/MonoPipe.cpp +++ b/media/libnbaio/MonoPipe.cpp @@ -183,7 +183,7 @@ ssize_t MonoPipe::write(const void *buffer, size_t count) } } if (ns > 0) { - const struct timespec req = {0, ns}; + const struct timespec req = {0, static_cast<long>(ns)}; nanosleep(&req, NULL); } // record the time that this write() completed diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 1adab38..76a3358 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1909,6 +1909,11 @@ status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) { err = setupAVCEncoderParameters(msg); break; + case OMX_VIDEO_CodingVP8: + case OMX_VIDEO_CodingVP9: + err = setupVPXEncoderParameters(msg); + break; + default: break; } @@ -2240,6 +2245,17 @@ status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) { return configureBitrate(bitrate, bitrateMode); } +status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg) { + int32_t bitrate; + if (!msg->findInt32("bitrate", &bitrate)) { + return INVALID_OPERATION; + } + + OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg); + + return configureBitrate(bitrate, bitrateMode); +} + status_t ACodec::verifySupportForProfileAndLevel( int32_t profile, int32_t level) { OMX_VIDEO_PARAM_PROFILELEVELTYPE params; @@ -3072,11 +3088,16 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { /* these are unfilled buffers returned by client */ CHECK(msg->findInt32("err", &err)); - ALOGV("[%s] saw error %d instead of an input buffer", - mCodec->mComponentName.c_str(), err); + if (err == OK) { + /* buffers with no errors are returned on MediaCodec.flush */ + mode = KEEP_BUFFERS; + } else { + ALOGV("[%s] saw error %d instead of an input buffer", + mCodec->mComponentName.c_str(), err); + eos = true; + } buffer.clear(); - mode = KEEP_BUFFERS; } int32_t tmp; @@ -3394,7 +3415,7 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { int32_t render; if (mCodec->mNativeWindow != NULL && msg->findInt32("render", &render) && render != 0 - && (info->mData == NULL || info->mData->size() != 0)) { + && info->mData != NULL && info->mData->size() != 0) { // The client wants this buffer to be rendered. status_t err; diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index a8a8786..05ee34e 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -721,16 +721,27 @@ int64_t AudioPlayer::getRealTimeUsLocked() const { return result + diffUs; } -int64_t AudioPlayer::getOutputPlayPositionUs_l() const +int64_t AudioPlayer::getOutputPlayPositionUs_l() { uint32_t playedSamples = 0; + uint32_t sampleRate; if (mAudioSink != NULL) { mAudioSink->getPosition(&playedSamples); + sampleRate = mAudioSink->getSampleRate(); } else { mAudioTrack->getPosition(&playedSamples); + sampleRate = mAudioTrack->getSampleRate(); + } + if (sampleRate != 0) { + mSampleRate = sampleRate; } - const int64_t playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / mSampleRate; + int64_t playedUs; + if (mSampleRate != 0) { + playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / mSampleRate; + } else { + playedUs = 0; + } // HAL position is relative to the first buffer we sent at mStartPosUs const int64_t renderedDuration = mStartPosUs + playedUs; diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index bdd842f..d7223d9 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -236,10 +236,10 @@ status_t AudioSource::read( memset((uint8_t *) buffer->data(), 0, buffer->range_length()); } else if (elapsedTimeUs < kAutoRampStartUs + kAutoRampDurationUs) { int32_t autoRampDurationFrames = - (kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL; + ((int64_t)kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting int32_t autoRampStartFrames = - (kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL; + ((int64_t)kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting int32_t nFrames = mNumFramesReceived - autoRampStartFrames; rampVolume(nFrames, autoRampDurationFrames, diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index c912f75..aae6800 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -606,6 +606,9 @@ void AwesomePlayer::reset_l() { mWatchForAudioSeekComplete = false; mWatchForAudioEOS = false; + + mMediaRenderingStartGeneration = 0; + mStartGeneration = 0; } void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { @@ -798,7 +801,7 @@ void AwesomePlayer::onBufferingUpdate() { } } - if (mFlags & (PLAYING | PREPARING)) { + if (mFlags & (PLAYING | PREPARING | CACHE_UNDERRUN)) { postBufferingEvent_l(); } } @@ -895,6 +898,8 @@ status_t AwesomePlayer::play_l() { return OK; } + mMediaRenderingStartGeneration = ++mStartGeneration; + if (!(mFlags & PREPARED)) { status_t err = prepare_l(); @@ -1197,8 +1202,7 @@ void AwesomePlayer::initRenderer_l() { setVideoScalingMode_l(mVideoScalingMode); if (USE_SURFACE_ALLOC && !strncmp(component, "OMX.", 4) - && strncmp(component, "OMX.google.", 11) - && strcmp(component, "OMX.Nvidia.mpeg2v.decode")) { + && strncmp(component, "OMX.google.", 11)) { // Hardware decoders avoid the CPU color conversion by decoding // directly to ANativeBuffers, so we must use a renderer that // just pushes those buffers to the ANativeWindow. @@ -1495,7 +1499,13 @@ status_t AwesomePlayer::initAudioDecoder() { // This doesn't guarantee that the hardware has a free stream // but it avoids us attempting to open (and re-open) an offload // stream to hardware that doesn't have the necessary codec - mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL), isStreamingHTTP()); + audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; + if (mAudioSink != NULL) { + streamType = mAudioSink->getAudioStreamType(); + } + + mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL), + isStreamingHTTP(), streamType); if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { ALOGV("createAudioPlayer: bypass OMX (raw)"); @@ -1927,7 +1937,7 @@ void AwesomePlayer::onVideoEvent() { ++mStats.mNumVideoFramesDropped; } - postVideoEvent_l(); + postVideoEvent_l(0); return; } } @@ -1967,6 +1977,41 @@ void AwesomePlayer::onVideoEvent() { return; } + /* get next frame time */ + if (wasSeeking == NO_SEEK) { + MediaSource::ReadOptions options; + for (;;) { + status_t err = mVideoSource->read(&mVideoBuffer, &options); + if (err != OK) { + // deal with any errors next time + CHECK(mVideoBuffer == NULL); + postVideoEvent_l(0); + return; + } + + if (mVideoBuffer->range_length() != 0) { + break; + } + + // Some decoders, notably the PV AVC software decoder + // return spurious empty buffers that we just want to ignore. + + mVideoBuffer->release(); + mVideoBuffer = NULL; + } + + { + Mutex::Autolock autoLock(mStatsLock); + ++mStats.mNumVideoFramesDecoded; + } + + int64_t nextTimeUs; + CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &nextTimeUs)); + int64_t delayUs = nextTimeUs - ts->getRealTimeUs() + mTimeSourceDeltaUs; + postVideoEvent_l(delayUs > 10000 ? 10000 : delayUs < 0 ? 0 : delayUs); + return; + } + postVideoEvent_l(); } @@ -2051,7 +2096,10 @@ void AwesomePlayer::onCheckAudioStatus() { mSeekNotificationSent = true; } - mSeeking = NO_SEEK; + if (mVideoSource == NULL) { + // For video the mSeeking flag is always reset in finishSeekIfNecessary + mSeeking = NO_SEEK; + } notifyIfMediaStarted_l(); } diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 20214e8..5772316 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -41,13 +41,15 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera( Size videoSize, int32_t videoFrameRate, const sp<IGraphicBufferProducer>& surface, - int64_t timeBetweenFrameCaptureUs) { + int64_t timeBetweenFrameCaptureUs, + bool storeMetaDataInVideoBuffers) { CameraSourceTimeLapse *source = new CameraSourceTimeLapse(camera, proxy, cameraId, clientName, clientUid, videoSize, videoFrameRate, surface, - timeBetweenFrameCaptureUs); + timeBetweenFrameCaptureUs, + storeMetaDataInVideoBuffers); if (source != NULL) { if (source->initCheck() != OK) { @@ -67,9 +69,11 @@ CameraSourceTimeLapse::CameraSourceTimeLapse( Size videoSize, int32_t videoFrameRate, const sp<IGraphicBufferProducer>& surface, - int64_t timeBetweenFrameCaptureUs) + int64_t timeBetweenFrameCaptureUs, + bool storeMetaDataInVideoBuffers) : CameraSource(camera, proxy, cameraId, clientName, clientUid, - videoSize, videoFrameRate, surface, true), + videoSize, videoFrameRate, surface, + storeMetaDataInVideoBuffers), mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate), mLastTimeLapseFrameRealTimestampUs(0), mSkipCurrentFrame(false) { diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index fc6fd9c..97987e2 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -107,6 +107,7 @@ status_t DataSource::getSize(off64_t *size) { Mutex DataSource::gSnifferMutex; List<DataSource::SnifferFunc> DataSource::gSniffers; +bool DataSource::gSniffersRegistered = false; bool DataSource::sniff( String8 *mimeType, float *confidence, sp<AMessage> *meta) { @@ -114,7 +115,13 @@ bool DataSource::sniff( *confidence = 0.0f; meta->clear(); - Mutex::Autolock autoLock(gSnifferMutex); + { + Mutex::Autolock autoLock(gSnifferMutex); + if (!gSniffersRegistered) { + return false; + } + } + for (List<SnifferFunc>::iterator it = gSniffers.begin(); it != gSniffers.end(); ++it) { String8 newMimeType; @@ -133,9 +140,7 @@ bool DataSource::sniff( } // static -void DataSource::RegisterSniffer(SnifferFunc func) { - Mutex::Autolock autoLock(gSnifferMutex); - +void DataSource::RegisterSniffer_l(SnifferFunc func) { for (List<SnifferFunc>::iterator it = gSniffers.begin(); it != gSniffers.end(); ++it) { if (*it == func) { @@ -148,23 +153,29 @@ void DataSource::RegisterSniffer(SnifferFunc func) { // static void DataSource::RegisterDefaultSniffers() { - RegisterSniffer(SniffMPEG4); - RegisterSniffer(SniffMatroska); - RegisterSniffer(SniffOgg); - RegisterSniffer(SniffWAV); - RegisterSniffer(SniffFLAC); - RegisterSniffer(SniffAMR); - RegisterSniffer(SniffMPEG2TS); - RegisterSniffer(SniffMP3); - RegisterSniffer(SniffAAC); - RegisterSniffer(SniffMPEG2PS); - RegisterSniffer(SniffWVM); + Mutex::Autolock autoLock(gSnifferMutex); + if (gSniffersRegistered) { + return; + } + + RegisterSniffer_l(SniffMPEG4); + RegisterSniffer_l(SniffMatroska); + RegisterSniffer_l(SniffOgg); + RegisterSniffer_l(SniffWAV); + RegisterSniffer_l(SniffFLAC); + RegisterSniffer_l(SniffAMR); + RegisterSniffer_l(SniffMPEG2TS); + RegisterSniffer_l(SniffMP3); + RegisterSniffer_l(SniffAAC); + RegisterSniffer_l(SniffMPEG2PS); + RegisterSniffer_l(SniffWVM); char value[PROPERTY_VALUE_MAX]; if (property_get("drm.service.enabled", value, NULL) && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { - RegisterSniffer(SniffDRM); + RegisterSniffer_l(SniffDRM); } + gSniffersRegistered = true; } // static diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index cbc169b..4f1c5b3 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -39,6 +39,7 @@ #include <utils/String8.h> #include <byteswap.h> +#include "include/ID3.h" namespace android { @@ -682,8 +683,9 @@ status_t MPEG4Extractor::parseDrmSINF(off64_t *offset, off64_t data_offset) { } sinf->len = dataLen - 3; sinf->IPMPData = new char[sinf->len]; + data_offset += 2; - if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) { + if (mDataSource->readAt(data_offset, sinf->IPMPData, sinf->len) < sinf->len) { return ERROR_IO; } data_offset += sinf->len; @@ -962,6 +964,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); int64_t paddingus = duration - (segment_duration + media_time); + if (paddingus < 0) { + // track duration from media header (which is what kKeyDuration is) might + // be slightly shorter than the segment duration, which would make the + // padding negative. Clamp to zero. + paddingus = 0; + } int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000; mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples); } @@ -1379,19 +1387,33 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } else { // No size was specified. Pick a conservatively large size. int32_t width, height; - if (mLastTrack->meta->findInt32(kKeyWidth, &width) && - mLastTrack->meta->findInt32(kKeyHeight, &height)) { - mLastTrack->meta->setInt32(kKeyMaxInputSize, width * height * 3 / 2); - } else { + if (!mLastTrack->meta->findInt32(kKeyWidth, &width) || + !mLastTrack->meta->findInt32(kKeyHeight, &height)) { ALOGE("No width or height, assuming worst case 1080p"); - mLastTrack->meta->setInt32(kKeyMaxInputSize, 3110400); + width = 1920; + height = 1080; + } + + const char *mime; + CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); + if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + // AVC requires compression ratio of at least 2, and uses + // macroblocks + max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192; + } else { + // For all other formats there is no minimum compression + // ratio. Use compression ratio of 1. + max_size = width * height * 3 / 2; } + mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size); } *offset += chunk_size; - // Calculate average frame rate. + // NOTE: setting another piece of metadata invalidates any pointers (such as the + // mimetype) previously obtained, so don't cache them. const char *mime; CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); + // Calculate average frame rate. if (!strncasecmp("video/", mime, 6)) { size_t nSamples = mLastTrack->sampleTable->countSamples(); int64_t durationUs; @@ -1766,6 +1788,18 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('I', 'D', '3', '2'): + { + if (chunk_data_size < 6) { + return ERROR_MALFORMED; + } + + parseID3v2MetaData(data_offset + 6); + + *offset += chunk_size; + break; + } + case FOURCC('-', '-', '-', '-'): { mLastCommentMean.clear(); @@ -2146,7 +2180,7 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { break; } - if (size >= 8 && metadataKey) { + if (size >= 8 && metadataKey && !mFileMetaData->hasData(metadataKey)) { if (metadataKey == kKeyAlbumArt) { mFileMetaData->setData( kKeyAlbumArt, MetaData::TYPE_NONE, @@ -2295,6 +2329,62 @@ status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int dept return OK; } +void MPEG4Extractor::parseID3v2MetaData(off64_t offset) { + ID3 id3(mDataSource, true /* ignorev1 */, offset); + + if (id3.isValid()) { + struct Map { + int key; + const char *tag1; + const char *tag2; + }; + static const Map kMap[] = { + { kKeyAlbum, "TALB", "TAL" }, + { kKeyArtist, "TPE1", "TP1" }, + { kKeyAlbumArtist, "TPE2", "TP2" }, + { kKeyComposer, "TCOM", "TCM" }, + { kKeyGenre, "TCON", "TCO" }, + { kKeyTitle, "TIT2", "TT2" }, + { kKeyYear, "TYE", "TYER" }, + { kKeyAuthor, "TXT", "TEXT" }, + { kKeyCDTrackNumber, "TRK", "TRCK" }, + { kKeyDiscNumber, "TPA", "TPOS" }, + { kKeyCompilation, "TCP", "TCMP" }, + }; + static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); + + for (size_t i = 0; i < kNumMapEntries; ++i) { + if (!mFileMetaData->hasData(kMap[i].key)) { + ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1); + if (it->done()) { + delete it; + it = new ID3::Iterator(id3, kMap[i].tag2); + } + + if (it->done()) { + delete it; + continue; + } + + String8 s; + it->getString(&s); + delete it; + + mFileMetaData->setCString(kMap[i].key, s); + } + } + + size_t dataSize; + String8 mime; + const void *data = id3.getAlbumArt(&dataSize, &mime); + + if (data) { + mFileMetaData->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize); + mFileMetaData->setCString(kKeyAlbumArtMIME, mime.string()); + } + } +} + sp<MediaSource> MPEG4Extractor::getTrack(size_t index) { status_t err; if ((err = readMetaData()) != OK) { @@ -2398,6 +2488,11 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return ERROR_MALFORMED; } + static uint32_t kSamplingRate[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350 + }; + ABitReader br(csd, csd_size); uint32_t objectType = br.getBits(5); @@ -2405,6 +2500,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( objectType = 32 + br.getBits(6); } + //keep AOT type + mLastTrack->meta->setInt32(kKeyAACAOT, objectType); + uint32_t freqIndex = br.getBits(4); int32_t sampleRate = 0; @@ -2417,29 +2515,30 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( numChannels = br.getBits(4); } else { numChannels = br.getBits(4); - if (objectType == 5) { - // SBR specific config per 14496-3 table 1.13 - freqIndex = br.getBits(4); - if (freqIndex == 15) { - if (csd_size < 8) { - return ERROR_MALFORMED; - } - sampleRate = br.getBits(24); - } + + if (freqIndex == 13 || freqIndex == 14) { + return ERROR_MALFORMED; } - if (sampleRate == 0) { - static uint32_t kSamplingRate[] = { - 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000, 7350 - }; + sampleRate = kSamplingRate[freqIndex]; + } - if (freqIndex == 13 || freqIndex == 14) { + if (objectType == 5 || objectType == 29) { // SBR specific config per 14496-3 table 1.13 + uint32_t extFreqIndex = br.getBits(4); + int32_t extSampleRate; + if (extFreqIndex == 15) { + if (csd_size < 8) { return ERROR_MALFORMED; } - - sampleRate = kSamplingRate[freqIndex]; + extSampleRate = br.getBits(24); + } else { + if (extFreqIndex == 13 || extFreqIndex == 14) { + return ERROR_MALFORMED; + } + extSampleRate = kSamplingRate[extFreqIndex]; } + //TODO: save the extension sampling rate value in meta data => + // mLastTrack->meta->setInt32(kKeyExtSampleRate, extSampleRate); } if (numChannels == 0) { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index e299caf..c4c47b3 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1506,7 +1506,8 @@ void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex) { info->mOwnedByClient = false; if (portIndex == kPortIndexInput) { - msg->setInt32("err", ERROR_END_OF_STREAM); + /* no error, just returning buffers */ + msg->setInt32("err", OK); } msg->post(); } @@ -1679,7 +1680,7 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) { return -EACCES; } - if (render && (info->mData == NULL || info->mData->size() != 0)) { + if (render && info->mData != NULL && info->mData->size() != 0) { info->mNotify->setInt32("render", true); if (mSoftRenderer != NULL) { diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp index ae6ae2d..1daead7 100644 --- a/media/libstagefright/MetaData.cpp +++ b/media/libstagefright/MetaData.cpp @@ -89,6 +89,9 @@ bool MetaData::setRect( return setData(key, TYPE_RECT, &r, sizeof(r)); } +/** + * Note that the returned pointer becomes invalid when additional metadata is set. + */ bool MetaData::findCString(uint32_t key, const char **value) { uint32_t type; const void *data; @@ -218,6 +221,16 @@ bool MetaData::findData(uint32_t key, uint32_t *type, return true; } +bool MetaData::hasData(uint32_t key) const { + ssize_t i = mItems.indexOfKey(key); + + if (i < 0) { + return false; + } + + return true; +} + MetaData::typed_data::typed_data() : mType(0), mSize(0) { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 7f56af8..43736ad 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -359,12 +359,7 @@ sp<MediaSource> OMXCodec::Create( observer->setCodec(codec); err = codec->configureCodec(meta); - if (err == OK) { - if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) { - codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime; - } - return codec; } @@ -1346,8 +1341,7 @@ OMXCodec::OMXCodec( mLeftOverBuffer(NULL), mPaused(false), mNativeWindow( - (!strncmp(componentName, "OMX.google.", 11) - || !strcmp(componentName, "OMX.Nvidia.mpeg2v.decode")) + (!strncmp(componentName, "OMX.google.", 11)) ? NULL : nativeWindow) { mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexOutput] = ENABLED; diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 6a16bb4..0afac69 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -38,11 +38,14 @@ namespace android { +static int64_t kWakelockMinDelay = 100000ll; // 100ms + TimedEventQueue::TimedEventQueue() : mNextEventID(1), mRunning(false), mStopped(false), - mDeathRecipient(new PMDeathRecipient(this)) { + mDeathRecipient(new PMDeathRecipient(this)), + mWakeLockCount(0) { } TimedEventQueue::~TimedEventQueue() { @@ -87,9 +90,7 @@ void TimedEventQueue::stop(bool flush) { // some events may be left in the queue if we did not flush and the wake lock // must be released. - if (!mQueue.empty()) { - releaseWakeLock_l(); - } + releaseWakeLock_l(true /*force*/); mQueue.clear(); mRunning = false; @@ -126,13 +127,15 @@ TimedEventQueue::event_id TimedEventQueue::postTimedEvent( QueueItem item; item.event = event; item.realtime_us = realtime_us; + item.has_wakelock = false; if (it == mQueue.begin()) { mQueueHeadChangedCondition.signal(); } - if (mQueue.empty()) { + if (realtime_us > ALooper::GetNowUs() + kWakelockMinDelay) { acquireWakeLock_l(); + item.has_wakelock = true; } mQueue.insert(it, item); @@ -188,10 +191,10 @@ void TimedEventQueue::cancelEvents( ALOGV("cancelling event %d", (*it).event->eventID()); (*it).event->setEventID(0); - it = mQueue.erase(it); - if (mQueue.empty()) { + if ((*it).has_wakelock) { releaseWakeLock_l(); } + it = mQueue.erase(it); if (stopAfterFirstMatch) { return; } @@ -214,6 +217,7 @@ void TimedEventQueue::threadEntry() { for (;;) { int64_t now_us = 0; sp<Event> event; + bool wakeLocked = false; { Mutex::Autolock autoLock(mLock); @@ -280,28 +284,29 @@ void TimedEventQueue::threadEntry() { // removeEventFromQueue_l will return NULL. // Otherwise, the QueueItem will be removed // from the queue and the referenced event returned. - event = removeEventFromQueue_l(eventID); + event = removeEventFromQueue_l(eventID, &wakeLocked); } if (event != NULL) { // Fire event with the lock NOT held. event->fire(this, now_us); + if (wakeLocked) { + Mutex::Autolock autoLock(mLock); + releaseWakeLock_l(); + } } } } sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l( - event_id id) { + event_id id, bool *wakeLocked) { for (List<QueueItem>::iterator it = mQueue.begin(); it != mQueue.end(); ++it) { if ((*it).event->eventID() == id) { sp<Event> event = (*it).event; event->setEventID(0); - + *wakeLocked = (*it).has_wakelock; mQueue.erase(it); - if (mQueue.empty()) { - releaseWakeLock_l(); - } return event; } } @@ -313,51 +318,61 @@ sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l( void TimedEventQueue::acquireWakeLock_l() { - if (mWakeLockToken != 0) { - return; - } - if (mPowerManager == 0) { - // use checkService() to avoid blocking if power service is not up yet - sp<IBinder> binder = - defaultServiceManager()->checkService(String16("power")); - if (binder == 0) { - ALOGW("cannot connect to the power manager service"); - } else { - mPowerManager = interface_cast<IPowerManager>(binder); - binder->linkToDeath(mDeathRecipient); + if (mWakeLockCount == 0) { + CHECK(mWakeLockToken == 0); + if (mPowerManager == 0) { + // use checkService() to avoid blocking if power service is not up yet + sp<IBinder> binder = + defaultServiceManager()->checkService(String16("power")); + if (binder == 0) { + ALOGW("cannot connect to the power manager service"); + } else { + mPowerManager = interface_cast<IPowerManager>(binder); + binder->linkToDeath(mDeathRecipient); + } } - } - if (mPowerManager != 0) { - sp<IBinder> binder = new BBinder(); - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, - binder, - String16("TimedEventQueue"), - String16("media")); - IPCThreadState::self()->restoreCallingIdentity(token); - if (status == NO_ERROR) { - mWakeLockToken = binder; + if (mPowerManager != 0) { + sp<IBinder> binder = new BBinder(); + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, + binder, + String16("TimedEventQueue"), + String16("media")); + IPCThreadState::self()->restoreCallingIdentity(token); + if (status == NO_ERROR) { + mWakeLockToken = binder; + mWakeLockCount++; + } } + } else { + mWakeLockCount++; } } -void TimedEventQueue::releaseWakeLock_l() +void TimedEventQueue::releaseWakeLock_l(bool force) { - if (mWakeLockToken == 0) { + if (mWakeLockCount == 0) { return; } - if (mPowerManager != 0) { - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - mPowerManager->releaseWakeLock(mWakeLockToken, 0); - IPCThreadState::self()->restoreCallingIdentity(token); + if (force) { + // Force wakelock release below by setting reference count to 1. + mWakeLockCount = 1; + } + if (--mWakeLockCount == 0) { + CHECK(mWakeLockToken != 0); + if (mPowerManager != 0) { + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + mPowerManager->releaseWakeLock(mWakeLockToken, 0); + IPCThreadState::self()->restoreCallingIdentity(token); + } + mWakeLockToken.clear(); } - mWakeLockToken.clear(); } void TimedEventQueue::clearPowerManager() { Mutex::Autolock _l(mLock); - releaseWakeLock_l(); + releaseWakeLock_l(true /*force*/); mPowerManager.clear(); } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 4db8e80..216a329 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -540,7 +540,8 @@ const struct mime_conv_t* p = &mimeLookup[0]; return BAD_VALUE; } -bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming) +bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, + bool isStreaming, audio_stream_type_t streamType) { const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); @@ -561,6 +562,17 @@ bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming) return false; } + // check whether it is ELD/LD content -> no offloading + // FIXME: this should depend on audio DSP capabilities. mapMimeToAudioFormat() should use the + // metadata to refine the AAC format and the audio HAL should only list supported profiles. + int32_t aacaot = -1; + if (meta->findInt32(kKeyAACAOT, &aacaot)) { + if (aacaot == 23 || aacaot == 39 ) { + ALOGV("track of type '%s' is ELD/LD content", mime); + return false; + } + } + int32_t srate = -1; if (!meta->findInt32(kKeySampleRate, &srate)) { ALOGV("track of type '%s' does not publish sample rate", mime); @@ -594,7 +606,7 @@ bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming) info.bit_rate = brate; - info.stream_type = AUDIO_STREAM_MUSIC; + info.stream_type = streamType; info.has_video = hasVideo; info.is_streaming = isStreaming; diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index bd12ddc..233db44 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -632,9 +632,6 @@ sp<M3UParser> LiveSession::fetchPlaylist( // playlist unchanged *unchanged = true; - ALOGV("Playlist unchanged, refresh state is now %d", - (int)mRefreshState); - return NULL; } diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 243888c..5ef7c0f 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -416,22 +416,32 @@ static bool MakeURL(const char *baseURL, const char *url, AString *out) { } else { // URL is a relative path - size_t n = strlen(baseURL); - if (baseURL[n - 1] == '/') { - out->setTo(baseURL); - out->append(url); + // Check for a possible query string + const char *qsPos = strchr(baseURL, '?'); + size_t end; + if (qsPos != NULL) { + end = qsPos - baseURL; } else { - const char *slashPos = strrchr(baseURL, '/'); - - if (slashPos > &baseURL[6]) { - out->setTo(baseURL, slashPos - baseURL); - } else { - out->setTo(baseURL); + end = strlen(baseURL); + } + // Check for the last slash before a potential query string + for (ssize_t pos = end - 1; pos >= 0; pos--) { + if (baseURL[pos] == '/') { + end = pos; + break; } + } - out->append("/"); - out->append(url); + // Check whether the found slash actually is part of the path + // and not part of the "http://". + if (end > 6) { + out->setTo(baseURL, end); + } else { + out->setTo(baseURL); } + + out->append("/"); + out->append(url); } ALOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str()); @@ -608,7 +618,7 @@ status_t M3UParser::parseMetaDataDuration( if (meta->get() == NULL) { *meta = new AMessage; } - (*meta)->setInt64(key, (int64_t)x * 1E6); + (*meta)->setInt64(key, (int64_t)(x * 1E6)); return OK; } diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 34d671a..1ec4a40 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -56,14 +56,14 @@ private: DISALLOW_EVIL_CONSTRUCTORS(MemorySource); }; -ID3::ID3(const sp<DataSource> &source, bool ignoreV1) +ID3::ID3(const sp<DataSource> &source, bool ignoreV1, off64_t offset) : mIsValid(false), mData(NULL), mSize(0), mFirstFrameOffset(0), mVersion(ID3_UNKNOWN), mRawSize(0) { - mIsValid = parseV2(source); + mIsValid = parseV2(source, offset); if (!mIsValid && !ignoreV1) { mIsValid = parseV1(source); @@ -79,7 +79,7 @@ ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1) mRawSize(0) { sp<MemorySource> source = new MemorySource(data, size); - mIsValid = parseV2(source); + mIsValid = parseV2(source, 0); if (!mIsValid && !ignoreV1) { mIsValid = parseV1(source); @@ -115,7 +115,7 @@ bool ID3::ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x) { return true; } -bool ID3::parseV2(const sp<DataSource> &source) { +bool ID3::parseV2(const sp<DataSource> &source, off64_t offset) { struct id3_header { char id[3]; uint8_t version_major; @@ -126,7 +126,7 @@ struct id3_header { id3_header header; if (source->readAt( - 0, &header, sizeof(header)) != (ssize_t)sizeof(header)) { + offset, &header, sizeof(header)) != (ssize_t)sizeof(header)) { return false; } @@ -185,7 +185,7 @@ struct id3_header { mSize = size; mRawSize = mSize + sizeof(header); - if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) { + if (source->readAt(offset + sizeof(header), mData, mSize) != (ssize_t)mSize) { free(mData); mData = NULL; diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index cca83ab..e83f3ef 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -35,7 +35,7 @@ struct ID3 { ID3_V2_4, }; - ID3(const sp<DataSource> &source, bool ignoreV1 = false); + ID3(const sp<DataSource> &source, bool ignoreV1 = false, off64_t offset = 0); ID3(const uint8_t *data, size_t size, bool ignoreV1 = false); ~ID3(); @@ -86,7 +86,7 @@ private: size_t mRawSize; bool parseV1(const sp<DataSource> &source); - bool parseV2(const sp<DataSource> &source); + bool parseV2(const sp<DataSource> &source, off64_t offset); void removeUnsynchronization(); bool removeUnsynchronizationV2_4(bool iTunesHack); diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index bd5e4b9..7b4bc6d 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -97,6 +97,7 @@ private: status_t parseChunk(off64_t *offset, int depth); status_t parseITunesMetaData(off64_t offset, size_t size); status_t parse3GPPMetaData(off64_t offset, size_t size, int depth); + void parseID3v2MetaData(off64_t offset); status_t updateAudioTrackInfoFromESDS_MPEG4Audio( const void *esds_data, size_t esds_size); diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h index 4e49c83..3e84256 100644 --- a/media/libstagefright/include/TimedEventQueue.h +++ b/media/libstagefright/include/TimedEventQueue.h @@ -118,6 +118,7 @@ private: struct QueueItem { sp<Event> event; int64_t realtime_us; + bool has_wakelock; }; struct StopEvent : public TimedEventQueue::Event { @@ -139,14 +140,15 @@ private: sp<IPowerManager> mPowerManager; sp<IBinder> mWakeLockToken; const sp<PMDeathRecipient> mDeathRecipient; + uint32_t mWakeLockCount; static void *ThreadWrapper(void *me); void threadEntry(); - sp<Event> removeEventFromQueue_l(event_id id); + sp<Event> removeEventFromQueue_l(event_id id, bool *wakeLocked); void acquireWakeLock_l(); - void releaseWakeLock_l(); + void releaseWakeLock_l(bool force = false); TimedEventQueue(const TimedEventQueue &); TimedEventQueue &operator=(const TimedEventQueue &); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 9850a46..175a263 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -1193,7 +1193,10 @@ status_t ATSParser::parseTS(ABitReader *br) { unsigned sync_byte = br->getBits(8); CHECK_EQ(sync_byte, 0x47u); - MY_LOGV("transport_error_indicator = %u", br->getBits(1)); + if (br->getBits(1)) { // transport_error_indicator + // silently ignore. + return OK; + } unsigned payload_unit_start_indicator = br->getBits(1); ALOGV("payload_unit_start_indicator = %u", payload_unit_start_indicator); diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 8f9c9c8..e0ff0d1 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -604,7 +604,9 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() { dstOffset += pos.nalSize + 4; } +#if !LOG_NDEBUG ALOGV("accessUnit contains nal types %s", out.c_str()); +#endif const NALPosition &pos = nals.itemAt(nals.size() - 1); size_t nextScan = pos.nalOffset + pos.nalSize; diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index cf43e94..b8970ad 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -148,6 +148,18 @@ void GraphicBufferSource::omxExecuting() { } } +void GraphicBufferSource::omxIdle() { + ALOGV("omxIdle"); + + Mutex::Autolock autoLock(mMutex); + + if (mExecuting) { + // We are only interested in the transition from executing->idle, + // not loaded->idle. + mExecuting = false; + } +} + void GraphicBufferSource::omxLoaded(){ Mutex::Autolock autoLock(mMutex); if (!mExecuting) { @@ -194,7 +206,9 @@ void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) { void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { Mutex::Autolock autoLock(mMutex); - CHECK(mExecuting); // could this happen if app stop()s early? + if (!mExecuting) { + return; + } int cbi = findMatchingCodecBuffer_l(header); if (cbi < 0) { @@ -213,7 +227,12 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // see if the GraphicBuffer reference was null, which should only ever // happen for EOS. if (codecBuffer.mGraphicBuffer == NULL) { - CHECK(mEndOfStream && mEndOfStreamSent); + if (!(mEndOfStream && mEndOfStreamSent)) { + // This can happen when broken code sends us the same buffer + // twice in a row. + ALOGE("ERROR: codecBufferEmptied on non-EOS null buffer " + "(buffer emptied twice?)"); + } // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. return; @@ -384,6 +403,23 @@ bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() { if (mLatestSubmittedBufferId < 0 || mSuspended) { return false; } + if (mBufferSlot[mLatestSubmittedBufferId] == NULL) { + // This can happen if the remote side disconnects, causing + // onBuffersReleased() to NULL out our copy of the slots. The + // buffer is gone, so we have nothing to show. + // + // To be on the safe side we try to release the buffer. + ALOGD("repeatLatestSubmittedBuffer_l: slot was NULL"); + mBufferQueue->releaseBuffer( + mLatestSubmittedBufferId, + mLatestSubmittedBufferFrameNum, + EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, + Fence::NO_FENCE); + mLatestSubmittedBufferId = -1; + mLatestSubmittedBufferFrameNum = 0; + return false; + } int cbi = findAvailableCodecBuffer_l(); if (cbi < 0) { diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 244a843..9e5eee6 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -69,6 +69,11 @@ public: // sitting in the BufferQueue, this will send them to the codec. void omxExecuting(); + // This is called when OMX transitions to OMX_StateIdle, indicating that + // the codec is meant to return all buffers back to the client for them + // to be freed. Do NOT submit any more buffers to the component. + void omxIdle(); + // This is called when OMX transitions to OMX_StateLoaded, indicating that // we are shutting down. void omxLoaded(); diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 46e5d71..5f104fc 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -243,13 +243,18 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { status_t OMXNodeInstance::sendCommand( OMX_COMMANDTYPE cmd, OMX_S32 param) { const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource()); - if (bufferSource != NULL - && cmd == OMX_CommandStateSet - && param == OMX_StateLoaded) { - // Initiating transition from Executing -> Loaded - // Buffers are about to be freed. - bufferSource->omxLoaded(); - setGraphicBufferSource(NULL); + if (bufferSource != NULL && cmd == OMX_CommandStateSet) { + if (param == OMX_StateIdle) { + // Initiating transition from Executing -> Idle + // ACodec is waiting for all buffers to be returned, do NOT + // submit any more buffers to the codec. + bufferSource->omxIdle(); + } else if (param == OMX_StateLoaded) { + // Initiating transition from Idle/Executing -> Loaded + // Buffers are about to be freed. + bufferSource->omxLoaded(); + setGraphicBufferSource(NULL); + } // fall through } diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index c674700..edcc087 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -565,7 +565,7 @@ status_t TSPacketizer::packetize( } } - // size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload; + size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload; ALOGV("packet 1 contains %zd padding bytes and %zd bytes of payload", numPaddingBytes, numBytesOfPayload); diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp index 930f0b0..c4f87a0 100644 --- a/media/mtp/MtpDataPacket.cpp +++ b/media/mtp/MtpDataPacket.cpp @@ -331,7 +331,7 @@ void MtpDataPacket::putString(const char* s) { void MtpDataPacket::putString(const uint16_t* string) { int count = 0; - for (int i = 0; i < 256; i++) { + for (int i = 0; i <= MTP_STRING_MAX_CHARACTER_NUMBER; i++) { if (string[i]) count++; else diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp index fe8cf04..f3420a4 100644 --- a/media/mtp/MtpStringBuffer.cpp +++ b/media/mtp/MtpStringBuffer.cpp @@ -56,42 +56,47 @@ MtpStringBuffer::~MtpStringBuffer() { } void MtpStringBuffer::set(const char* src) { - int length = strlen(src); - if (length >= sizeof(mBuffer)) - length = sizeof(mBuffer) - 1; - memcpy(mBuffer, src, length); - // count the characters int count = 0; char ch; - while ((ch = *src++) != 0) { + char* dest = (char*)mBuffer; + + while ((ch = *src++) != 0 && count < MTP_STRING_MAX_CHARACTER_NUMBER) { if ((ch & 0x80) == 0) { // single byte character + *dest++ = ch; } else if ((ch & 0xE0) == 0xC0) { // two byte character - if (! *src++) { + char ch1 = *src++; + if (! ch1) { // last character was truncated, so ignore last byte - length--; break; } + + *dest++ = ch; + *dest++ = ch1; } else if ((ch & 0xF0) == 0xE0) { // 3 byte char - if (! *src++) { + char ch1 = *src++; + if (! ch1) { // last character was truncated, so ignore last byte - length--; break; } - if (! *src++) { - // last character was truncated, so ignore last two bytes - length -= 2; + char ch2 = *src++; + if (! ch2) { + // last character was truncated, so ignore last byte break; } + + *dest++ = ch; + *dest++ = ch1; + *dest++ = ch2; } count++; } - mByteCount = length + 1; - mBuffer[length] = 0; + *dest++ = 0; + mByteCount = dest - (char*)mBuffer; mCharCount = count; } @@ -100,7 +105,7 @@ void MtpStringBuffer::set(const uint16_t* src) { uint16_t ch; uint8_t* dest = mBuffer; - while ((ch = *src++) != 0 && count < 255) { + while ((ch = *src++) != 0 && count < MTP_STRING_MAX_CHARACTER_NUMBER) { if (ch >= 0x0800) { *dest++ = (uint8_t)(0xE0 | (ch >> 12)); *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F)); diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h index cbc8307..e5150df 100644 --- a/media/mtp/MtpStringBuffer.h +++ b/media/mtp/MtpStringBuffer.h @@ -19,6 +19,9 @@ #include <stdint.h> +// Max Character number of a MTP String +#define MTP_STRING_MAX_CHARACTER_NUMBER 255 + namespace android { class MtpDataPacket; @@ -29,7 +32,7 @@ class MtpStringBuffer { private: // mBuffer contains string in UTF8 format // maximum 3 bytes/character, with 1 extra for zero termination - uint8_t mBuffer[255 * 3 + 1]; + uint8_t mBuffer[MTP_STRING_MAX_CHARACTER_NUMBER * 3 + 1]; int mCharCount; int mByteCount; |