diff options
Diffstat (limited to 'media')
39 files changed, 1730 insertions, 293 deletions
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk index 2bb6dbe..e0ca8af 100644 --- a/media/libeffects/downmix/Android.mk +++ b/media/libeffects/downmix/Android.mk @@ -15,16 +15,10 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_RELATIVE_PATH := soundfx -ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) -LOCAL_LDLIBS += -ldl -endif - LOCAL_C_INCLUDES := \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) -LOCAL_PRELINK_MODULE := false - LOCAL_CFLAGS += -fvisibility=hidden include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c index a39d837..661fde9 100644 --- a/media/libeffects/downmix/EffectDownmix.c +++ b/media/libeffects/downmix/EffectDownmix.c @@ -16,7 +16,8 @@ #define LOG_TAG "EffectDownmix" //#define LOG_NDEBUG 0 -#include <cutils/log.h> +#include <log/log.h> +#include <inttypes.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> @@ -29,25 +30,13 @@ #define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896 +// subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_* typedef enum { - CHANNEL_MASK_SURROUND = AUDIO_CHANNEL_OUT_SURROUND, - CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD, - // like AUDIO_CHANNEL_OUT_QUAD with *_SIDE_* instead of *_BACK_*, same channel order - CHANNEL_MASK_QUAD_SIDE = - AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT, - CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1, - // like AUDIO_CHANNEL_OUT_5POINT1 with *_SIDE_* instead of *_BACK_*, same channel order - CHANNEL_MASK_5POINT1_SIDE = - AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT, - CHANNEL_MASK_7POINT1_SIDE_BACK = AUDIO_CHANNEL_OUT_7POINT1, + CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK, + CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE, + CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK, + CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE, + CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1, } downmix_input_channel_mask_t; // effect_handle_t interface implementation for downmix effect @@ -99,7 +88,7 @@ const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t * // strictly for testing, logs the indices of the channels for a given mask, // uses the same code as Downmix_foldGeneric() void Downmix_testIndexComputation(uint32_t mask) { - ALOGI("Testing index computation for 0x%x:", mask); + ALOGI("Testing index computation for 0x%" PRIx32 ":", mask); // check against unsupported channels if (mask & kUnsupported) { ALOGE("Unsupported channels (top or front left/right of center)"); @@ -220,7 +209,7 @@ int32_t DownmixLib_Create(const effect_uuid_t *uuid, *pHandle = (effect_handle_t) module; - ALOGV("DownmixLib_Create() %p , size %d", module, sizeof(downmix_module_t)); + ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t)); return 0; } @@ -254,7 +243,7 @@ int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t ALOGV("DownmixLib_GetDescriptor() i=%d", i); if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) { memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t)); - ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %x", + ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32, i, gDescriptors[i]->uuid.timeLow); return 0; } @@ -328,7 +317,7 @@ static int Downmix_Process(effect_handle_t self, // bypass the optimized downmix routines for the common formats if (!Downmix_foldGeneric( downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) { - ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask); + ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask); return -EINVAL; } break; @@ -339,20 +328,17 @@ static int Downmix_Process(effect_handle_t self, case CHANNEL_MASK_QUAD_SIDE: Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate); break; - case CHANNEL_MASK_SURROUND: - Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate); - break; case CHANNEL_MASK_5POINT1_BACK: case CHANNEL_MASK_5POINT1_SIDE: Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate); break; - case CHANNEL_MASK_7POINT1_SIDE_BACK: + case CHANNEL_MASK_7POINT1: Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate); break; default: if (!Downmix_foldGeneric( downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) { - ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask); + ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask); return -EINVAL; } break; @@ -380,7 +366,7 @@ static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdS pDownmixer = (downmix_object_t*) &pDwmModule->context; - ALOGV("Downmix_Command command %d cmdSize %d",cmdCode, cmdSize); + ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize); switch (cmdCode) { case EFFECT_CMD_INIT: @@ -404,7 +390,7 @@ static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdS break; case EFFECT_CMD_GET_PARAM: - ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %d, pReplyData: %p", + ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p", pCmdData, *replySize, pReplyData); if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) || pReplyData == NULL || @@ -413,7 +399,7 @@ static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdS } effect_param_t *rep = (effect_param_t *) pReplyData; memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t)); - ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %d", + ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32, *(int32_t *)rep->data, rep->vsize); rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize, rep->data + sizeof(int32_t)); @@ -421,8 +407,8 @@ static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdS break; case EFFECT_CMD_SET_PARAM: - ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, " \ - "pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData); + ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32 + ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData); if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t))) || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) { return -EINVAL; @@ -471,7 +457,7 @@ static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdS return -EINVAL; } // FIXME change type if playing on headset vs speaker - ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData); + ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData); break; case EFFECT_CMD_SET_VOLUME: { @@ -491,7 +477,7 @@ static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdS if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) { return -EINVAL; } - ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData); + ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData); break; case EFFECT_CMD_SET_CONFIG_REVERSE: @@ -500,7 +486,7 @@ static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdS break; default: - ALOGW("Downmix_Command invalid command %d",cmdCode); + ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode); return -EINVAL; } @@ -702,28 +688,28 @@ int Downmix_Reset(downmix_object_t *pDownmixer, bool init) { int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) { int16_t value16; - ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d", + ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32, pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue); switch (param) { case DOWNMIX_PARAM_TYPE: if (size != sizeof(downmix_type_t)) { - ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %u, should be %zu", + ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu", size, sizeof(downmix_type_t)); return -EINVAL; } value16 = *(int16_t *)pValue; - ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16); + ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16); if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) { - ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16); + ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16); return -EINVAL; } else { pDownmixer->type = (downmix_type_t) value16; break; default: - ALOGE("Downmix_setParameter unknown parameter %d", param); + ALOGE("Downmix_setParameter unknown parameter %" PRId32, param); return -EINVAL; } } @@ -762,17 +748,17 @@ int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t * case DOWNMIX_PARAM_TYPE: if (*pSize < sizeof(int16_t)) { - ALOGE("Downmix_getParameter invalid parameter size %zu for DOWNMIX_PARAM_TYPE", *pSize); + ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize); return -EINVAL; } pValue16 = (int16_t *)pValue; *pValue16 = (int16_t) pDownmixer->type; *pSize = sizeof(int16_t); - ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16); + ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16); break; default: - ALOGE("Downmix_getParameter unknown parameter %d", param); + ALOGE("Downmix_getParameter unknown parameter %" PRId16, param); return -EINVAL; } @@ -827,65 +813,6 @@ void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool ac /*---------------------------------------------------------------------------- - * Downmix_foldFromSurround() - *---------------------------------------------------------------------------- - * Purpose: - * downmix a "surround sound" (mono rear) signal to stereo - * - * Inputs: - * pSrc surround signal to downmix - * numFrames the number of surround frames to downmix - * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, - * or overwrite pDst (when false) - * - * Outputs: - * pDst downmixed stereo audio samples - * - *---------------------------------------------------------------------------- - */ -void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { - int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format - // sample at index 0 is FL - // sample at index 1 is FR - // sample at index 2 is FC - // sample at index 3 is RC - // code is mostly duplicated between the two values of accumulate to avoid repeating the test - // for every sample - if (accumulate) { - while (numFrames) { - // centerPlusRearContrib = FC(-3dB) + RC(-3dB) - centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12); - // FL + centerPlusRearContrib - lt = (pSrc[0] << 12) + centerPlusRearContrib; - // FR + centerPlusRearContrib - rt = (pSrc[1] << 12) + centerPlusRearContrib; - // accumulate in destination - pDst[0] = clamp16(pDst[0] + (lt >> 13)); - pDst[1] = clamp16(pDst[1] + (rt >> 13)); - pSrc += 4; - pDst += 2; - numFrames--; - } - } else { // same code as above but without adding and clamping pDst[i] to itself - while (numFrames) { - // centerPlusRearContrib = FC(-3dB) + RC(-3dB) - centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12); - // FL + centerPlusRearContrib - lt = (pSrc[0] << 12) + centerPlusRearContrib; - // FR + centerPlusRearContrib - rt = (pSrc[1] << 12) + centerPlusRearContrib; - // store in destination - pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above - pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above - pSrc += 4; - pDst += 2; - numFrames--; - } - } -} - - -/*---------------------------------------------------------------------------- * Downmix_foldFrom5Point1() *---------------------------------------------------------------------------- * Purpose: diff --git a/media/libeffects/downmix/EffectDownmix.h b/media/libeffects/downmix/EffectDownmix.h index fcb3c9e..2399abd 100644 --- a/media/libeffects/downmix/EffectDownmix.h +++ b/media/libeffects/downmix/EffectDownmix.h @@ -97,7 +97,6 @@ int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t s int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue); void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); -void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); bool Downmix_foldGeneric( diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk index 9e8cb83..ea3c59d 100644 --- a/media/libeffects/preprocessing/Android.mk +++ b/media/libeffects/preprocessing/Android.mk @@ -24,12 +24,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ liblog -ifeq ($(TARGET_SIMULATOR),true) -LOCAL_LDLIBS += -ldl -else LOCAL_SHARED_LIBRARIES += libdl -endif - LOCAL_CFLAGS += -fvisibility=hidden include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 5bdaa03..47cab62 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -16,8 +16,9 @@ #define LOG_TAG "EffectVisualizer" //#define LOG_NDEBUG 0 -#include <cutils/log.h> +#include <log/log.h> #include <assert.h> +#include <inttypes.h> #include <stdlib.h> #include <string.h> #include <new> @@ -226,8 +227,8 @@ int Visualizer_init(VisualizerContext *pContext) // int VisualizerLib_Create(const effect_uuid_t *uuid, - int32_t sessionId, - int32_t ioId, + int32_t /*sessionId*/, + int32_t /*ioId*/, effect_handle_t *pHandle) { int ret; int i; @@ -418,7 +419,7 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, return -EINVAL; } -// ALOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize); +// ALOGV("Visualizer_command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize); switch (cmdCode) { case EFFECT_CMD_INIT: @@ -484,19 +485,19 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, } switch (*(uint32_t *)p->data) { case VISUALIZER_PARAM_CAPTURE_SIZE: - ALOGV("get mCaptureSize = %d", pContext->mCaptureSize); + ALOGV("get mCaptureSize = %" PRIu32, pContext->mCaptureSize); *((uint32_t *)p->data + 1) = pContext->mCaptureSize; p->vsize = sizeof(uint32_t); *replySize += sizeof(uint32_t); break; case VISUALIZER_PARAM_SCALING_MODE: - ALOGV("get mScalingMode = %d", pContext->mScalingMode); + ALOGV("get mScalingMode = %" PRIu32, pContext->mScalingMode); *((uint32_t *)p->data + 1) = pContext->mScalingMode; p->vsize = sizeof(uint32_t); *replySize += sizeof(uint32_t); break; case VISUALIZER_PARAM_MEASUREMENT_MODE: - ALOGV("get mMeasurementMode = %d", pContext->mMeasurementMode); + ALOGV("get mMeasurementMode = %" PRIu32, pContext->mMeasurementMode); *((uint32_t *)p->data + 1) = pContext->mMeasurementMode; p->vsize = sizeof(uint32_t); *replySize += sizeof(uint32_t); @@ -520,19 +521,19 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, switch (*(uint32_t *)p->data) { case VISUALIZER_PARAM_CAPTURE_SIZE: pContext->mCaptureSize = *((uint32_t *)p->data + 1); - ALOGV("set mCaptureSize = %d", pContext->mCaptureSize); + ALOGV("set mCaptureSize = %" PRIu32, pContext->mCaptureSize); break; case VISUALIZER_PARAM_SCALING_MODE: pContext->mScalingMode = *((uint32_t *)p->data + 1); - ALOGV("set mScalingMode = %d", pContext->mScalingMode); + ALOGV("set mScalingMode = %" PRIu32, pContext->mScalingMode); break; case VISUALIZER_PARAM_LATENCY: pContext->mLatency = *((uint32_t *)p->data + 1); - ALOGV("set mLatency = %d", pContext->mLatency); + ALOGV("set mLatency = %" PRIu32, pContext->mLatency); break; case VISUALIZER_PARAM_MEASUREMENT_MODE: pContext->mMeasurementMode = *((uint32_t *)p->data + 1); - ALOGV("set mMeasurementMode = %d", pContext->mMeasurementMode); + ALOGV("set mMeasurementMode = %" PRIu32, pContext->mMeasurementMode); break; default: *(int32_t *)pReplyData = -EINVAL; @@ -545,9 +546,9 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, case VISUALIZER_CMD_CAPTURE: { - int32_t captureSize = pContext->mCaptureSize; + uint32_t captureSize = pContext->mCaptureSize; if (pReplyData == NULL || *replySize != captureSize) { - ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d captureSize %d", + ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %" PRIu32 " captureSize %" PRIu32, *replySize, captureSize); return -EINVAL; } @@ -573,7 +574,7 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, int32_t capturePoint = pContext->mCaptureIdx - captureSize - deltaSmpl; if (capturePoint < 0) { - int32_t size = -capturePoint; + uint32_t size = -capturePoint; if (size > captureSize) { size = captureSize; } @@ -604,7 +605,7 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, // measurements aren't relevant anymore and shouldn't bias the new one) const int32_t delayMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext); if (delayMs > DISCARD_MEASUREMENTS_TIME_MS) { - ALOGV("Discarding measurements, last measurement is %dms old", delayMs); + ALOGV("Discarding measurements, last measurement is %" PRId32 "ms old", delayMs); for (uint32_t i=0 ; i<pContext->mMeasurementWindowSizeInBuffers ; i++) { pContext->mPastMeasurements[i].mIsValid = false; pContext->mPastMeasurements[i].mPeakU16 = 0; @@ -638,14 +639,14 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, } else { pIntReplyData[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peakU16 / 32767.0f)); } - ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)", + ALOGV("VISUALIZER_CMD_MEASURE peak=%" PRIu16 " (%" PRId32 "mB), rms=%.1f (%" PRId32 "mB)", peakU16, pIntReplyData[MEASUREMENT_IDX_PEAK], rms, pIntReplyData[MEASUREMENT_IDX_RMS]); } break; default: - ALOGW("Visualizer_command invalid command %d",cmdCode); + ALOGW("Visualizer_command invalid command %" PRIu32, cmdCode); return -EINVAL; } diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index a7bf380..2c8605c 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -79,14 +79,14 @@ AudioRecord::AudioRecord( uint32_t notificationFrames, int sessionId, transfer_type transferType, - audio_input_flags_t flags __unused) + audio_input_flags_t flags) : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mProxy(NULL) { mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user, - notificationFrames, false /*threadCanCallJava*/, sessionId, transferType); + notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags); } AudioRecord::~AudioRecord() diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index fbfd3da..aaaa3f1 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -315,12 +315,20 @@ status_t AudioTrack::set( flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER); } - if (audio_is_linear_pcm(format)) { - mFrameSize = channelCount * audio_bytes_per_sample(format); - mFrameSizeAF = channelCount * sizeof(int16_t); + if (flags & AUDIO_OUTPUT_FLAG_DIRECT) { + if (audio_is_linear_pcm(format)) { + mFrameSize = channelCount * audio_bytes_per_sample(format); + } else { + mFrameSize = sizeof(uint8_t); + } + mFrameSizeAF = mFrameSize; } else { - mFrameSize = sizeof(uint8_t); - mFrameSizeAF = sizeof(uint8_t); + ALOG_ASSERT(audio_is_linear_pcm(format)); + mFrameSize = channelCount * audio_bytes_per_sample(format); + mFrameSizeAF = channelCount * audio_bytes_per_sample( + format == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : format); + // createTrack will return an error if PCM format is not supported by server, + // so no need to check for specific PCM formats here } // Make copy of input parameter offloadInfo so that in the future: @@ -931,7 +939,11 @@ status_t AudioTrack::createTrack_l(size_t epoch) // Ensure that buffer alignment matches channel count // 8-bit data in shared memory is not currently supported by AudioFlinger - size_t alignment = /* mFormat == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2; + size_t alignment = audio_bytes_per_sample( + mFormat == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : mFormat); + if (alignment & 1) { + alignment = 1; + } if (mChannelCount > 1) { // More than 2 channels does not require stronger alignment than stereo alignment <<= 1; @@ -947,7 +959,7 @@ status_t AudioTrack::createTrack_l(size_t epoch) // there's no frameCount parameter. // But when initializing a shared buffer AudioTrack via set(), // there _is_ a frameCount parameter. We silently ignore it. - frameCount = mSharedBuffer->size()/mChannelCount/sizeof(int16_t); + frameCount = mSharedBuffer->size() / mFrameSizeAF; } else if (!(mFlags & AUDIO_OUTPUT_FLAG_FAST)) { @@ -1005,7 +1017,8 @@ status_t AudioTrack::createTrack_l(size_t epoch) sp<IAudioTrack> track = audioFlinger->createTrack(mStreamType, mSampleRate, // AudioFlinger only sees 16-bit PCM - mFormat == AUDIO_FORMAT_PCM_8_BIT ? + mFormat == AUDIO_FORMAT_PCM_8_BIT && + !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT) ? AUDIO_FORMAT_PCM_16_BIT : mFormat, mChannelMask, &temp, @@ -1820,7 +1833,7 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const result.append(" AudioTrack::dump\n"); snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, - mVolume[0], mVolume[1]); + mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]); result.append(buffer); snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat, mChannelCount, mFrameCount); diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index eb813bd..1940fe7 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -106,7 +106,7 @@ public: data.writeInt32(format); data.writeInt32(channelMask); size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0; - data.writeInt32(frameCount); + data.writeInt64(frameCount); track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT; data.writeInt32(lFlags); // haveSharedBuffer @@ -128,7 +128,7 @@ public: if (lStatus != NO_ERROR) { ALOGE("createTrack error: %s", strerror(-lStatus)); } else { - frameCount = reply.readInt32(); + frameCount = reply.readInt64(); if (pFrameCount != NULL) { *pFrameCount = frameCount; } @@ -179,7 +179,7 @@ public: data.writeInt32(format); data.writeInt32(channelMask); size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0; - data.writeInt32(frameCount); + data.writeInt64(frameCount); track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT; data.writeInt32(lFlags); data.writeInt32((int32_t) tid); @@ -192,7 +192,7 @@ public: if (lStatus != NO_ERROR) { ALOGE("openRecord error: %s", strerror(-lStatus)); } else { - frameCount = reply.readInt32(); + frameCount = reply.readInt64(); if (pFrameCount != NULL) { *pFrameCount = frameCount; } @@ -248,7 +248,7 @@ public: data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.writeInt32((int32_t) output); remote()->transact(FRAME_COUNT, data, &reply); - return reply.readInt32(); + return reply.readInt64(); } virtual uint32_t latency(audio_io_handle_t output) const @@ -398,7 +398,7 @@ public: data.writeInt32(format); data.writeInt32(channelMask); remote()->transact(GET_INPUTBUFFERSIZE, data, &reply); - return reply.readInt32(); + return reply.readInt64(); } virtual audio_io_handle_t openOutput(audio_module_handle_t module, @@ -769,7 +769,7 @@ public: Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); remote()->transact(GET_PRIMARY_OUTPUT_FRAME_COUNT, data, &reply); - return reply.readInt32(); + return reply.readInt64(); } virtual status_t setLowRamDevice(bool isLowRamDevice) @@ -797,7 +797,7 @@ status_t BnAudioFlinger::onTransact( uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); - size_t frameCount = data.readInt32(); + size_t frameCount = data.readInt64(); track_flags_t flags = (track_flags_t) data.readInt32(); bool haveSharedBuffer = data.readInt32() != 0; sp<IMemory> buffer; @@ -821,7 +821,7 @@ status_t BnAudioFlinger::onTransact( &sessionId, clientUid, &status); LOG_ALWAYS_FATAL_IF((track != 0) != (status == NO_ERROR)); } - reply->writeInt32(frameCount); + reply->writeInt64(frameCount); reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeInt32(status); @@ -834,7 +834,7 @@ status_t BnAudioFlinger::onTransact( uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); - size_t frameCount = data.readInt32(); + size_t frameCount = data.readInt64(); track_flags_t flags = (track_flags_t) data.readInt32(); pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); @@ -842,7 +842,7 @@ status_t BnAudioFlinger::onTransact( sp<IAudioRecord> record = openRecord(input, sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId, &status); LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR)); - reply->writeInt32(frameCount); + reply->writeInt64(frameCount); reply->writeInt32(flags); reply->writeInt32(sessionId); reply->writeInt32(status); @@ -861,7 +861,7 @@ status_t BnAudioFlinger::onTransact( } break; case FRAME_COUNT: { CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32( frameCount((audio_io_handle_t) data.readInt32()) ); + reply->writeInt64( frameCount((audio_io_handle_t) data.readInt32()) ); return NO_ERROR; } break; case LATENCY: { @@ -960,7 +960,7 @@ status_t BnAudioFlinger::onTransact( uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); audio_channel_mask_t channelMask = data.readInt32(); - reply->writeInt32( getInputBufferSize(sampleRate, format, channelMask) ); + reply->writeInt64( getInputBufferSize(sampleRate, format, channelMask) ); return NO_ERROR; } break; case OPEN_OUTPUT: { @@ -1164,7 +1164,7 @@ status_t BnAudioFlinger::onTransact( } break; case GET_PRIMARY_OUTPUT_FRAME_COUNT: { CHECK_INTERFACE(IAudioFlinger, data, reply); - reply->writeInt32(getPrimaryOutputFrameCount()); + reply->writeInt64(getPrimaryOutputFrameCount()); return NO_ERROR; } break; case SET_LOW_RAM_DEVICE: { diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp index 3c0d4cf..1c299f7 100644 --- a/media/libmedia/IAudioFlingerClient.cpp +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -55,7 +55,7 @@ public: data.writeInt32(desc->samplingRate); data.writeInt32(desc->format); data.writeInt32(desc->channelMask); - data.writeInt32(desc->frameCount); + data.writeInt64(desc->frameCount); data.writeInt32(desc->latency); } remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); @@ -85,7 +85,7 @@ status_t BnAudioFlingerClient::onTransact( desc.samplingRate = data.readInt32(); desc.format = (audio_format_t) data.readInt32(); desc.channelMask = (audio_channel_mask_t) data.readInt32(); - desc.frameCount = data.readInt32(); + desc.frameCount = data.readInt64(); desc.latency = data.readInt32(); param2 = &desc; } diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index ffc21fc..265bb1b 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -118,7 +118,7 @@ public: virtual status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer) { Parcel data, reply; data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); - data.writeInt32(size); + data.writeInt64(size); status_t status = remote()->transact(ALLOCATE_TIMED_BUFFER, data, &reply); if (status == NO_ERROR) { @@ -238,7 +238,7 @@ status_t BnAudioTrack::onTransact( case ALLOCATE_TIMED_BUFFER: { CHECK_INTERFACE(IAudioTrack, data, reply); sp<IMemory> buffer; - status_t status = allocateTimedBuffer(data.readInt32(), &buffer); + status_t status = allocateTimedBuffer(data.readInt64(), &buffer); reply->writeInt32(status); if (status == NO_ERROR) { reply->writeStrongBinder(buffer->asBinder()); diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp index 33239a7..8a66c7c 100644 --- a/media/libmedia/IMediaLogService.cpp +++ b/media/libmedia/IMediaLogService.cpp @@ -43,7 +43,7 @@ public: Parcel data, reply; data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor()); data.writeStrongBinder(shared->asBinder()); - data.writeInt32((int32_t) size); + data.writeInt64((int64_t) size); data.writeCString(name); status_t status = remote()->transact(REGISTER_WRITER, data, &reply); // FIXME ignores status @@ -71,7 +71,7 @@ status_t BnMediaLogService::onTransact( case REGISTER_WRITER: { CHECK_INTERFACE(IMediaLogService, data, reply); sp<IMemory> shared = interface_cast<IMemory>(data.readStrongBinder()); - size_t size = (size_t) data.readInt32(); + size_t size = (size_t) data.readInt64(); const char *name = data.readCString(); registerWriter(shared, size, name); return NO_ERROR; diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp index c7d9d51..432d890 100644 --- a/media/libmedia/IMediaMetadataRetriever.cpp +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -101,7 +101,7 @@ public: data.writeInt32(0); } else { // serialize the headers - data.writeInt32(headers->size()); + data.writeInt64(headers->size()); for (size_t i = 0; i < headers->size(); ++i) { data.writeString8(headers->keyAt(i)); data.writeString8(headers->valueAt(i)); @@ -212,8 +212,8 @@ status_t BnMediaMetadataRetriever::onTransact( const char* srcUrl = data.readCString(); KeyedVector<String8, String8> headers; - int32_t numHeaders = data.readInt32(); - for (int i = 0; i < numHeaders; ++i) { + size_t numHeaders = (size_t) data.readInt64(); + for (size_t i = 0; i < numHeaders; ++i) { String8 key = data.readString8(); String8 value = data.readString8(); headers.add(key, value); diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 71ce320..9c13848 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -140,7 +140,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); data.writeInt32(index); - data.writeInt32(size); + data.writeInt64(size); data.write(params, size); remote()->transact(GET_PARAMETER, data, &reply); @@ -161,7 +161,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); data.writeInt32(index); - data.writeInt32(size); + data.writeInt64(size); data.write(params, size); remote()->transact(SET_PARAMETER, data, &reply); @@ -175,7 +175,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); data.writeInt32(index); - data.writeInt32(size); + data.writeInt64(size); data.write(params, size); remote()->transact(GET_CONFIG, data, &reply); @@ -196,7 +196,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); data.writeInt32(index); - data.writeInt32(size); + data.writeInt64(size); data.write(params, size); remote()->transact(SET_CONFIG, data, &reply); @@ -375,7 +375,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); data.writeInt32(port_index); - data.writeInt32(size); + data.writeInt64(size); remote()->transact(ALLOC_BUFFER, data, &reply); status_t err = reply.readInt32(); @@ -484,7 +484,7 @@ public: data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeIntPtr((intptr_t)node); data.writeInt32(port_index); - data.writeInt32(size); + data.writeInt64(size); data.write(optionData, size); data.writeInt32(type); remote()->transact(SET_INTERNAL_OPTION, data, &reply); @@ -596,7 +596,7 @@ status_t BnOMX::onTransact( node_id node = (void*)data.readIntPtr(); OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32()); - size_t size = data.readInt32(); + size_t size = data.readInt64(); void *params = malloc(size); data.read(params, size); @@ -810,7 +810,7 @@ status_t BnOMX::onTransact( node_id node = (void*)data.readIntPtr(); OMX_U32 port_index = data.readInt32(); - size_t size = data.readInt32(); + size_t size = data.readInt64(); buffer_id buffer; void *buffer_data; diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp index 68ffca8..fe2cc61 100644 --- a/media/libmedia/IStreamSource.cpp +++ b/media/libmedia/IStreamSource.cpp @@ -62,7 +62,7 @@ struct BpStreamSource : public BpInterface<IStreamSource> { virtual void setBuffers(const Vector<sp<IMemory> > &buffers) { Parcel data, reply; data.writeInterfaceToken(IStreamSource::getInterfaceDescriptor()); - data.writeInt32(static_cast<int32_t>(buffers.size())); + data.writeInt64(static_cast<int64_t>(buffers.size())); for (size_t i = 0; i < buffers.size(); ++i) { data.writeStrongBinder(buffers.itemAt(i)->asBinder()); } @@ -72,7 +72,7 @@ struct BpStreamSource : public BpInterface<IStreamSource> { virtual void onBufferAvailable(size_t index) { Parcel data, reply; data.writeInterfaceToken(IStreamSource::getInterfaceDescriptor()); - data.writeInt32(static_cast<int32_t>(index)); + data.writeInt64(static_cast<int64_t>(index)); remote()->transact( ON_BUFFER_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY); } @@ -102,7 +102,7 @@ status_t BnStreamSource::onTransact( case SET_BUFFERS: { CHECK_INTERFACE(IStreamSource, data, reply); - size_t n = static_cast<size_t>(data.readInt32()); + size_t n = static_cast<size_t>(data.readInt64()); Vector<sp<IMemory> > buffers; for (size_t i = 0; i < n; ++i) { sp<IMemory> mem = @@ -117,7 +117,7 @@ status_t BnStreamSource::onTransact( case ON_BUFFER_AVAILABLE: { CHECK_INTERFACE(IStreamSource, data, reply); - onBufferAvailable(static_cast<size_t>(data.readInt32())); + onBufferAvailable(static_cast<size_t>(data.readInt64())); break; } @@ -145,8 +145,8 @@ struct BpStreamListener : public BpInterface<IStreamListener> { virtual void queueBuffer(size_t index, size_t size) { Parcel data, reply; data.writeInterfaceToken(IStreamListener::getInterfaceDescriptor()); - data.writeInt32(static_cast<int32_t>(index)); - data.writeInt32(static_cast<int32_t>(size)); + data.writeInt64(static_cast<int64_t>(index)); + data.writeInt64(static_cast<int64_t>(size)); remote()->transact(QUEUE_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); } @@ -177,8 +177,8 @@ status_t BnStreamListener::onTransact( case QUEUE_BUFFER: { CHECK_INTERFACE(IStreamListener, data, reply); - size_t index = static_cast<size_t>(data.readInt32()); - size_t size = static_cast<size_t>(data.readInt32()); + size_t index = static_cast<size_t>(data.readInt64()); + size_t size = static_cast<size_t>(data.readInt64()); queueBuffer(index, size); break; diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp index f004ca4..66f7161 100644 --- a/media/libmedia/MemoryLeakTrackUtil.cpp +++ b/media/libmedia/MemoryLeakTrackUtil.cpp @@ -17,6 +17,7 @@ #include <media/MemoryLeakTrackUtil.h> #include <stdio.h> +#include <stdlib.h> #include <sys/types.h> #include <unistd.h> diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp index 96738a7..4d9a1fa 100644 --- a/media/libnbaio/NBLog.cpp +++ b/media/libnbaio/NBLog.cpp @@ -343,7 +343,7 @@ void NBLog::Reader::dump(int fd, size_t indent) String8 timestamp, body; lost += i; if (lost > 0) { - body.appendFormat("warning: lost %u bytes worth of events", lost); + body.appendFormat("warning: lost %zu bytes worth of events", lost); // TODO timestamp empty here, only other choice to wait for the first timestamp event in the // log to push it out. Consider keeping the timestamp/body between calls to readAt(). dumpLine(timestamp, body); @@ -354,7 +354,7 @@ void NBLog::Reader::dump(int fd, size_t indent) maxSec /= 10; } if (maxSec >= 0) { - timestamp.appendFormat("[%*s]", width + 4, ""); + timestamp.appendFormat("[%*s]", (int) width + 4, ""); } bool deferredTimestamp = false; while (i < avail) { @@ -364,7 +364,7 @@ void NBLog::Reader::dump(int fd, size_t indent) size_t advance = length + 3; switch (event) { case EVENT_STRING: - body.appendFormat("%.*s", length, (const char *) data); + body.appendFormat("%.*s", (int) length, (const char *) data); break; case EVENT_TIMESTAMP: { // already checked that length == sizeof(struct timespec); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 4aecb80..0a3a3b6 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -17,6 +17,8 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ACodec" +#include <utils/Trace.h> + #include <media/stagefright/ACodec.h> #include <binder/MemoryDealer.h> @@ -36,6 +38,7 @@ #include <media/hardware/HardwareAPI.h> #include <OMX_AudioExt.h> +#include <OMX_VideoExt.h> #include <OMX_Component.h> #include <OMX_IndexExt.h> @@ -97,12 +100,6 @@ struct CodecObserver : public BnOMXObserver { msg->setInt64( "timestamp", omx_msg.u.extended_buffer_data.timestamp); - msg->setPointer( - "platform_private", - omx_msg.u.extended_buffer_data.platform_private); - msg->setPointer( - "data_ptr", - omx_msg.u.extended_buffer_data.data_ptr); break; } @@ -157,9 +154,7 @@ private: IOMX::buffer_id bufferID, size_t rangeOffset, size_t rangeLength, OMX_U32 flags, - int64_t timeUs, - void *platformPrivate, - void *dataPtr); + int64_t timeUs); void getMoreInputDataIfPossible(); @@ -2370,12 +2365,81 @@ status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) { status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg) { int32_t bitrate; + int32_t iFrameInterval = 0; + size_t tsLayers = 0; + OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE pattern = + OMX_VIDEO_VPXTemporalLayerPatternNone; + static const uint32_t kVp8LayerRateAlloction + [OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS] + [OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS] = { + {100, 100, 100}, // 1 layer + { 60, 100, 100}, // 2 layers {60%, 40%} + { 40, 60, 100}, // 3 layers {40%, 20%, 40%} + }; if (!msg->findInt32("bitrate", &bitrate)) { return INVALID_OPERATION; } + msg->findInt32("i-frame-interval", &iFrameInterval); OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg); + float frameRate; + if (!msg->findFloat("frame-rate", &frameRate)) { + int32_t tmp; + if (!msg->findInt32("frame-rate", &tmp)) { + return INVALID_OPERATION; + } + frameRate = (float)tmp; + } + + AString tsSchema; + if (msg->findString("ts-schema", &tsSchema)) { + if (tsSchema == "webrtc.vp8.1-layer") { + pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC; + tsLayers = 1; + } else if (tsSchema == "webrtc.vp8.2-layer") { + pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC; + tsLayers = 2; + } else if (tsSchema == "webrtc.vp8.3-layer") { + pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC; + tsLayers = 3; + } else { + ALOGW("Unsupported ts-schema [%s]", tsSchema.c_str()); + } + } + + OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE vp8type; + InitOMXParams(&vp8type); + vp8type.nPortIndex = kPortIndexOutput; + status_t err = mOMX->getParameter( + mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidVp8Encoder, + &vp8type, sizeof(vp8type)); + + if (err == OK) { + if (iFrameInterval > 0) { + vp8type.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate); + } + vp8type.eTemporalPattern = pattern; + vp8type.nTemporalLayerCount = tsLayers; + if (tsLayers > 0) { + for (size_t i = 0; i < OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS; i++) { + vp8type.nTemporalLayerBitrateRatio[i] = + kVp8LayerRateAlloction[tsLayers - 1][i]; + } + } + if (bitrateMode == OMX_Video_ControlRateConstant) { + vp8type.nMinQuantizer = 2; + vp8type.nMaxQuantizer = 63; + } + + err = mOMX->setParameter( + mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidVp8Encoder, + &vp8type, sizeof(vp8type)); + if (err != OK) { + ALOGW("Extended VP8 parameters set failed: %d", err); + } + } + return configureBitrate(bitrate, bitrateMode); } @@ -3156,23 +3220,17 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { int32_t rangeOffset, rangeLength, flags; int64_t timeUs; - void *platformPrivate; - void *dataPtr; CHECK(msg->findInt32("range_offset", &rangeOffset)); CHECK(msg->findInt32("range_length", &rangeLength)); CHECK(msg->findInt32("flags", &flags)); CHECK(msg->findInt64("timestamp", &timeUs)); - CHECK(msg->findPointer("platform_private", &platformPrivate)); - CHECK(msg->findPointer("data_ptr", &dataPtr)); return onOMXFillBufferDone( bufferID, (size_t)rangeOffset, (size_t)rangeLength, (OMX_U32)flags, - timeUs, - platformPrivate, - dataPtr); + timeUs); } default: @@ -3471,9 +3529,7 @@ bool ACodec::BaseState::onOMXFillBufferDone( IOMX::buffer_id bufferID, size_t rangeOffset, size_t rangeLength, OMX_U32 flags, - int64_t timeUs, - void * /* platformPrivate */, - void * /* dataPtr */) { + int64_t timeUs) { ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx", mCodec->mComponentName.c_str(), bufferID, timeUs, flags); @@ -3611,9 +3667,31 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { if (mCodec->mNativeWindow != NULL && msg->findInt32("render", &render) && render != 0 && info->mData != NULL && info->mData->size() != 0) { + ATRACE_NAME("render"); // The client wants this buffer to be rendered. + 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 + if (info->mData->meta()->findInt64("timeUs", ×tampNs)) { + ALOGI("using buffer PTS of %" PRId64, timestampNs); + timestampNs *= 1000; + } +#endif + } + status_t err; + err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs); + if (err != OK) { + ALOGW("failed to set buffer timestamp: %d", err); + } else { + ALOGI("set PTS to %" PRId64, timestampNs); + } + if ((err = mCodec->mNativeWindow->queueBuffer( mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), -1)) == OK) { @@ -3623,6 +3701,10 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { info->mStatus = BufferInfo::OWNED_BY_US; } } else { + if (mCodec->mNativeWindow != NULL && + (info->mData == NULL || info->mData->size() != 0)) { + ATRACE_NAME("frame-drop"); + } info->mStatus = BufferInfo::OWNED_BY_US; } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 714b5e0..d9e39ff 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -14,6 +14,7 @@ LOCAL_SRC_FILES:= \ AwesomePlayer.cpp \ CameraSource.cpp \ CameraSourceTimeLapse.cpp \ + ClockEstimator.cpp \ DataSource.cpp \ DataURISource.cpp \ DRMExtractor.cpp \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index e924076..d679be1 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -42,6 +42,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/timedtext/TimedTextDriver.h> #include <media/stagefright/AudioPlayer.h> +#include <media/stagefright/ClockEstimator.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/FileSource.h> #include <media/stagefright/MediaBuffer.h> @@ -105,12 +106,15 @@ struct AwesomeLocalRenderer : public AwesomeRenderer { } virtual void render(MediaBuffer *buffer) { + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + render((const uint8_t *)buffer->data() + buffer->range_offset(), - buffer->range_length()); + buffer->range_length(), timeUs * 1000); } - void render(const void *data, size_t size) { - mTarget->render(data, size, NULL); + void render(const void *data, size_t size, int64_t timestampNs) { + mTarget->render(data, size, timestampNs, NULL); } protected: @@ -231,6 +235,8 @@ AwesomePlayer::AwesomePlayer() &AwesomePlayer::onAudioTearDownEvent); mAudioTearDownEventPending = false; + mClockEstimator = new WindowedLinearFitEstimator(); + reset(); } @@ -718,11 +724,9 @@ void AwesomePlayer::onBufferingUpdate() { finishAsyncPrepare_l(); } } else { - int64_t bitrate; - if (getBitrate(&bitrate)) { - size_t cachedSize = mCachedSource->cachedSize(); - int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate; - + bool eos2; + int64_t cachedDurationUs; + if (getCachedDuration_l(&cachedDurationUs, &eos2) && mDurationUs > 0) { int percentage = 100.0 * (double)cachedDurationUs / mDurationUs; if (percentage > 100) { percentage = 100; @@ -730,7 +734,7 @@ void AwesomePlayer::onBufferingUpdate() { notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage); } else { - // We don't know the bitrate of the stream, use absolute size + // We don't know the bitrate/duration of the stream, use absolute size // limits to maintain the cache. if ((mFlags & PLAYING) && !eos @@ -1868,21 +1872,28 @@ void AwesomePlayer::onVideoEvent() { TimeSource *ts = ((mFlags & AUDIO_AT_EOS) || !(mFlags & AUDIOPLAYER_STARTED)) ? &mSystemTimeSource : mTimeSource; + int64_t systemTimeUs = mSystemTimeSource.getRealTimeUs(); + int64_t looperTimeUs = ALooper::GetNowUs(); if (mFlags & FIRST_FRAME) { modifyFlags(FIRST_FRAME, CLEAR); mSinceLastDropped = 0; - mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs; + mClockEstimator->reset(); + mTimeSourceDeltaUs = estimateRealTimeUs(ts, systemTimeUs) - timeUs; } int64_t realTimeUs, mediaTimeUs; if (!(mFlags & AUDIO_AT_EOS) && mAudioPlayer != NULL && mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) { + ALOGV("updating TSdelta (%" PRId64 " => %" PRId64 " change %" PRId64 ")", + mTimeSourceDeltaUs, realTimeUs - mediaTimeUs, + mTimeSourceDeltaUs - (realTimeUs - mediaTimeUs)); + ATRACE_INT("TS delta change (ms)", (mTimeSourceDeltaUs - (realTimeUs - mediaTimeUs)) / 1E3); mTimeSourceDeltaUs = realTimeUs - mediaTimeUs; } if (wasSeeking == SEEK_VIDEO_ONLY) { - int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs; + int64_t nowUs = estimateRealTimeUs(ts, systemTimeUs) - mTimeSourceDeltaUs; int64_t latenessUs = nowUs - timeUs; @@ -1893,12 +1904,13 @@ void AwesomePlayer::onVideoEvent() { } } + int64_t latenessUs = 0; if (wasSeeking == NO_SEEK) { // Let's display the first frame after seeking right away. - int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs; + int64_t nowUs = estimateRealTimeUs(ts, systemTimeUs) - mTimeSourceDeltaUs; - int64_t latenessUs = nowUs - timeUs; + latenessUs = nowUs - timeUs; ATRACE_INT("Video Lateness (ms)", latenessUs / 1E3); @@ -1952,9 +1964,9 @@ void AwesomePlayer::onVideoEvent() { } } - if (latenessUs < -10000) { - // We're more than 10ms early. - postVideoEvent_l(10000); + if (latenessUs < -30000) { + // We're more than 30ms early, schedule at most 20 ms before time due + postVideoEvent_l(latenessUs < -60000 ? 30000 : -latenessUs - 20000); return; } } @@ -1968,6 +1980,8 @@ void AwesomePlayer::onVideoEvent() { if (mVideoRenderer != NULL) { mSinceLastDropped++; + mVideoBuffer->meta_data()->setInt64(kKeyTime, looperTimeUs - latenessUs); + mVideoRenderer->render(mVideoBuffer); if (!mVideoRenderingStarted) { mVideoRenderingStarted = true; @@ -2017,14 +2031,26 @@ void AwesomePlayer::onVideoEvent() { 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); + systemTimeUs = mSystemTimeSource.getRealTimeUs(); + int64_t delayUs = nextTimeUs - estimateRealTimeUs(ts, systemTimeUs) + mTimeSourceDeltaUs; + ATRACE_INT("Frame delta (ms)", (nextTimeUs - timeUs) / 1E3); + ALOGV("next frame in %" PRId64, delayUs); + // try to schedule 30ms before time due + postVideoEvent_l(delayUs > 60000 ? 30000 : (delayUs < 30000 ? 0 : delayUs - 30000)); return; } postVideoEvent_l(); } +int64_t AwesomePlayer::estimateRealTimeUs(TimeSource *ts, int64_t systemTimeUs) { + if (ts == &mSystemTimeSource) { + return systemTimeUs; + } else { + return (int64_t)mClockEstimator->estimate(systemTimeUs, ts->getRealTimeUs()); + } +} + void AwesomePlayer::postVideoEvent_l(int64_t delayUs) { ATRACE_CALL(); diff --git a/media/libstagefright/ClockEstimator.cpp b/media/libstagefright/ClockEstimator.cpp new file mode 100644 index 0000000..34d1e42 --- /dev/null +++ b/media/libstagefright/ClockEstimator.cpp @@ -0,0 +1,177 @@ +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ClockEstimator" +#include <utils/Log.h> + +#include <math.h> +#include <media/stagefright/ClockEstimator.h> + +#include <media/stagefright/foundation/ADebug.h> + +namespace android { + +WindowedLinearFitEstimator::WindowedLinearFitEstimator( + size_t headLength, double headFactor, size_t mainLength, double tailFactor) + : mHeadFactorInv(1. / headFactor), + mTailFactor(tailFactor), + mHistoryLength(mainLength + headLength), + mHeadLength(headLength) { + reset(); + mXHistory.resize(mHistoryLength); + mYHistory.resize(mHistoryLength); + mFirstWeight = pow(headFactor, mHeadLength); +} + +WindowedLinearFitEstimator::LinearFit::LinearFit() { + reset(); +} + +void WindowedLinearFitEstimator::LinearFit::reset() { + mX = mXX = mY = mYY = mXY = mW = 0.; +} + +double WindowedLinearFitEstimator::LinearFit::size() const { + double s = mW * mW + mX * mX + mY * mY + mXX * mXX + mXY * mXY + mYY * mYY; + if (s > 1e72) { + // 1e72 corresponds to clock monotonic time of about 8 years + ALOGW("estimator is overflowing: w=%g x=%g y=%g xx=%g xy=%g yy=%g", + mW, mX, mY, mXX, mXY, mYY); + } + return s; +} + +void WindowedLinearFitEstimator::LinearFit::add(double x, double y, double w) { + mW += w; + mX += w * x; + mY += w * y; + mXX += w * x * x; + mXY += w * x * y; + mYY += w * y * y; +} + +void WindowedLinearFitEstimator::LinearFit::combine(const LinearFit &lf) { + mW += lf.mW; + mX += lf.mX; + mY += lf.mY; + mXX += lf.mXX; + mXY += lf.mXY; + mYY += lf.mYY; +} + +void WindowedLinearFitEstimator::LinearFit::scale(double w) { + mW *= w; + mX *= w; + mY *= w; + mXX *= w; + mXY *= w; + mYY *= w; +} + +double WindowedLinearFitEstimator::LinearFit::interpolate(double x) { + double div = mW * mXX - mX * mX; + if (fabs(div) < 1e-5 * mW * mW) { + // this only should happen on the first value + return x; + // assuming a = 1, we could also return x + (mY - mX) / mW; + } + double a_div = (mW * mXY - mX * mY); + double b_div = (mXX * mY - mX * mXY); + ALOGV("a=%.4g b=%.4g in=%g out=%g", + a_div / div, b_div / div, x, (a_div * x + b_div) / div); + return (a_div * x + b_div) / div; +} + +double WindowedLinearFitEstimator::estimate(double x, double y) { + /* + * TODO: We could update the head by adding the new sample to it + * and amplifying it, but this approach can lead to unbounded + * error. Instead, we recalculate the head at each step, which + * is computationally more expensive. We could balance the two + * methods by recalculating just before the error becomes + * significant. + */ + const bool update_head = false; + if (update_head) { + // add new sample to the head + mHead.scale(mHeadFactorInv); // amplify head + mHead.add(x, y, mFirstWeight); + } + + /* + * TRICKY: place elements into the circular buffer at decreasing + * indices, so that we can access past elements by addition + * (thereby avoiding potentially negative indices.) + */ + if (mNumSamples >= mHeadLength) { + // move last head sample from head to the main window + size_t lastHeadIx = (mSampleIx + mHeadLength) % mHistoryLength; + if (update_head) { + mHead.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], -1.); // remove + } + mMain.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], 1.); + if (mNumSamples >= mHistoryLength) { + // move last main sample from main window to tail + mMain.add(mXHistory[mSampleIx], mYHistory[mSampleIx], -1.); // remove + mTail.add(mXHistory[mSampleIx], mYHistory[mSampleIx], 1.); + mTail.scale(mTailFactor); // attenuate tail + } + } + + mXHistory.editItemAt(mSampleIx) = x; + mYHistory.editItemAt(mSampleIx) = y; + if (mNumSamples < mHistoryLength) { + ++mNumSamples; + } + + // recalculate head unless we were using the update method + if (!update_head) { + mHead.reset(); + double w = mFirstWeight; + for (size_t headIx = 0; headIx < mHeadLength && headIx < mNumSamples; ++headIx) { + size_t ix = (mSampleIx + headIx) % mHistoryLength; + mHead.add(mXHistory[ix], mYHistory[ix], w); + w *= mHeadFactorInv; + } + } + + if (mSampleIx > 0) { + --mSampleIx; + } else { + mSampleIx = mHistoryLength - 1; + } + + // return estimation result + LinearFit total; + total.combine(mHead); + total.combine(mMain); + total.combine(mTail); + return total.interpolate(x); +} + +void WindowedLinearFitEstimator::reset() { + mHead.reset(); + mMain.reset(); + mTail.reset(); + mNumSamples = 0; + mSampleIx = mHistoryLength - 1; +} + +}; // namespace android + + diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 601dccf..5b525f2 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaCodec" #include <utils/Log.h> +#include <inttypes.h> #include <media/stagefright/MediaCodec.h> @@ -323,6 +324,16 @@ status_t MediaCodec::renderOutputBufferAndRelease(size_t index) { return PostAndAwaitResponse(msg, &response); } +status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestampNs) { + sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id()); + msg->setSize("index", index); + msg->setInt32("render", true); + msg->setInt64("timestampNs", timestampNs); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + status_t MediaCodec::releaseOutputBuffer(size_t index) { sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id()); msg->setSize("index", index); @@ -1707,9 +1718,25 @@ status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) { if (render && info->mData != NULL && info->mData->size() != 0) { info->mNotify->setInt32("render", true); + int64_t timestampNs = 0; + if (msg->findInt64("timestampNs", ×tampNs)) { + info->mNotify->setInt64("timestampNs", timestampNs); + } 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 + if (info->mData->meta()->findInt64("timeUs", ×tampNs)) { + ALOGI("using buffer PTS of %" PRId64, timestampNs); + timestampNs *= 1000; + } +#endif + } + if (mSoftRenderer != NULL) { mSoftRenderer->render( - info->mData->data(), info->mData->size(), NULL); + info->mData->data(), info->mData->size(), timestampNs, NULL); } } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index a879656..9a7f3db 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1627,15 +1627,15 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { info.mMediaBuffer = NULL; if (portIndex == kPortIndexOutput) { - if (!(mOMXLivesLocally - && (mQuirks & kRequiresAllocateBufferOnOutputPorts) - && (mQuirks & kDefersOutputBufferAllocation))) { - // If the node does not fill in the buffer ptr at this time, - // we will defer creating the MediaBuffer until receiving - // the first FILL_BUFFER_DONE notification instead. - info.mMediaBuffer = new MediaBuffer(info.mData, info.mSize); - info.mMediaBuffer->setObserver(this); - } + // Fail deferred MediaBuffer creation until FILL_BUFFER_DONE; + // this legacy mode is no longer supported. + LOG_ALWAYS_FATAL_IF((mOMXLivesLocally + && (mQuirks & kRequiresAllocateBufferOnOutputPorts) + && (mQuirks & kDefersOutputBufferAllocation)), + "allocateBuffersOnPort cannot defer buffer allocation"); + + info.mMediaBuffer = new MediaBuffer(info.mData, info.mSize); + info.mMediaBuffer->setObserver(this); } mPortBuffers[portIndex].push(info); @@ -2234,22 +2234,6 @@ void OMXCodec::on_message(const omx_message &msg) { } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) { CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED); - if (info->mMediaBuffer == NULL) { - CHECK(mOMXLivesLocally); - CHECK(mQuirks & kRequiresAllocateBufferOnOutputPorts); - CHECK(mQuirks & kDefersOutputBufferAllocation); - - // The qcom video decoders on Nexus don't actually allocate - // output buffer memory on a call to OMX_AllocateBuffer - // the "pBuffer" member of the OMX_BUFFERHEADERTYPE - // structure is only filled in later. - - info->mMediaBuffer = new MediaBuffer( - msg.u.extended_buffer_data.data_ptr, - info->mSize); - info->mMediaBuffer->setObserver(this); - } - MediaBuffer *buffer = info->mMediaBuffer; bool isGraphicBuffer = buffer->graphicBuffer() != NULL; @@ -2285,10 +2269,6 @@ void OMXCodec::on_message(const omx_message &msg) { } buffer->meta_data()->setPointer( - kKeyPlatformPrivate, - msg.u.extended_buffer_data.platform_private); - - buffer->meta_data()->setPointer( kKeyBufferID, msg.u.extended_buffer_data.buffer); diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index b3a6bcc..dc38ea8 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -27,7 +27,6 @@ namespace android { - template<class T> static void InitOMXParams(T *params) { params->nSize = sizeof(T); @@ -148,10 +147,20 @@ SoftVPXEncoder::SoftVPXEncoder(const char *name, mErrorResilience(OMX_FALSE), mColorFormat(OMX_COLOR_FormatYUV420Planar), mLevel(OMX_VIDEO_VP8Level_Version0), + mKeyFrameInterval(0), + mMinQuantizer(0), + mMaxQuantizer(0), + mTemporalLayers(0), + mTemporalPatternType(OMX_VIDEO_VPXTemporalLayerPatternNone), + mTemporalPatternLength(0), + mTemporalPatternIdx(0), + mLastTimestamp(0x7FFFFFFFFFFFFFFFLL), mConversionBuffer(NULL), mInputDataIsMeta(false), mGrallocModule(NULL), mKeyFrameRequested(false) { + memset(mTemporalLayerBitrateRatio, 0, sizeof(mTemporalLayerBitrateRatio)); + mTemporalLayerBitrateRatio[0] = 100; initPorts(); } @@ -235,7 +244,9 @@ status_t SoftVPXEncoder::initEncoder() { if (mCodecInterface == NULL) { return UNKNOWN_ERROR; } - + ALOGD("VP8: initEncoder. BRMode: %u. TSLayers: %zu. KF: %u. QP: %u - %u", + (uint32_t)mBitrateControlMode, mTemporalLayers, mKeyFrameInterval, + mMinQuantizer, mMaxQuantizer); codec_return = vpx_codec_enc_config_default(mCodecInterface, mCodecConfiguration, 0); // Codec specific flags @@ -276,7 +287,7 @@ status_t SoftVPXEncoder::initEncoder() { mCodecConfiguration->g_timebase.num = 1; mCodecConfiguration->g_timebase.den = 1000000; // rc_target_bitrate is in kbps, mBitrate in bps - mCodecConfiguration->rc_target_bitrate = mBitrate / 1000; + mCodecConfiguration->rc_target_bitrate = (mBitrate + 500) / 1000; mCodecConfiguration->rc_end_usage = mBitrateControlMode; // Disable frame drop - not allowed in MediaCodec now. mCodecConfiguration->rc_dropframe_thresh = 0; @@ -285,10 +296,6 @@ status_t SoftVPXEncoder::initEncoder() { mCodecConfiguration->rc_resize_allowed = 0; // Single-pass mode. mCodecConfiguration->g_pass = VPX_RC_ONE_PASS; - // Minimum quantization level. - mCodecConfiguration->rc_min_quantizer = 2; - // Maximum quantization level. - mCodecConfiguration->rc_max_quantizer = 63; // Maximum amount of bits that can be subtracted from the target // bitrate - expressed as percentage of the target bitrate. mCodecConfiguration->rc_undershoot_pct = 100; @@ -306,10 +313,95 @@ status_t SoftVPXEncoder::initEncoder() { mCodecConfiguration->g_error_resilient = 1; // Disable lagged encoding. mCodecConfiguration->g_lag_in_frames = 0; + // Maximum key frame interval - for CBR boost to 3000 + mCodecConfiguration->kf_max_dist = 3000; // Encoder determines optimal key frame placement automatically. mCodecConfiguration->kf_mode = VPX_KF_AUTO; } + // Frames temporal pattern - for now WebRTC like pattern is only supported. + switch (mTemporalLayers) { + case 0: + { + mTemporalPatternLength = 0; + break; + } + case 1: + { + mCodecConfiguration->ts_number_layers = 1; + mCodecConfiguration->ts_rate_decimator[0] = 1; + mCodecConfiguration->ts_periodicity = 1; + mCodecConfiguration->ts_layer_id[0] = 0; + mTemporalPattern[0] = kTemporalUpdateLastRefAll; + mTemporalPatternLength = 1; + break; + } + case 2: + { + mCodecConfiguration->ts_number_layers = 2; + mCodecConfiguration->ts_rate_decimator[0] = 2; + mCodecConfiguration->ts_rate_decimator[1] = 1; + mCodecConfiguration->ts_periodicity = 2; + mCodecConfiguration->ts_layer_id[0] = 0; + mCodecConfiguration->ts_layer_id[1] = 1; + mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef; + mTemporalPattern[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef; + mTemporalPattern[2] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[3] = kTemporalUpdateGoldenRefAltRef; + mTemporalPattern[4] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef; + mTemporalPattern[6] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[7] = kTemporalUpdateNone; + mTemporalPatternLength = 8; + break; + } + case 3: + { + mCodecConfiguration->ts_number_layers = 3; + mCodecConfiguration->ts_rate_decimator[0] = 4; + mCodecConfiguration->ts_rate_decimator[1] = 2; + mCodecConfiguration->ts_rate_decimator[2] = 1; + mCodecConfiguration->ts_periodicity = 4; + mCodecConfiguration->ts_layer_id[0] = 0; + mCodecConfiguration->ts_layer_id[1] = 2; + mCodecConfiguration->ts_layer_id[2] = 1; + mCodecConfiguration->ts_layer_id[3] = 2; + mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef; + mTemporalPattern[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef; + mTemporalPattern[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef; + mTemporalPattern[3] = kTemporalUpdateNone; + mTemporalPattern[4] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[5] = kTemporalUpdateNone; + mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef; + mTemporalPattern[7] = kTemporalUpdateNone; + mTemporalPatternLength = 8; + break; + } + default: + { + ALOGE("Wrong number of temporal layers %u", mTemporalLayers); + return UNKNOWN_ERROR; + } + } + + // Set bitrate values for each layer + for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) { + mCodecConfiguration->ts_target_bitrate[i] = + mCodecConfiguration->rc_target_bitrate * + mTemporalLayerBitrateRatio[i] / 100; + } + if (mKeyFrameInterval > 0) { + mCodecConfiguration->kf_max_dist = mKeyFrameInterval; + mCodecConfiguration->kf_min_dist = mKeyFrameInterval; + mCodecConfiguration->kf_mode = VPX_KF_AUTO; + } + if (mMinQuantizer > 0) { + mCodecConfiguration->rc_min_quantizer = mMinQuantizer; + } + if (mMaxQuantizer > 0) { + mCodecConfiguration->rc_max_quantizer = mMaxQuantizer; + } + codec_return = vpx_codec_enc_init(mCodecContext, mCodecInterface, mCodecConfiguration, @@ -466,6 +558,24 @@ OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index, return OMX_ErrorNone; } + case OMX_IndexParamVideoAndroidVp8Encoder: { + OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *vp8AndroidParams = + (OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *)param; + + if (vp8AndroidParams->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + vp8AndroidParams->nKeyFrameInterval = mKeyFrameInterval; + vp8AndroidParams->eTemporalPattern = mTemporalPatternType; + vp8AndroidParams->nTemporalLayerCount = mTemporalLayers; + vp8AndroidParams->nMinQuantizer = mMinQuantizer; + vp8AndroidParams->nMaxQuantizer = mMaxQuantizer; + memcpy(vp8AndroidParams->nTemporalLayerBitrateRatio, + mTemporalLayerBitrateRatio, sizeof(mTemporalLayerBitrateRatio)); + return OMX_ErrorNone; + } + case OMX_IndexParamVideoProfileLevelQuerySupported: { OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel = (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param; @@ -552,11 +662,15 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, return internalSetVp8Params( (const OMX_VIDEO_PARAM_VP8TYPE *)param); + case OMX_IndexParamVideoAndroidVp8Encoder: + return internalSetAndroidVp8Params( + (const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *)param); + case OMX_IndexParamVideoProfileLevelCurrent: return internalSetProfileLevel( (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param); - case OMX_IndexVendorStartUnused: + case kStoreMetaDataExtensionIndex: { // storeMetaDataInBuffers const StoreMetaDataInBuffersParams *storeParam = @@ -665,6 +779,50 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetVp8Params( return OMX_ErrorNone; } +OMX_ERRORTYPE SoftVPXEncoder::internalSetAndroidVp8Params( + const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE* vp8AndroidParams) { + if (vp8AndroidParams->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + if (vp8AndroidParams->eTemporalPattern != OMX_VIDEO_VPXTemporalLayerPatternNone && + vp8AndroidParams->eTemporalPattern != OMX_VIDEO_VPXTemporalLayerPatternWebRTC) { + return OMX_ErrorBadParameter; + } + if (vp8AndroidParams->nTemporalLayerCount > OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS) { + return OMX_ErrorBadParameter; + } + if (vp8AndroidParams->nMinQuantizer > vp8AndroidParams->nMaxQuantizer) { + return OMX_ErrorBadParameter; + } + + mTemporalPatternType = vp8AndroidParams->eTemporalPattern; + if (vp8AndroidParams->eTemporalPattern == OMX_VIDEO_VPXTemporalLayerPatternWebRTC) { + mTemporalLayers = vp8AndroidParams->nTemporalLayerCount; + } else if (vp8AndroidParams->eTemporalPattern == OMX_VIDEO_VPXTemporalLayerPatternNone) { + mTemporalLayers = 0; + } + // Check the bitrate distribution between layers is in increasing order + if (mTemporalLayers > 1) { + for (size_t i = 0; i < mTemporalLayers - 1; i++) { + if (vp8AndroidParams->nTemporalLayerBitrateRatio[i + 1] <= + vp8AndroidParams->nTemporalLayerBitrateRatio[i]) { + ALOGE("Wrong bitrate ratio - should be in increasing order."); + return OMX_ErrorBadParameter; + } + } + } + mKeyFrameInterval = vp8AndroidParams->nKeyFrameInterval; + mMinQuantizer = vp8AndroidParams->nMinQuantizer; + mMaxQuantizer = vp8AndroidParams->nMaxQuantizer; + memcpy(mTemporalLayerBitrateRatio, vp8AndroidParams->nTemporalLayerBitrateRatio, + sizeof(mTemporalLayerBitrateRatio)); + ALOGD("VP8: internalSetAndroidVp8Params. BRMode: %u. TS: %zu. KF: %u." + " QP: %u - %u BR0: %u. BR1: %u. BR2: %u", + (uint32_t)mBitrateControlMode, mTemporalLayers, mKeyFrameInterval, + mMinQuantizer, mMaxQuantizer, mTemporalLayerBitrateRatio[0], + mTemporalLayerBitrateRatio[1], mTemporalLayerBitrateRatio[2]); + return OMX_ErrorNone; +} OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams( const OMX_VIDEO_PARAM_PORTFORMATTYPE* format) { @@ -728,7 +886,7 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams( OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef; def->format.video.nFrameWidth = mWidth; def->format.video.nFrameHeight = mHeight; - def->format.video.xFramerate = port->format.video.xFramerate; + def->format.video.xFramerate = mFramerate; def->format.video.eColorFormat = mColorFormat; def = &editPortInfo(kOutputPortIndex)->mDef; def->format.video.nFrameWidth = mWidth; @@ -770,6 +928,74 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetBitrateParams( return OMX_ErrorNone; } +vpx_enc_frame_flags_t SoftVPXEncoder::getEncodeFlags() { + vpx_enc_frame_flags_t flags = 0; + int patternIdx = mTemporalPatternIdx % mTemporalPatternLength; + mTemporalPatternIdx++; + switch (mTemporalPattern[patternIdx]) { + case kTemporalUpdateLast: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + flags |= VP8_EFLAG_NO_REF_ARF; + break; + case kTemporalUpdateGoldenWithoutDependency: + flags |= VP8_EFLAG_NO_REF_GF; + // Deliberately no break here. + case kTemporalUpdateGolden: + flags |= VP8_EFLAG_NO_REF_ARF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateAltrefWithoutDependency: + flags |= VP8_EFLAG_NO_REF_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + // Deliberately no break here. + case kTemporalUpdateAltref: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateNoneNoRefAltref: + flags |= VP8_EFLAG_NO_REF_ARF; + // Deliberately no break here. + case kTemporalUpdateNone: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + flags |= VP8_EFLAG_NO_UPD_ENTROPY; + break; + case kTemporalUpdateNoneNoRefGoldenRefAltRef: + flags |= VP8_EFLAG_NO_REF_GF; + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + flags |= VP8_EFLAG_NO_UPD_ENTROPY; + break; + case kTemporalUpdateGoldenWithoutDependencyRefAltRef: + flags |= VP8_EFLAG_NO_REF_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateLastRefAltRef: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + break; + case kTemporalUpdateGoldenRefAltRef: + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateLastAndGoldenRefAltRef: + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + break; + case kTemporalUpdateLastRefAll: + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_GF; + break; + } + return flags; +} void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { // Initialize encoder if not already @@ -854,6 +1080,9 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { kInputBufferAlignment, source); vpx_enc_frame_flags_t flags = 0; + if (mTemporalPatternLength > 0) { + flags = getEncodeFlags(); + } if (mKeyFrameRequested) { flags |= VPX_EFLAG_FORCE_KF; mKeyFrameRequested = false; @@ -874,7 +1103,13 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { mBitrateUpdated = false; } - uint32_t frameDuration = (uint32_t)(((uint64_t)1000000 << 16) / mFramerate); + uint32_t frameDuration; + if (inputBufferHeader->nTimeStamp > mLastTimestamp) { + frameDuration = (uint32_t)(inputBufferHeader->nTimeStamp - mLastTimestamp); + } else { + frameDuration = (uint32_t)(((uint64_t)1000000 << 16) / mFramerate); + } + mLastTimestamp = inputBufferHeader->nTimeStamp; codec_return = vpx_codec_encode( mCodecContext, &raw_frame, @@ -921,10 +1156,9 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { OMX_ERRORTYPE SoftVPXEncoder::getExtensionIndex( const char *name, OMX_INDEXTYPE *index) { if (!strcmp(name, "OMX.google.android.index.storeMetaDataInBuffers")) { - *index = OMX_IndexVendorStartUnused; + *(int32_t*)index = kStoreMetaDataExtensionIndex; return OMX_ErrorNone; } - return SimpleSoftOMXComponent::getExtensionIndex(name, index); } diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index 1c983ab..c5a83d1 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -91,6 +91,47 @@ protected: const char *name, OMX_INDEXTYPE *index); private: + enum { + kStoreMetaDataExtensionIndex = OMX_IndexVendorStartUnused + 1, + }; + + enum TemporalReferences { + // For 1 layer case: reference all (last, golden, and alt ref), but only + // update last. + kTemporalUpdateLastRefAll = 12, + // First base layer frame for 3 temporal layers, which updates last and + // golden with alt ref dependency. + kTemporalUpdateLastAndGoldenRefAltRef = 11, + // First enhancement layer with alt ref dependency. + kTemporalUpdateGoldenRefAltRef = 10, + // First enhancement layer with alt ref dependency. + kTemporalUpdateGoldenWithoutDependencyRefAltRef = 9, + // Base layer with alt ref dependency. + kTemporalUpdateLastRefAltRef = 8, + // Highest enhacement layer without dependency on golden with alt ref + // dependency. + kTemporalUpdateNoneNoRefGoldenRefAltRef = 7, + // Second layer and last frame in cycle, for 2 layers. + kTemporalUpdateNoneNoRefAltref = 6, + // Highest enhancement layer. + kTemporalUpdateNone = 5, + // Second enhancement layer. + kTemporalUpdateAltref = 4, + // Second enhancement layer without dependency on previous frames in + // the second enhancement layer. + kTemporalUpdateAltrefWithoutDependency = 3, + // First enhancement layer. + kTemporalUpdateGolden = 2, + // First enhancement layer without dependency on previous frames in + // the first enhancement layer. + kTemporalUpdateGoldenWithoutDependency = 1, + // Base layer. + kTemporalUpdateLast = 0, + }; + enum { + kMaxTemporalPattern = 8 + }; + // number of buffers allocated per port static const uint32_t kNumBuffers = 4; @@ -159,6 +200,36 @@ private: // something else. OMX_VIDEO_VP8LEVELTYPE mLevel; + // Key frame interval in frames + uint32_t mKeyFrameInterval; + + // Minimum (best quality) quantizer + uint32_t mMinQuantizer; + + // Maximum (worst quality) quantizer + uint32_t mMaxQuantizer; + + // Number of coding temporal layers to be used. + size_t mTemporalLayers; + + // Temporal layer bitrare ratio in percentage + uint32_t mTemporalLayerBitrateRatio[OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS]; + + // Temporal pattern type + OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE mTemporalPatternType; + + // Temporal pattern length + size_t mTemporalPatternLength; + + // Temporal pattern current index + size_t mTemporalPatternIdx; + + // Frame type temporal pattern + TemporalReferences mTemporalPattern[kMaxTemporalPattern]; + + // Last input buffer timestamp + OMX_TICKS mLastTimestamp; + // Conversion buffer is needed to convert semi // planar yuv420 to planar format // It is only allocated if input format is @@ -184,6 +255,9 @@ private: // dtor. status_t releaseEncoder(); + // Get current encode flags + vpx_enc_frame_flags_t getEncodeFlags(); + // Handles port changes with respect to color formats OMX_ERRORTYPE internalSetFormatParams( const OMX_VIDEO_PARAM_PORTFORMATTYPE* format); @@ -205,6 +279,10 @@ private: OMX_ERRORTYPE internalSetVp8Params( const OMX_VIDEO_PARAM_VP8TYPE* vp8Params); + // Handles Android vp8 specific parameters. + OMX_ERRORTYPE internalSetAndroidVp8Params( + const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE* vp8AndroidParams); + // Updates encoder profile OMX_ERRORTYPE internalSetProfileLevel( const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel); diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp index 515e4d3..8f356b6 100644 --- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -355,7 +355,12 @@ void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { outHeader->nFlags = 0; int err = vorbis_dsp_synthesis(mState, &pack, 1); if (err != 0) { + // FIXME temporary workaround for log spam +#if !defined(__arm__) && !defined(__aarch64__) + ALOGV("vorbis_dsp_synthesis returned %d", err); +#else ALOGW("vorbis_dsp_synthesis returned %d", err); +#endif } else { numFrames = vorbis_dsp_pcmout( mState, (int16_t *)outHeader->pBuffer, diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index 77f21b7..67dfcd2 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -138,7 +138,7 @@ static int ALIGN(int x, int y) { } void SoftwareRenderer::render( - const void *data, size_t size, void *platformPrivate) { + const void *data, size_t size, int64_t timestampNs, void *platformPrivate) { ANativeWindowBuffer *buf; int err; if ((err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), @@ -230,6 +230,11 @@ void SoftwareRenderer::render( CHECK_EQ(0, mapper.unlock(buf->handle)); + if ((err = native_window_set_buffers_timestamp(mNativeWindow.get(), + timestampNs)) != 0) { + ALOGW("Surface::set_buffers_timestamp returned error %d", err); + } + if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf, -1)) != 0) { ALOGW("Surface::queueBuffer returned error %d", err); diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp index fcd825f..f2d501e 100644 --- a/media/libstagefright/foundation/AString.cpp +++ b/media/libstagefright/foundation/AString.cpp @@ -197,64 +197,64 @@ void AString::append(const AString &from, size_t offset, size_t n) { void AString::append(int x) { char s[16]; - sprintf(s, "%d", x); - + int result = snprintf(s, sizeof(s), "%d", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(unsigned x) { char s[16]; - sprintf(s, "%u", x); - + int result = snprintf(s, sizeof(s), "%u", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(long x) { - char s[16]; - sprintf(s, "%ld", x); - + char s[32]; + int result = snprintf(s, sizeof(s), "%ld", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(unsigned long x) { - char s[16]; - sprintf(s, "%lu", x); - + char s[32]; + int result = snprintf(s, sizeof(s), "%lu", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(long long x) { char s[32]; - sprintf(s, "%lld", x); - + int result = snprintf(s, sizeof(s), "%lld", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(unsigned long long x) { char s[32]; - sprintf(s, "%llu", x); - + int result = snprintf(s, sizeof(s), "%llu", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(float x) { char s[16]; - sprintf(s, "%f", x); - + int result = snprintf(s, sizeof(s), "%f", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(double x) { char s[16]; - sprintf(s, "%f", x); - + int result = snprintf(s, sizeof(s), "%f", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } void AString::append(void *x) { - char s[16]; - sprintf(s, "%p", x); - + char s[32]; + int result = snprintf(s, sizeof(s), "%p", x); + CHECK((result > 0) && ((size_t) result) < sizeof(s)); append(s); } diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 07a3ed0..326d85b 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -917,6 +917,7 @@ void PlaylistFetcher::onDownloadNext() { if (err == -EAGAIN) { // bad starting sequence number hint + mTSParser.clear(); postMonitorQueue(); return; } @@ -1201,9 +1202,35 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu return OK; } +/* static */ +bool PlaylistFetcher::bufferStartsWithWebVTTMagicSequence( + const sp<ABuffer> &buffer) { + size_t pos = 0; + + // skip possible BOM + if (buffer->size() >= pos + 3 && + !memcmp("\xef\xbb\xbf", buffer->data() + pos, 3)) { + pos += 3; + } + + // accept WEBVTT followed by SPACE, TAB or (CR) LF + if (buffer->size() < pos + 6 || + memcmp("WEBVTT", buffer->data() + pos, 6)) { + return false; + } + pos += 6; + + if (buffer->size() == pos) { + return true; + } + + uint8_t sep = buffer->data()[pos]; + return sep == ' ' || sep == '\t' || sep == '\n' || sep == '\r'; +} + status_t PlaylistFetcher::extractAndQueueAccessUnits( const sp<ABuffer> &buffer, const sp<AMessage> &itemMeta) { - if (buffer->size() >= 7 && !memcmp("WEBVTT\n", buffer->data(), 7)) { + if (bufferStartsWithWebVTTMagicSequence(buffer)) { if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES) { ALOGE("This stream only contains subtitles."); return ERROR_MALFORMED; diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h index e0ed11d..e4fdbff 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.h +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -91,6 +91,7 @@ private: static const int32_t kNumSkipFrames; static bool bufferStartsWithTsSyncByte(const sp<ABuffer>& buffer); + static bool bufferStartsWithWebVTTMagicSequence(const sp<ABuffer>& buffer); // notifications to mSession sp<AMessage> mNotify; diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index a81bbba..77d65e0 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -32,6 +32,7 @@ namespace android { struct AudioPlayer; +struct ClockEstimator; struct DataSource; struct MediaBuffer; struct MediaExtractor; @@ -236,6 +237,7 @@ private: MediaBuffer *mVideoBuffer; + sp<ClockEstimator> mClockEstimator; sp<HTTPBase> mConnectingDataSource; sp<NuCachedSource2> mCachedSource; @@ -296,6 +298,7 @@ private: bool getBitrate(int64_t *bitrate); + int64_t estimateRealTimeUs(TimeSource *ts, int64_t systemTimeUs); void finishSeekIfNecessary(int64_t videoTimeUs); void ensureCacheIsFetching_l(); diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h index 7ab0042..0ba670c 100644 --- a/media/libstagefright/include/SoftwareRenderer.h +++ b/media/libstagefright/include/SoftwareRenderer.h @@ -34,7 +34,7 @@ public: ~SoftwareRenderer(); void render( - const void *data, size_t size, void *platformPrivate); + const void *data, size_t size, int64_t timestampNs, void *platformPrivate); private: enum YUVMode { diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index 3df57b4..16f6c58 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -29,6 +29,8 @@ #include <media/hardware/MetadataBufferType.h> #include <ui/GraphicBuffer.h> +#include <inttypes.h> + namespace android { static const bool EXTRA_CHECK = true; @@ -763,13 +765,13 @@ void GraphicBufferSource::onFrameAvailable() { void GraphicBufferSource::onBuffersReleased() { Mutex::Autolock lock(mMutex); - uint32_t slotMask; + uint64_t slotMask; if (mConsumer->getReleasedBuffers(&slotMask) != NO_ERROR) { ALOGW("onBuffersReleased: unable to get released buffer set"); - slotMask = 0xffffffff; + slotMask = 0xffffffffffffffffULL; } - ALOGV("onBuffersReleased: 0x%08x", slotMask); + ALOGV("onBuffersReleased: 0x%016" PRIx64, slotMask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if ((slotMask & 0x01) != 0) { diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 74076c6..b62d5f5 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -470,8 +470,6 @@ OMX_ERRORTYPE OMX::OnFillBufferDone( msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen; msg.u.extended_buffer_data.flags = pBuffer->nFlags; msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp; - msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate; - msg.u.extended_buffer_data.data_ptr = pBuffer->pBuffer; findDispatcher(node)->post(msg); diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk new file mode 100644 index 0000000..b8dd19e --- /dev/null +++ b/media/ndk/Android.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2014 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. +# + +LOCAL_PATH:= $(call my-dir) + +ifneq ($(TARGET_BUILD_PDK), true) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + NdkMediaCodec.cpp \ + NdkMediaExtractor.cpp \ + NdkMediaFormat.cpp \ + NdkMediaMuxer.cpp \ + +LOCAL_MODULE:= libmediandk + +LOCAL_C_INCLUDES := \ + bionic/libc/private \ + frameworks/base/core/jni \ + frameworks/av/include/ndk + +LOCAL_SHARED_LIBRARIES := \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + liblog \ + libutils \ + libandroid_runtime \ + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp new file mode 100644 index 0000000..9592af8 --- /dev/null +++ b/media/ndk/NdkMediaCodec.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "NdkMediaCodec" + +#include "NdkMediaCodec.h" +#include "NdkMediaFormatPriv.h" + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <gui/Surface.h> + +#include <media/ICrypto.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ABuffer.h> + +#include <media/stagefright/MediaCodec.h> +#include <media/stagefright/MediaErrors.h> + +using namespace android; + + +static int translate_error(status_t err) { + if (err == OK) { + return OK; + } else if (err == -EAGAIN) { + return AMEDIACODEC_INFO_TRY_AGAIN_LATER; + } + ALOGE("sf error code: %d", err); + return -1000; +} + + +class CodecHandler: public AHandler { +public: + CodecHandler(sp<android::MediaCodec>); + virtual void onMessageReceived(const sp<AMessage> &msg); +}; + +CodecHandler::CodecHandler(sp<android::MediaCodec>) { + +} + +void CodecHandler::onMessageReceived(const sp<AMessage> &msg) { + ALOGI("handler got message %d", msg->what()); +} + +struct AMediaCodec { + sp<android::MediaCodec> mCodec; + sp<ALooper> mLooper; + sp<CodecHandler> mHandler; +}; + +extern "C" { + +static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) { + AMediaCodec *mData = new AMediaCodec(); + mData->mLooper = new ALooper; + mData->mLooper->setName("NDK MediaCodec_looper"); + status_t ret = mData->mLooper->start( + false, // runOnCallingThread + true, // canCallJava XXX + PRIORITY_FOREGROUND); + ALOGV("looper start: %d", ret); + if (name_is_type) { + mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder); + } else { + mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name); + } + mData->mHandler = new CodecHandler(mData->mCodec); + mData->mLooper->registerHandler(mData->mHandler); + return mData; +} + + +AMediaCodec* AMediaCodec_createCodecByName(const char *name) { + return createAMediaCodec(name, false, false); +} + +AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) { + return createAMediaCodec(mime_type, true, false); +} + +AMediaCodec* AMediaCodec_createEncoderByType(const char *name) { + return createAMediaCodec(name, true, true); +} + +int AMediaCodec_delete(AMediaCodec *mData) { + if (mData->mCodec != NULL) { + mData->mCodec->release(); + mData->mCodec.clear(); + } + + if (mData->mLooper != NULL) { + mData->mLooper->unregisterHandler(mData->mHandler->id()); + mData->mLooper->stop(); + mData->mLooper.clear(); + } + delete mData; + return OK; +} + +int AMediaCodec_configure( + AMediaCodec *mData, const AMediaFormat* format, ANativeWindow* window, uint32_t flags) { + sp<AMessage> nativeFormat; + AMediaFormat_getFormat(format, &nativeFormat); + ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str()); + sp<Surface> surface = NULL; + if (window != NULL) { + surface = (Surface*) window; + } + + return translate_error(mData->mCodec->configure(nativeFormat, surface, NULL, flags)); +} + +int AMediaCodec_start(AMediaCodec *mData) { + return translate_error(mData->mCodec->start()); +} + +int AMediaCodec_stop(AMediaCodec *mData) { + return translate_error(mData->mCodec->stop()); +} + +int AMediaCodec_flush(AMediaCodec *mData) { + return translate_error(mData->mCodec->flush()); +} + +ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) { + size_t idx; + status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs); + if (ret == OK) { + return idx; + } + return translate_error(ret); +} + +uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) { + android::Vector<android::sp<android::ABuffer> > abufs; + if (mData->mCodec->getInputBuffers(&abufs) == 0) { + size_t n = abufs.size(); + if (idx >= n) { + ALOGE("buffer index %d out of range", idx); + return NULL; + } + if (out_size != NULL) { + *out_size = abufs[idx]->capacity(); + } + return abufs[idx]->data(); + } + ALOGE("couldn't get input buffers"); + return NULL; +} + +uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) { + android::Vector<android::sp<android::ABuffer> > abufs; + if (mData->mCodec->getOutputBuffers(&abufs) == 0) { + size_t n = abufs.size(); + if (idx >= n) { + ALOGE("buffer index %d out of range", idx); + return NULL; + } + if (out_size != NULL) { + *out_size = abufs[idx]->capacity(); + } + return abufs[idx]->data(); + } + ALOGE("couldn't get output buffers"); + return NULL; +} + +int AMediaCodec_queueInputBuffer(AMediaCodec *mData, + size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) { + + AString errorMsg; + status_t ret = mData->mCodec->queueInputBuffer(idx, offset, size, time, flags, &errorMsg); + return translate_error(ret); +} + +ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData, + AMediaCodecBufferInfo *info, int64_t timeoutUs) { + size_t idx; + size_t offset; + size_t size; + uint32_t flags; + int64_t presentationTimeUs; + status_t ret = mData->mCodec->dequeueOutputBuffer(&idx, &offset, &size, &presentationTimeUs, + &flags, timeoutUs); + + switch (ret) { + case OK: + info->offset = offset; + info->size = size; + info->flags = flags; + info->presentationTimeUs = presentationTimeUs; + return idx; + case -EAGAIN: + return AMEDIACODEC_INFO_TRY_AGAIN_LATER; + case android::INFO_FORMAT_CHANGED: + return AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED; + case INFO_OUTPUT_BUFFERS_CHANGED: + return AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED; + default: + break; + } + return translate_error(ret); +} + +AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) { + sp<AMessage> format; + mData->mCodec->getOutputFormat(&format); + return AMediaFormat_fromMsg(&format); +} + +int AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) { + if (render) { + return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx)); + } else { + return translate_error(mData->mCodec->releaseOutputBuffer(idx)); + } +} + +} // extern "C" + diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp new file mode 100644 index 0000000..681633a --- /dev/null +++ b/media/ndk/NdkMediaExtractor.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "NdkMediaExtractor" + + +#include "NdkMediaExtractor.h" +#include "NdkMediaFormatPriv.h" + + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/NuMediaExtractor.h> +#include <media/IMediaHTTPService.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_util_Binder.h> + +#include <jni.h> + +using namespace android; + +static int translate_error(status_t err) { + if (err == OK) { + return OK; + } + ALOGE("sf error code: %d", err); + return -1000; +} + +struct AMediaExtractor { + sp<NuMediaExtractor> mImpl; + +}; + +extern "C" { + +AMediaExtractor* AMediaExtractor_new() { + ALOGV("ctor"); + AMediaExtractor *mData = new AMediaExtractor(); + mData->mImpl = new NuMediaExtractor(); + return mData; +} + +int AMediaExtractor_delete(AMediaExtractor *mData) { + ALOGV("dtor"); + delete mData; + return OK; +} + +int AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, off64_t length) { + ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + mData->mImpl->setDataSource(fd, offset, length); + return 0; +} + +int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) { + ALOGV("setDataSource(%s)", location); + // TODO: add header support + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jobject service = NULL; + if (env == NULL) { + ALOGE("setDataSource(path) must be called from Java thread"); + env->ExceptionClear(); + return -1; + } + + jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService"); + if (mediahttpclass == NULL) { + ALOGE("can't find MediaHttpService"); + env->ExceptionClear(); + return -1; + } + + jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass, + "createHttpServiceBinderIfNecessary", "(Ljava/lang/String;)Landroid/os/IBinder;"); + if (mediaHttpCreateMethod == NULL) { + ALOGE("can't find method"); + env->ExceptionClear(); + return -1; + } + + jstring jloc = env->NewStringUTF(location); + + service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc); + env->DeleteLocalRef(jloc); + + sp<IMediaHTTPService> httpService; + if (service != NULL) { + sp<IBinder> binder = ibinderForJavaObject(env, service); + httpService = interface_cast<IMediaHTTPService>(binder); + } + + mData->mImpl->setDataSource(httpService, location, NULL); + env->ExceptionClear(); + return 0; +} + +int AMediaExtractor_getTrackCount(AMediaExtractor *mData) { + return mData->mImpl->countTracks(); +} + +AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) { + sp<AMessage> format; + mData->mImpl->getTrackFormat(idx, &format); + return AMediaFormat_fromMsg(&format); +} + +int AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) { + ALOGV("selectTrack(%z)", idx); + return translate_error(mData->mImpl->selectTrack(idx)); +} + +int AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) { + ALOGV("unselectTrack(%z)", idx); + return translate_error(mData->mImpl->unselectTrack(idx)); +} + +bool AMediaExtractor_advance(AMediaExtractor *mData) { + //ALOGV("advance"); + return mData->mImpl->advance(); +} + +int AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) { + //ALOGV("readSampleData"); + sp<ABuffer> tmp = new ABuffer(buffer, capacity); + if (mData->mImpl->readSampleData(tmp) == OK) { + return tmp->size(); + } + return -1; +} + +int AMediaExtractor_getSampleFlags(AMediaExtractor *mData) { + int sampleFlags = 0; + sp<MetaData> meta; + status_t err = mData->mImpl->getSampleMeta(&meta); + if (err != OK) { + return -1; + } + int32_t val; + if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) { + sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_SYNC; + } + + uint32_t type; + const void *data; + size_t size; + if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) { + sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED; + } + return sampleFlags; +} + +int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) { + size_t idx; + if (mData->mImpl->getSampleTrackIndex(&idx) != OK) { + return -1; + } + return idx; +} + +int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) { + int64_t time; + if (mData->mImpl->getSampleTime(&time) != OK) { + return -1; + } + return time; +} + + +} // extern "C" + diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp new file mode 100644 index 0000000..c08814f --- /dev/null +++ b/media/ndk/NdkMediaFormat.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "NdkMediaFormat" + + +#include "NdkMediaFormat.h" + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_util_Binder.h> + +#include <jni.h> + +using namespace android; + +struct AMediaFormat { + sp<AMessage> mFormat; + String8 mDebug; + KeyedVector<String8, String8> mStringCache; +}; + +extern "C" { + +// private functions for conversion to/from AMessage +AMediaFormat* AMediaFormat_fromMsg(const void* data) { + ALOGV("private ctor"); + AMediaFormat* mData = new AMediaFormat(); + mData->mFormat = *((sp<AMessage>*)data); + return mData; +} + +void AMediaFormat_getFormat(const AMediaFormat* mData, void* dest) { + *((sp<AMessage>*)dest) = mData->mFormat; +} + + +/* + * public function follow + */ +AMediaFormat *AMediaFormat_new() { + ALOGV("ctor"); + sp<AMessage> msg = new AMessage(); + return AMediaFormat_fromMsg(&msg); +} + +int AMediaFormat_delete(AMediaFormat *mData) { + ALOGV("dtor"); + delete mData; + return OK; +} + + +const char* AMediaFormat_toString(AMediaFormat *mData) { + sp<AMessage> f = mData->mFormat; + String8 ret; + int num = f->countEntries(); + for (int i = 0; i < num; i++) { + if (i != 0) { + ret.append(", "); + } + AMessage::Type t; + const char *name = f->getEntryNameAt(i, &t); + ret.append(name); + ret.append(": "); + switch (t) { + case AMessage::kTypeInt32: + { + int32_t val; + f->findInt32(name, &val); + ret.appendFormat("int32(%d)", val); + break; + } + case AMessage::kTypeInt64: + { + int64_t val; + f->findInt64(name, &val); + ret.appendFormat("int64(%lld)", val); + break; + } + case AMessage::kTypeSize: + { + size_t val; + f->findSize(name, &val); + ret.appendFormat("size_t(%d)", val); + break; + } + case AMessage::kTypeFloat: + { + float val; + f->findFloat(name, &val); + ret.appendFormat("float(%f)", val); + break; + } + case AMessage::kTypeDouble: + { + double val; + f->findDouble(name, &val); + ret.appendFormat("double(%f)", val); + break; + } + case AMessage::kTypeString: + { + AString val; + f->findString(name, &val); + ret.appendFormat("string(%s)", val.c_str()); + break; + } + case AMessage::kTypeBuffer: + { + ret.appendFormat("data"); + break; + } + default: + { + ret.appendFormat("unknown(%d)", t); + break; + } + } + } + ret.append("}"); + mData->mDebug = ret; + return mData->mDebug.string(); +} + +bool AMediaFormat_getInt32(AMediaFormat* format, const char *name, int32_t *out) { + return format->mFormat->findInt32(name, out); +} + +bool AMediaFormat_getInt64(AMediaFormat* format, const char *name, int64_t *out) { + return format->mFormat->findInt64(name, out); +} + +bool AMediaFormat_getFloat(AMediaFormat* format, const char *name, float *out) { + return format->mFormat->findFloat(name, out); +} + +bool AMediaFormat_getSize(AMediaFormat* format, const char *name, size_t *out) { + return format->mFormat->findSize(name, out); +} + +bool AMediaFormat_getBuffer(AMediaFormat* format, const char *name, void** data, size_t *outsize) { + sp<ABuffer> buf; + if (format->mFormat->findBuffer(name, &buf)) { + *data = buf->data() + buf->offset(); + *outsize = buf->size(); + return true; + } + return false; +} + +bool AMediaFormat_getString(AMediaFormat* mData, const char *name, const char **out) { + + for (size_t i = 0; i < mData->mStringCache.size(); i++) { + if (strcmp(mData->mStringCache.keyAt(i).string(), name) == 0) { + mData->mStringCache.removeItemsAt(i, 1); + break; + } + } + + AString tmp; + if (mData->mFormat->findString(name, &tmp)) { + String8 ret(tmp.c_str()); + mData->mStringCache.add(String8(name), ret); + *out = ret.string(); + return true; + } + return false; +} + +void AMediaFormat_setInt32(AMediaFormat* format, const char *name, int32_t value) { + format->mFormat->setInt32(name, value); +} + +void AMediaFormat_setInt64(AMediaFormat* format, const char *name, int64_t value) { + format->mFormat->setInt64(name, value); +} + +void AMediaFormat_setFloat(AMediaFormat* format, const char* name, float value) { + format->mFormat->setFloat(name, value); +} + +void AMediaFormat_setString(AMediaFormat* format, const char* name, const char* value) { + // AMessage::setString() makes a copy of the string + format->mFormat->setString(name, value, strlen(value)); +} + +void AMediaFormat_setBuffer(AMediaFormat* format, const char* name, void* data, size_t size) { + // the ABuffer(void*, size_t) constructor doesn't take ownership of the data, so create + // a new buffer and copy the data into it + sp<ABuffer> buf = new ABuffer(size); + memcpy(buf->data(), data, size); + buf->setRange(0, size); + // AMessage::setBuffer() increases the refcount of the buffer + format->mFormat->setBuffer(name, buf); +} + + +const char* AMEDIAFORMAT_KEY_AAC_PROFILE = "aac-profile"; +const char* AMEDIAFORMAT_KEY_BIT_RATE = "bitrate"; +const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT = "channel-count"; +const char* AMEDIAFORMAT_KEY_CHANNEL_MASK = "channel-mask"; +const char* AMEDIAFORMAT_KEY_COLOR_FORMAT = "color-format"; +const char* AMEDIAFORMAT_KEY_DURATION = "durationUs"; +const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level"; +const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate"; +const char* AMEDIAFORMAT_KEY_HEIGHT = "height"; +const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts"; +const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect"; +const char* AMEDIAFORMAT_KEY_IS_DEFAULT = "is-default"; +const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle"; +const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval"; +const char* AMEDIAFORMAT_KEY_LANGUAGE = "language"; +const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height"; +const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size"; +const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width"; +const char* AMEDIAFORMAT_KEY_MIME = "mime"; +const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown"; +const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after"; +const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate"; +const char* AMEDIAFORMAT_KEY_WIDTH = "width"; +const char* AMEDIAFORMAT_KEY_STRIDE = "stride"; + + +} // extern "C" + + diff --git a/media/ndk/NdkMediaFormatPriv.h b/media/ndk/NdkMediaFormatPriv.h new file mode 100644 index 0000000..02342d9 --- /dev/null +++ b/media/ndk/NdkMediaFormatPriv.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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. + */ + +/* + * This file defines an NDK API. + * Do not remove methods. + * Do not change method signatures. + * Do not change the value of constants. + * Do not change the size of any of the classes defined in here. + * Do not reference types that are not part of the NDK. + * Do not #include files that aren't part of the NDK. + */ + +#ifndef _NDK_MEDIA_FORMAT_PRIV_H +#define _NDK_MEDIA_FORMAT_PRIV_H + +#include <NdkMediaFormat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +AMediaFormat* AMediaFormat_fromMsg(void*); +void AMediaFormat_getFormat(const AMediaFormat* mData, void* dest); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _NDK_MEDIA_FORMAT_PRIV_H + diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp new file mode 100644 index 0000000..98129cb --- /dev/null +++ b/media/ndk/NdkMediaMuxer.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "NdkMediaMuxer" + + +#include "NdkMediaMuxer.h" +#include "NdkMediaCodec.h" +#include "NdkMediaFormatPriv.h" + + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaMuxer.h> +#include <media/IMediaHTTPService.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_util_Binder.h> + +#include <jni.h> + +using namespace android; + +static int translate_error(status_t err) { + if (err == OK) { + return OK; + } + ALOGE("sf error code: %d", err); + return -1000; +} + +struct AMediaMuxer { + sp<MediaMuxer> mImpl; + +}; + +extern "C" { + +AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format) { + ALOGV("ctor"); + AMediaMuxer *mData = new AMediaMuxer(); + mData->mImpl = new MediaMuxer(fd, (android::MediaMuxer::OutputFormat)format); + return mData; +} + +int AMediaMuxer_delete(AMediaMuxer *muxer) { + ALOGV("dtor"); + delete muxer; + return OK; +} + +int AMediaMuxer_setLocation(AMediaMuxer *muxer, float latitude, float longtitude) { + return translate_error(muxer->mImpl->setLocation(latitude * 10000, longtitude * 10000)); +} + +int AMediaMuxer_setOrientationHint(AMediaMuxer *muxer, int degrees) { + return translate_error(muxer->mImpl->setOrientationHint(degrees)); +} + +ssize_t AMediaMuxer_addTrack(AMediaMuxer *muxer, const AMediaFormat *format) { + sp<AMessage> msg; + AMediaFormat_getFormat(format, &msg); + return translate_error(muxer->mImpl->addTrack(msg)); +} + +int AMediaMuxer_start(AMediaMuxer *muxer) { + return translate_error(muxer->mImpl->start()); +} + +int AMediaMuxer_stop(AMediaMuxer *muxer) { + return translate_error(muxer->mImpl->stop()); +} + +int AMediaMuxer_writeSampleData(AMediaMuxer *muxer, + size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info) { + sp<ABuffer> buf = new ABuffer((void*)(data + info.offset), info.size); + return translate_error( + muxer->mImpl->writeSampleData(buf, trackIdx, info.presentationTimeUs, info.flags)); +} + + +} // extern "C" + |