summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/libeffects/downmix/Android.mk6
-rw-r--r--media/libeffects/downmix/EffectDownmix.c133
-rw-r--r--media/libeffects/downmix/EffectDownmix.h1
-rw-r--r--media/libeffects/preprocessing/Android.mk5
-rw-r--r--media/libeffects/visualizer/EffectVisualizer.cpp35
-rw-r--r--media/libmedia/AudioRecord.cpp4
-rw-r--r--media/libmedia/AudioTrack.cpp31
-rw-r--r--media/libmedia/IAudioFlinger.cpp28
-rw-r--r--media/libmedia/IAudioFlingerClient.cpp4
-rw-r--r--media/libmedia/IAudioTrack.cpp4
-rw-r--r--media/libmedia/IMediaLogService.cpp4
-rw-r--r--media/libmedia/IMediaMetadataRetriever.cpp6
-rw-r--r--media/libmedia/IOMX.cpp16
-rw-r--r--media/libmedia/IStreamSource.cpp16
-rw-r--r--media/libmedia/MemoryLeakTrackUtil.cpp1
-rw-r--r--media/libnbaio/NBLog.cpp6
-rw-r--r--media/libstagefright/ACodec.cpp120
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/AwesomePlayer.cpp62
-rw-r--r--media/libstagefright/ClockEstimator.cpp177
-rw-r--r--media/libstagefright/MediaCodec.cpp29
-rw-r--r--media/libstagefright/OMXCodec.cpp38
-rw-r--r--media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp258
-rw-r--r--media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h78
-rw-r--r--media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp5
-rw-r--r--media/libstagefright/colorconversion/SoftwareRenderer.cpp7
-rw-r--r--media/libstagefright/foundation/AString.cpp42
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.cpp29
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.h1
-rw-r--r--media/libstagefright/include/AwesomePlayer.h3
-rw-r--r--media/libstagefright/include/SoftwareRenderer.h2
-rw-r--r--media/libstagefright/omx/GraphicBufferSource.cpp8
-rw-r--r--media/libstagefright/omx/OMX.cpp2
-rw-r--r--media/ndk/Android.mk46
-rw-r--r--media/ndk/NdkMediaCodec.cpp238
-rw-r--r--media/ndk/NdkMediaExtractor.cpp189
-rw-r--r--media/ndk/NdkMediaFormat.cpp245
-rw-r--r--media/ndk/NdkMediaFormatPriv.h44
-rw-r--r--media/ndk/NdkMediaMuxer.cpp99
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", &timestampNs)) {
+ // 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", &timestampNs)) {
+ 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", &timestampNs)) {
+ 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", &timestampNs)) {
+ 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"
+