Merge "synchronized audio beat detection: DO NOT MERGE" into ics-aah
This commit is contained in:
@@ -34,6 +34,7 @@ static struct {
|
|||||||
{ AID_MEDIA, "media.player" },
|
{ AID_MEDIA, "media.player" },
|
||||||
{ AID_MEDIA, "media.camera" },
|
{ AID_MEDIA, "media.camera" },
|
||||||
{ AID_MEDIA, "media.audio_policy" },
|
{ AID_MEDIA, "media.audio_policy" },
|
||||||
|
{ AID_MEDIA, "android.media.IAAHMetaDataService" },
|
||||||
{ AID_DRM, "drm.drmManager" },
|
{ AID_DRM, "drm.drmManager" },
|
||||||
{ AID_NFC, "nfc" },
|
{ AID_NFC, "nfc" },
|
||||||
{ AID_RADIO, "radio.phone" },
|
{ AID_RADIO, "radio.phone" },
|
||||||
|
|||||||
412
media/libaah_rtp/AAHMetaDataService_jni.cpp
Normal file
412
media/libaah_rtp/AAHMetaDataService_jni.cpp
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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_TAG "AAHMetaDataServiceJNI"
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
#include <binder/IServiceManager.h>
|
||||||
|
#include <utils/Log.h>
|
||||||
|
#include <utils/misc.h>
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
#include "JNIHelp.h"
|
||||||
|
|
||||||
|
#include "IAAHMetaData.h"
|
||||||
|
|
||||||
|
// error code, synced with MetaDataServiceRtp.java
|
||||||
|
enum {
|
||||||
|
SUCCESS = 0,
|
||||||
|
ERROR = -1,
|
||||||
|
ALREADY_EXISTS = -2,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
static const char* kAAHMetaDataServiceBinderName =
|
||||||
|
"android.media.IAAHMetaDataService";
|
||||||
|
|
||||||
|
static const char* kAAHMetaDataServiceClassName =
|
||||||
|
"android/media/libaah/MetaDataServiceRtp";
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jmethodID postEventFromNativeId;
|
||||||
|
jmethodID flushFromNativeId;
|
||||||
|
jfieldID mCookieId;
|
||||||
|
jclass clazz;
|
||||||
|
} jnireflect;
|
||||||
|
|
||||||
|
static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
|
||||||
|
if (NULL != *array) {
|
||||||
|
uint32_t len = env->GetArrayLength(*array);
|
||||||
|
if (len >= size)
|
||||||
|
return;
|
||||||
|
|
||||||
|
env->DeleteGlobalRef(*array);
|
||||||
|
*array = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyteArray localRef = env->NewByteArray(size);
|
||||||
|
if (NULL != localRef) {
|
||||||
|
// Promote to global ref.
|
||||||
|
*array = (jbyteArray) env->NewGlobalRef(localRef);
|
||||||
|
|
||||||
|
// Release our (now pointless) local ref.
|
||||||
|
env->DeleteLocalRef(localRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JNIMetaDataService acts as IAAHMetaDataClient, propagates message to java.
|
||||||
|
// It also starts a background thread that querying and monitoring life cycle
|
||||||
|
// of IAAHMetaDataService
|
||||||
|
// JNIMetaDataService will shoot itself when the related java object is garbage
|
||||||
|
// collected. This might not be important if java program is using singleton
|
||||||
|
// pattern; but it's also safe if java program create and destroy the object
|
||||||
|
// repeatedly.
|
||||||
|
class JNIMetaDataService : virtual public BnAAHMetaDataClient,
|
||||||
|
virtual public android::IBinder::DeathRecipient,
|
||||||
|
virtual public Thread {
|
||||||
|
public:
|
||||||
|
JNIMetaDataService();
|
||||||
|
|
||||||
|
// start working, must be called only once during initialize
|
||||||
|
bool start(jobject ref);
|
||||||
|
// stop thread and unref this object, you should never access the object
|
||||||
|
// after calling destroy()
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
// override BnAAHMetaDataClient
|
||||||
|
virtual void notify(uint16_t typeId, uint32_t item_len, const void* data);
|
||||||
|
|
||||||
|
virtual void flush();
|
||||||
|
|
||||||
|
// enable / disable the searching service
|
||||||
|
void setEnabled(bool e);
|
||||||
|
|
||||||
|
// override Thread
|
||||||
|
virtual bool threadLoop();
|
||||||
|
|
||||||
|
// override android::IBinder::DeathRecipient
|
||||||
|
virtual void binderDied(const wp<IBinder>& who);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual ~JNIMetaDataService();
|
||||||
|
|
||||||
|
sp<JNIMetaDataService> self_strongref;
|
||||||
|
sp<JNIMetaDataService> thread_strongref;
|
||||||
|
jobject metadataservice_ref;
|
||||||
|
jbyteArray metadata_buffer;
|
||||||
|
Mutex lock;
|
||||||
|
Condition cond;
|
||||||
|
volatile bool remote_service_invalid;
|
||||||
|
volatile bool exitThread;
|
||||||
|
volatile bool enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
void JNIMetaDataService::notify(uint16_t typeId, uint32_t item_len,
|
||||||
|
const void* data) {
|
||||||
|
LOGV("notify received type=%d item_len=%d", typeId, item_len);
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||||
|
// ensureArraySize provides some simple optimization of reusing
|
||||||
|
// the byte array object. If in the future that different types
|
||||||
|
// of metadata hit client, then more sophisticated strategy is needed.
|
||||||
|
ensureArraySize(env, &metadata_buffer, item_len);
|
||||||
|
if (metadata_buffer) {
|
||||||
|
jbyte *nArray = env->GetByteArrayElements(metadata_buffer, NULL);
|
||||||
|
memcpy(nArray, data, item_len);
|
||||||
|
env->ReleaseByteArrayElements(metadata_buffer, nArray, 0);
|
||||||
|
}
|
||||||
|
env->CallStaticVoidMethod(jnireflect.clazz,
|
||||||
|
jnireflect.postEventFromNativeId,
|
||||||
|
metadataservice_ref, typeId, item_len,
|
||||||
|
metadata_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JNIMetaDataService::flush() {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||||
|
env->CallStaticVoidMethod(jnireflect.clazz,
|
||||||
|
jnireflect.flushFromNativeId,
|
||||||
|
metadataservice_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIMetaDataService::JNIMetaDataService()
|
||||||
|
: metadataservice_ref(NULL),
|
||||||
|
metadata_buffer(NULL),
|
||||||
|
remote_service_invalid(true),
|
||||||
|
exitThread(false),
|
||||||
|
enabled(false) {
|
||||||
|
// Holds strong reference to myself, because the way that binder works
|
||||||
|
// requires to use RefBase, we cannot explicitly delete this object,
|
||||||
|
// otherwise, access from service manager might cause segfault.
|
||||||
|
// So we hold this reference until destroy() is called.
|
||||||
|
// Alternative solution is to create another JNIMetaDataServiceCookie class
|
||||||
|
// which holds the strong reference but that adds more memory fragmentation
|
||||||
|
self_strongref = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIMetaDataService::~JNIMetaDataService() {
|
||||||
|
LOGV("~JNIMetaDataService");
|
||||||
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||||
|
if (metadata_buffer) {
|
||||||
|
env->DeleteGlobalRef(metadata_buffer);
|
||||||
|
metadata_buffer = NULL;
|
||||||
|
}
|
||||||
|
if (metadataservice_ref) {
|
||||||
|
env->DeleteGlobalRef(metadataservice_ref);
|
||||||
|
metadataservice_ref = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JNIMetaDataService::threadLoop() {
|
||||||
|
LOGV("Enter JNIMetaDataService::threadLoop");
|
||||||
|
sp < IServiceManager > sm = defaultServiceManager();
|
||||||
|
sp < IBinder > binder;
|
||||||
|
sp<IAAHMetaDataService> remote_service;
|
||||||
|
lock.lock();
|
||||||
|
while (true) {
|
||||||
|
if (exitThread) {
|
||||||
|
break;
|
||||||
|
} else if (remote_service_invalid && enabled) {
|
||||||
|
// getService() may block 10s, so we do this not holding lock
|
||||||
|
lock.unlock();
|
||||||
|
binder = sm->getService(
|
||||||
|
String16(kAAHMetaDataServiceBinderName));
|
||||||
|
lock.lock();
|
||||||
|
if (binder != NULL) {
|
||||||
|
LOGD("found remote %s", kAAHMetaDataServiceBinderName);
|
||||||
|
if (remote_service.get()) {
|
||||||
|
remote_service->asBinder()->unlinkToDeath(this);
|
||||||
|
remote_service->removeClient(thread_strongref);
|
||||||
|
remote_service = NULL;
|
||||||
|
}
|
||||||
|
remote_service = interface_cast < IAAHMetaDataService
|
||||||
|
> (binder);
|
||||||
|
remote_service->asBinder()->linkToDeath(this);
|
||||||
|
remote_service->addClient(thread_strongref);
|
||||||
|
remote_service_invalid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exitThread && !(remote_service_invalid && enabled)) {
|
||||||
|
// if exitThread flag is not set and we are not searching remote
|
||||||
|
// service, wait next signal to be triggered either
|
||||||
|
// - destroy() being called
|
||||||
|
// - enabled or remote_service_invalid changed
|
||||||
|
cond.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (remote_service.get()) {
|
||||||
|
remote_service->removeClient(thread_strongref);
|
||||||
|
remote_service->asBinder()->unlinkToDeath(this);
|
||||||
|
remote_service = NULL;
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
binder = NULL;
|
||||||
|
sm = NULL;
|
||||||
|
// cleanup the thread reference
|
||||||
|
thread_strongref = NULL;
|
||||||
|
LOGV("Exit JNIMetaDataService::threadLoop");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JNIMetaDataService::start(jobject ref) {
|
||||||
|
metadataservice_ref = ref;
|
||||||
|
// now add a strong ref, used in threadLoop()
|
||||||
|
thread_strongref = this;
|
||||||
|
if (NO_ERROR
|
||||||
|
!= run("aah_metadataservice_monitor", ANDROID_PRIORITY_NORMAL)) {
|
||||||
|
thread_strongref = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JNIMetaDataService::destroy() {
|
||||||
|
lock.lock();
|
||||||
|
exitThread = true;
|
||||||
|
lock.unlock();
|
||||||
|
cond.signal();
|
||||||
|
// unref JNIMetaDataService, JNIMetaDataService will not be deleted for now;
|
||||||
|
// it will be deleted when thread exits and cleans thread_strongref.
|
||||||
|
self_strongref = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JNIMetaDataService::setEnabled(bool e) {
|
||||||
|
bool sendSignal;
|
||||||
|
lock.lock();
|
||||||
|
sendSignal = e && !enabled;
|
||||||
|
enabled = e;
|
||||||
|
lock.unlock();
|
||||||
|
if (sendSignal) {
|
||||||
|
cond.signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JNIMetaDataService::binderDied(const wp<IBinder>& who) {
|
||||||
|
LOGD("remote %s died, re-searching...", kAAHMetaDataServiceBinderName);
|
||||||
|
bool sendSignal;
|
||||||
|
lock.lock();
|
||||||
|
remote_service_invalid = true;
|
||||||
|
sendSignal = enabled;
|
||||||
|
lock.unlock();
|
||||||
|
if (sendSignal) {
|
||||||
|
cond.signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by java object to initialize the native part
|
||||||
|
static jint aahmetadataservice_native_setup(JNIEnv* env, jobject thiz,
|
||||||
|
jobject weak_this) {
|
||||||
|
|
||||||
|
jint retvalue = SUCCESS;
|
||||||
|
jobject ref;
|
||||||
|
JNIMetaDataService* lpJniService = new JNIMetaDataService();
|
||||||
|
if (lpJniService == NULL) {
|
||||||
|
LOGE("setup: Error in allocating JNIMetaDataService");
|
||||||
|
retvalue = ERROR;
|
||||||
|
goto setup_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we use a weak reference so the java object can be garbage collected.
|
||||||
|
ref = env->NewGlobalRef(weak_this);
|
||||||
|
if (ref == NULL) {
|
||||||
|
LOGE("setup: Error in NewGlobalRef");
|
||||||
|
retvalue = ERROR;
|
||||||
|
goto setup_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGV("setup: lpJniService: %p metadataservice_ref %p", lpJniService, ref);
|
||||||
|
|
||||||
|
env->SetIntField(thiz, jnireflect.mCookieId,
|
||||||
|
reinterpret_cast<jint>(lpJniService));
|
||||||
|
|
||||||
|
if (!lpJniService->start(ref)) {
|
||||||
|
retvalue = ERROR;
|
||||||
|
LOGE("setup: Error in starting JNIMetaDataService");
|
||||||
|
goto setup_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retvalue;
|
||||||
|
|
||||||
|
// failures:
|
||||||
|
setup_failure:
|
||||||
|
|
||||||
|
if (lpJniService != NULL) {
|
||||||
|
lpJniService->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return retvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JNIMetaDataService* get_service(JNIEnv* env, jobject thiz) {
|
||||||
|
return reinterpret_cast<JNIMetaDataService*>(env->GetIntField(
|
||||||
|
thiz, jnireflect.mCookieId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// called when the java object is garbaged collected
|
||||||
|
static void aahmetadataservice_native_finalize(JNIEnv* env, jobject thiz) {
|
||||||
|
JNIMetaDataService* pService = get_service(env, thiz);
|
||||||
|
if (pService == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOGV("finalize jni object");
|
||||||
|
// clean up the service object
|
||||||
|
pService->destroy();
|
||||||
|
env->SetIntField(thiz, jnireflect.mCookieId, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aahmetadataservice_native_enable(JNIEnv* env, jobject thiz) {
|
||||||
|
JNIMetaDataService* pService = get_service(env, thiz);
|
||||||
|
if (pService == NULL) {
|
||||||
|
LOGD("native service already deleted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pService->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aahmetadataservice_native_disable(JNIEnv* env, jobject thiz) {
|
||||||
|
JNIMetaDataService* pService = get_service(env, thiz);
|
||||||
|
if (pService == NULL) {
|
||||||
|
LOGD("native service already deleted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pService->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JNINativeMethod kAAHMetaDataServiceMethods[] = {
|
||||||
|
{ "native_setup", "(Ljava/lang/Object;)I",
|
||||||
|
(void *) aahmetadataservice_native_setup },
|
||||||
|
{ "native_enable", "()V", (void *) aahmetadataservice_native_enable },
|
||||||
|
{ "native_disable", "()V", (void *) aahmetadataservice_native_disable },
|
||||||
|
{ "native_finalize", "()V", (void *) aahmetadataservice_native_finalize },
|
||||||
|
};
|
||||||
|
|
||||||
|
static jint jniOnLoad(JavaVM* vm, void* reserved) {
|
||||||
|
LOGV("jniOnLoad");
|
||||||
|
JNIEnv* env = NULL;
|
||||||
|
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
|
||||||
|
LOGE("ERROR: GetEnv failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass clazz = env->FindClass(kAAHMetaDataServiceClassName);
|
||||||
|
if (!clazz) {
|
||||||
|
LOGE("ERROR: FindClass failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jnireflect.clazz = (jclass) env->NewGlobalRef(clazz);
|
||||||
|
|
||||||
|
if (env->RegisterNatives(jnireflect.clazz, kAAHMetaDataServiceMethods,
|
||||||
|
NELEM(kAAHMetaDataServiceMethods)) < 0) {
|
||||||
|
LOGE("ERROR: RegisterNatives failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jnireflect.postEventFromNativeId = env->GetStaticMethodID(
|
||||||
|
jnireflect.clazz, "postMetaDataFromNative",
|
||||||
|
"(Ljava/lang/Object;SI[B)V");
|
||||||
|
if (!jnireflect.postEventFromNativeId) {
|
||||||
|
LOGE("Can't find %s", "postMetaDataFromNative");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
jnireflect.flushFromNativeId = env->GetStaticMethodID(
|
||||||
|
jnireflect.clazz, "flushFromNative",
|
||||||
|
"(Ljava/lang/Object;)V");
|
||||||
|
if (!jnireflect.flushFromNativeId) {
|
||||||
|
LOGE("Can't find %s", "flushFromNative");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jnireflect.mCookieId = env->GetFieldID(jnireflect.clazz, "mCookie", "I");
|
||||||
|
if (!jnireflect.mCookieId) {
|
||||||
|
LOGE("Can't find %s", "mCookie");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JNI_VERSION_1_4;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
|
return android::jniOnLoad(vm, reserved);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -9,6 +9,8 @@ LOCAL_MODULE := libaah_rtp
|
|||||||
LOCAL_MODULE_TAGS := optional
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
|
aah_audio_algorithm.cpp \
|
||||||
|
aah_audio_processor.cpp \
|
||||||
aah_decoder_pump.cpp \
|
aah_decoder_pump.cpp \
|
||||||
aah_rx_player.cpp \
|
aah_rx_player.cpp \
|
||||||
aah_rx_player_core.cpp \
|
aah_rx_player_core.cpp \
|
||||||
@@ -17,6 +19,8 @@ LOCAL_SRC_FILES := \
|
|||||||
aah_tx_group.cpp \
|
aah_tx_group.cpp \
|
||||||
aah_tx_packet.cpp \
|
aah_tx_packet.cpp \
|
||||||
aah_tx_player.cpp \
|
aah_tx_player.cpp \
|
||||||
|
AAHMetaDataService_jni.cpp \
|
||||||
|
IAAHMetaData.cpp \
|
||||||
utils.cpp
|
utils.cpp
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := \
|
LOCAL_C_INCLUDES := \
|
||||||
@@ -26,6 +30,7 @@ LOCAL_C_INCLUDES := \
|
|||||||
frameworks/base/media/libstagefright
|
frameworks/base/media/libstagefright
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
|
libandroid_runtime \
|
||||||
libbinder \
|
libbinder \
|
||||||
libcommon_time_client \
|
libcommon_time_client \
|
||||||
libcutils \
|
libcutils \
|
||||||
@@ -39,3 +44,4 @@ LOCAL_LDLIBS := \
|
|||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||||
|
|||||||
222
media/libaah_rtp/IAAHMetaData.cpp
Normal file
222
media/libaah_rtp/IAAHMetaData.cpp
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
**
|
||||||
|
** Copyright 2012, 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_TAG "LibAAH_RTP"
|
||||||
|
//#define LOG_NDEBUG 0
|
||||||
|
#include <utils/Log.h>
|
||||||
|
|
||||||
|
#include <binder/IServiceManager.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
#include <utils/threads.h>
|
||||||
|
#include <binder/IInterface.h>
|
||||||
|
#include <binder/Parcel.h>
|
||||||
|
#include <media/stagefright/Utils.h>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "IAAHMetaData.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
|
||||||
|
FLUSH = IBinder::FIRST_CALL_TRANSACTION + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class BpAAHMetaDataClient : public BpInterface<IAAHMetaDataClient> {
|
||||||
|
public:
|
||||||
|
BpAAHMetaDataClient(const sp<IBinder>& impl)
|
||||||
|
: BpInterface<IAAHMetaDataClient>(impl) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void notify(uint16_t typeId, uint32_t item_len, const void* buf) {
|
||||||
|
Parcel data, reply;
|
||||||
|
data.writeInterfaceToken(IAAHMetaDataClient::getInterfaceDescriptor());
|
||||||
|
data.writeInt32((int32_t) typeId);
|
||||||
|
data.writeInt32((int32_t) item_len);
|
||||||
|
data.write(buf, item_len);
|
||||||
|
remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
|
||||||
|
}
|
||||||
|
virtual void flush() {
|
||||||
|
Parcel data, reply;
|
||||||
|
data.writeInterfaceToken(IAAHMetaDataClient::getInterfaceDescriptor());
|
||||||
|
remote()->transact(FLUSH, data, &reply, IBinder::FLAG_ONEWAY);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_META_INTERFACE(AAHMetaDataClient, "android.media.IAAHMetaDataClient");
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
status_t BnAAHMetaDataClient::onTransact(uint32_t code, const Parcel& data,
|
||||||
|
Parcel* reply, uint32_t flags) {
|
||||||
|
switch (code) {
|
||||||
|
case NOTIFY: {
|
||||||
|
CHECK_INTERFACE(IAAHMetaDataClient, data, reply);
|
||||||
|
uint16_t typeId = (uint16_t) data.readInt32();
|
||||||
|
uint32_t item_len = (uint32_t) data.readInt32();
|
||||||
|
const void* buf = data.readInplace(item_len);
|
||||||
|
|
||||||
|
notify(typeId, item_len, buf);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FLUSH: {
|
||||||
|
CHECK_INTERFACE(IAAHMetaDataClient, data, reply);
|
||||||
|
flush();
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return BBinder::onTransact(code, data, reply, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ADDCLIENT = IBinder::FIRST_CALL_TRANSACTION,
|
||||||
|
REMOVECLIENT = ADDCLIENT + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class BpAAHMetaDataService : public BpInterface<IAAHMetaDataService> {
|
||||||
|
public:
|
||||||
|
BpAAHMetaDataService(const sp<IBinder>& impl)
|
||||||
|
: BpInterface<IAAHMetaDataService>(impl) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void addClient(const sp<IAAHMetaDataClient>& client) {
|
||||||
|
Parcel data, reply;
|
||||||
|
data.writeInterfaceToken(IAAHMetaDataService::getInterfaceDescriptor());
|
||||||
|
data.writeStrongBinder(client->asBinder());
|
||||||
|
remote()->transact(ADDCLIENT, data, &reply, IBinder::FLAG_ONEWAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void removeClient(const sp<IAAHMetaDataClient>& client) {
|
||||||
|
Parcel data, reply;
|
||||||
|
data.writeInterfaceToken(IAAHMetaDataService::getInterfaceDescriptor());
|
||||||
|
data.writeStrongBinder(client->asBinder());
|
||||||
|
remote()->transact(REMOVECLIENT, data, &reply, IBinder::FLAG_ONEWAY);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_META_INTERFACE(AAHMetaDataService, "android.media.IAAHMetaDataService");
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
status_t BnAAHMetaDataService::onTransact(uint32_t code, const Parcel& data,
|
||||||
|
Parcel* reply, uint32_t flags) {
|
||||||
|
switch (code) {
|
||||||
|
case ADDCLIENT: {
|
||||||
|
CHECK_INTERFACE(IAAHMetaDataService, data, reply);
|
||||||
|
sp<IAAHMetaDataClient> client = interface_cast < IAAHMetaDataClient
|
||||||
|
> (data.readStrongBinder());
|
||||||
|
|
||||||
|
addClient(client);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case REMOVECLIENT: {
|
||||||
|
CHECK_INTERFACE(IAAHMetaDataService, data, reply);
|
||||||
|
sp<IAAHMetaDataClient> client = interface_cast < IAAHMetaDataClient
|
||||||
|
> (data.readStrongBinder());
|
||||||
|
|
||||||
|
removeClient(client);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return BBinder::onTransact(code, data, reply, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool s_aah_metadata_service_initialized = false;
|
||||||
|
static sp<AAHMetaDataService> s_aah_metadata_service = NULL;
|
||||||
|
static Mutex s_aah_metadata_service_lock;
|
||||||
|
|
||||||
|
const sp<AAHMetaDataService>& AAHMetaDataService::getInstance() {
|
||||||
|
Mutex::Autolock autolock(&s_aah_metadata_service_lock);
|
||||||
|
if (!s_aah_metadata_service_initialized) {
|
||||||
|
s_aah_metadata_service = new AAHMetaDataService();
|
||||||
|
status_t ret = android::defaultServiceManager()->addService(
|
||||||
|
IAAHMetaDataService::descriptor, s_aah_metadata_service);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOGE("failed to add AAHMetaDataService error code %d", ret);
|
||||||
|
s_aah_metadata_service = NULL;
|
||||||
|
}
|
||||||
|
s_aah_metadata_service_initialized = true;
|
||||||
|
}
|
||||||
|
return s_aah_metadata_service;
|
||||||
|
}
|
||||||
|
|
||||||
|
AAHMetaDataService::AAHMetaDataService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAHMetaDataService::addClient(const sp<IAAHMetaDataClient>& client) {
|
||||||
|
Mutex::Autolock lock(mLock);
|
||||||
|
IAAHMetaDataClient* obj = client.get();
|
||||||
|
LOGV("addClient %p", obj);
|
||||||
|
client->asBinder()->linkToDeath(this);
|
||||||
|
mClients.add(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAHMetaDataService::binderDied(const wp<IBinder>& who) {
|
||||||
|
Mutex::Autolock lock(mLock);
|
||||||
|
for (uint32_t i = 0; i < mClients.size(); ++i) {
|
||||||
|
const sp<IAAHMetaDataClient>& c = mClients[i];
|
||||||
|
if (who == c->asBinder()) {
|
||||||
|
LOGD("IAAHMetaDataClient binder Died");
|
||||||
|
LOGV("removed died client %p", c.get());
|
||||||
|
mClients.removeAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAHMetaDataService::removeClient(const sp<IAAHMetaDataClient>& client) {
|
||||||
|
IAAHMetaDataClient* obj = client.get();
|
||||||
|
Mutex::Autolock lock(mLock);
|
||||||
|
for (uint32_t i = 0; i < mClients.size(); ++i) {
|
||||||
|
const sp<IAAHMetaDataClient>& c = mClients[i];
|
||||||
|
if (c->asBinder() == client->asBinder()) {
|
||||||
|
LOGV("removeClient %p", c.get());
|
||||||
|
mClients.removeAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAHMetaDataService::broadcast(uint16_t typeId, uint32_t item_len,
|
||||||
|
void* data) {
|
||||||
|
LOGV("broadcast %d", typeId);
|
||||||
|
Mutex::Autolock lock(mLock);
|
||||||
|
uint8_t* buf = reinterpret_cast<uint8_t*>(data);
|
||||||
|
for (uint32_t i = 0; i < mClients.size(); ++i) {
|
||||||
|
const sp<IAAHMetaDataClient> c = mClients[i];
|
||||||
|
LOGV("notify %p", c.get());
|
||||||
|
c->notify(typeId, item_len, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAHMetaDataService::flush() {
|
||||||
|
Mutex::Autolock lock(mLock);
|
||||||
|
for (uint32_t i = 0; i < mClients.size(); ++i) {
|
||||||
|
const sp<IAAHMetaDataClient> c = mClients[i];
|
||||||
|
c->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
// namespace android
|
||||||
89
media/libaah_rtp/IAAHMetaData.h
Normal file
89
media/libaah_rtp/IAAHMetaData.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ANDROID_IAAHMETADATA_H
|
||||||
|
#define ANDROID_IAAHMETADATA_H
|
||||||
|
|
||||||
|
#include <utils/SortedVector.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
#include <utils/String8.h>
|
||||||
|
#include <utils/threads.h>
|
||||||
|
#include <binder/IInterface.h>
|
||||||
|
#include <binder/Parcel.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class IAAHMetaDataClient : public IInterface {
|
||||||
|
public:
|
||||||
|
DECLARE_META_INTERFACE (AAHMetaDataClient);
|
||||||
|
|
||||||
|
virtual void notify(uint16_t typeId, uint32_t item_len,
|
||||||
|
const void* data) = 0;
|
||||||
|
virtual void flush() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class BnAAHMetaDataClient : public BnInterface<IAAHMetaDataClient> {
|
||||||
|
public:
|
||||||
|
virtual status_t onTransact(uint32_t code, const Parcel& data,
|
||||||
|
Parcel* reply, uint32_t flags = 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class IAAHMetaDataService : public IInterface {
|
||||||
|
public:
|
||||||
|
DECLARE_META_INTERFACE (AAHMetaDataService);
|
||||||
|
|
||||||
|
virtual void addClient(const sp<IAAHMetaDataClient>& client) = 0;
|
||||||
|
virtual void removeClient(const sp<IAAHMetaDataClient>& client) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class BnAAHMetaDataService : public BnInterface<IAAHMetaDataService> {
|
||||||
|
public:
|
||||||
|
virtual status_t onTransact(uint32_t code, const Parcel& data,
|
||||||
|
Parcel* reply, uint32_t flags = 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class AAHMetaDataService : public BnAAHMetaDataService,
|
||||||
|
public android::IBinder::DeathRecipient {
|
||||||
|
public:
|
||||||
|
static const sp<AAHMetaDataService>& getInstance();
|
||||||
|
void broadcast(uint16_t typeId, uint32_t item_len, void* data);
|
||||||
|
void flush();
|
||||||
|
virtual void addClient(const sp<IAAHMetaDataClient>& client);
|
||||||
|
virtual void removeClient(const sp<IAAHMetaDataClient>& client);
|
||||||
|
virtual void binderDied(const wp<IBinder>& who);
|
||||||
|
private:
|
||||||
|
AAHMetaDataService();
|
||||||
|
|
||||||
|
SortedVector<sp<IAAHMetaDataClient> > mClients;
|
||||||
|
Mutex mLock;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
;
|
||||||
|
// namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_IAAHMETADATA_H
|
||||||
291
media/libaah_rtp/aah_audio_algorithm.cpp
Normal file
291
media/libaah_rtp/aah_audio_algorithm.cpp
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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_TAG "LibAAH_RTP"
|
||||||
|
//#define LOG_NDEBUG 0
|
||||||
|
#include <utils/Log.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "aah_audio_algorithm.h"
|
||||||
|
|
||||||
|
// #define DEBUG_BEAT_VALUE
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
// magic number, the bar should set higher if kBands is bigger
|
||||||
|
const float BeatDetectionAlgorithm::kThreshHold = 8;
|
||||||
|
const float BeatDetectionAlgorithm::kSumThreshold = 250;
|
||||||
|
|
||||||
|
|
||||||
|
// back trace time 1s
|
||||||
|
const float BeatDetectionAlgorithm::kBacktraceTime = 1;
|
||||||
|
|
||||||
|
// we must wait 1 second before switch generate a new sequence number, this is
|
||||||
|
// to prevent visualizer switches too much
|
||||||
|
const int64_t BeatDetectionAlgorithm::kBeatInterval = 1000000;
|
||||||
|
|
||||||
|
const float BeatDetectionAlgorithm::kMaxBeatValue = 100000;
|
||||||
|
|
||||||
|
// how many beat information will be cached before send out? We group beats
|
||||||
|
// in one packet to reduce the cost of sending too much packets. The time
|
||||||
|
// should be shorter than kAAHBufferTimeUs defined in TxPlayer
|
||||||
|
// The extra latency is introduced by fft, beat algorithm, time transform,
|
||||||
|
// binder service latency, jni latency, etc. If all these extra latency
|
||||||
|
// add up too much, then kAAHBufferTimeUs must be increased
|
||||||
|
const int32_t BeatDetectionAlgorithm::kAAHBeatInfoBufferTimeMS = 250;
|
||||||
|
|
||||||
|
// each thread holds a random data structure
|
||||||
|
static __thread unsigned short sRandData[3];
|
||||||
|
static __thread bool sRandDataInitialized = false;
|
||||||
|
|
||||||
|
static inline float normalizeBeatValue(float scale, float threshold) {
|
||||||
|
if (scale < 1) {
|
||||||
|
return 1;
|
||||||
|
} else if (scale > threshold) {
|
||||||
|
return threshold;
|
||||||
|
}
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeatDetectionAlgorithm::BeatDetectionAlgorithm()
|
||||||
|
: mSamplesPerSegment(0),
|
||||||
|
mSegments(0),
|
||||||
|
mEnergyTrain(NULL),
|
||||||
|
mBeatTrain(NULL) {
|
||||||
|
if (!sRandDataInitialized) {
|
||||||
|
seed48(sRandData);
|
||||||
|
sRandDataInitialized = true;
|
||||||
|
}
|
||||||
|
mBeatSequenceNumber = nrand48(sRandData);
|
||||||
|
}
|
||||||
|
|
||||||
|
BeatDetectionAlgorithm::~BeatDetectionAlgorithm() {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BeatDetectionAlgorithm::initialize(uint32_t samples_per_seg,
|
||||||
|
uint32_t sample_rates) {
|
||||||
|
LOGV("initialize algorithm samples_per_seg %d sample_rates %d",
|
||||||
|
samples_per_seg, sample_rates);
|
||||||
|
uint32_t segments = (uint32_t)(
|
||||||
|
sample_rates / samples_per_seg * kBacktraceTime);
|
||||||
|
if (mSamplesPerSegment == samples_per_seg && mSegments == segments) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint32_t samplesPerBand = samples_per_seg / kBands;
|
||||||
|
if (samplesPerBand * kBands != samples_per_seg) {
|
||||||
|
LOGE("%s samples per segment not divided evenly by bands",
|
||||||
|
__PRETTY_FUNCTION__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (samplesPerBand & 1) {
|
||||||
|
LOGE("%s each band must contain even number of samples",
|
||||||
|
__PRETTY_FUNCTION__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
mSamplesPerSegment = samples_per_seg;
|
||||||
|
mSegments = segments;
|
||||||
|
mSamplesPerBand = samplesPerBand;
|
||||||
|
mTrainMatrixSize = kSearchBands * mSegments;
|
||||||
|
mEnergyTrain = new uint64_t[mTrainMatrixSize];
|
||||||
|
mBeatTrain = new float[mTrainMatrixSize];
|
||||||
|
if (!mEnergyTrain || !mBeatTrain) {
|
||||||
|
LOGE("%s failed allocating memory", __PRETTY_FUNCTION__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
flush();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeatDetectionAlgorithm::process(int64_t ts, int32_t* fft,
|
||||||
|
uint32_t samples_per_seg) {
|
||||||
|
CHECK(samples_per_seg == mSamplesPerSegment);
|
||||||
|
if (mSegments == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// access fft array as 16bits
|
||||||
|
int16_t* segmentFt = (int16_t*)fft;
|
||||||
|
float maxNewEnergyScale = 0;
|
||||||
|
int maxBeatIdx = -1;
|
||||||
|
float sum = 0;
|
||||||
|
for (int i = 0, trainIndexForBand = 0;
|
||||||
|
i < kBandEnd - kBandStart;
|
||||||
|
i++, trainIndexForBand += mSegments) {
|
||||||
|
uint64_t newEnergy = 0;
|
||||||
|
// mSamplesPerBand is already validated to be even in initialize()
|
||||||
|
uint32_t startSample = (kBandStart + i) * mSamplesPerBand;
|
||||||
|
for (uint32_t li = startSample;
|
||||||
|
li < startSample + mSamplesPerBand;
|
||||||
|
li += 2) {
|
||||||
|
uint64_t amplitude = (int32_t)segmentFt[li] * (int32_t)segmentFt[li]
|
||||||
|
+ (int32_t)segmentFt[li + 1] * (int32_t)segmentFt[li + 1];
|
||||||
|
newEnergy += amplitude;
|
||||||
|
}
|
||||||
|
newEnergy = newEnergy / (mSamplesPerBand >> 1);
|
||||||
|
if (mEnergyTrainFilled) {
|
||||||
|
// update beat train
|
||||||
|
float newEnergyScale = (float) newEnergy
|
||||||
|
/ ((double) mEnergyTrainSum[i] / (double) mSegments);
|
||||||
|
mBeatTrain[trainIndexForBand + mBeatTrainIdx] = newEnergyScale;
|
||||||
|
if (isnan(newEnergyScale) || isinf(newEnergyScale)
|
||||||
|
|| newEnergyScale > maxNewEnergyScale) {
|
||||||
|
maxNewEnergyScale = newEnergyScale;
|
||||||
|
maxBeatIdx = i;
|
||||||
|
}
|
||||||
|
if (newEnergyScale > kThreshHold) {
|
||||||
|
sum += newEnergyScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the energy train and energy sum
|
||||||
|
mEnergyTrainSum[i] -= mEnergyTrain[trainIndexForBand + mEnergyTrainIdx];
|
||||||
|
mEnergyTrain[trainIndexForBand + mEnergyTrainIdx] = newEnergy;
|
||||||
|
mEnergyTrainSum[i] += mEnergyTrain[trainIndexForBand + mEnergyTrainIdx];
|
||||||
|
|
||||||
|
}
|
||||||
|
if (isnan(maxNewEnergyScale) || isinf(maxNewEnergyScale)
|
||||||
|
|| maxNewEnergyScale > kMaxBeatValue) {
|
||||||
|
maxNewEnergyScale = kMaxBeatValue;
|
||||||
|
}
|
||||||
|
bool beat = false;
|
||||||
|
if (sum >= kSumThreshold /*&& maxNewEnergyScale > kThreshHold*/
|
||||||
|
&& (mBeatLastTs == -1 || (ts - mBeatLastTs) > kBeatInterval)) {
|
||||||
|
mBeatLastTs = ts;
|
||||||
|
mBeatSequenceNumber++;
|
||||||
|
beat = true;
|
||||||
|
LOGV("BEAT!!!! %d %f", mBeatSequenceNumber, maxNewEnergyScale);
|
||||||
|
}
|
||||||
|
mBeatValue = maxNewEnergyScale;
|
||||||
|
mBeatValueSmoothed = mBeatValueSmoothed * 0.7
|
||||||
|
+ normalizeBeatValue(mBeatValue, 30) * 0.3;
|
||||||
|
AudioBeatInfo beatInfo(ts, mBeatValue, mBeatValueSmoothed,
|
||||||
|
mBeatSequenceNumber);
|
||||||
|
// allowing overwrite existing item in the queue if we didn't send out
|
||||||
|
// data in time: lost beats is very unlikely to happen
|
||||||
|
mBeatInfoQueue.writeAllowOverflow(beatInfo);
|
||||||
|
|
||||||
|
#ifdef DEBUG_BEAT_VALUE
|
||||||
|
char debugstr[256];
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < mBeatValue && i < sizeof(debugstr) - 1; i++) {
|
||||||
|
debugstr[i] = beat ? 'B' : '*';
|
||||||
|
}
|
||||||
|
debugstr[i] = 0;
|
||||||
|
LOGD("%lld %lld %f %f %s", mBeatLastTs, ts, mBeatValue, sum, debugstr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mEnergyTrainIdx = mEnergyTrainIdx + 1;
|
||||||
|
if (mEnergyTrainIdx == mSegments) {
|
||||||
|
mEnergyTrainIdx = 0;
|
||||||
|
mEnergyTrainFilled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mEnergyTrainFilled) {
|
||||||
|
mBeatTrainIdx = mBeatTrainIdx + 1;
|
||||||
|
if (mBeatTrainIdx == mSegments) {
|
||||||
|
mBeatTrainIdx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeatDetectionAlgorithm::cleanup() {
|
||||||
|
if (mEnergyTrain) {
|
||||||
|
delete mEnergyTrain;
|
||||||
|
mEnergyTrain = NULL;
|
||||||
|
}
|
||||||
|
if (mBeatTrain) {
|
||||||
|
delete mBeatTrain;
|
||||||
|
mBeatTrain = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TRTPMetaDataBeat : public TRTPMetaDataBlock {
|
||||||
|
public:
|
||||||
|
TRTPMetaDataBeat()
|
||||||
|
: TRTPMetaDataBlock(kMetaDataBeat, 0) {}
|
||||||
|
TRTPMetaDataBeat(uint16_t beats,
|
||||||
|
AudioBeatInfo* beatInfo)
|
||||||
|
: TRTPMetaDataBlock(kMetaDataBeat, calculateItemLength(beats))
|
||||||
|
, mCount(beats)
|
||||||
|
{
|
||||||
|
memcpy(&beatInfos, beatInfo, beats * sizeof(AudioBeatInfo) );
|
||||||
|
}
|
||||||
|
static inline uint32_t calculateItemLength(uint16_t beats) {
|
||||||
|
return 2 + BeatDetectionAlgorithm::kItemLength * beats;
|
||||||
|
}
|
||||||
|
virtual ~TRTPMetaDataBeat() {}
|
||||||
|
virtual void write(uint8_t*& buf) const;
|
||||||
|
uint16_t mCount;
|
||||||
|
struct AudioBeatInfo beatInfos[BeatDetectionAlgorithm::kBeatQueueLen];
|
||||||
|
};
|
||||||
|
|
||||||
|
void TRTPMetaDataBeat::write(uint8_t*& buf) const {
|
||||||
|
writeBlockHead(buf);
|
||||||
|
TRTPPacket::writeU16(buf, mCount);
|
||||||
|
for (uint16_t i = 0; i < mCount; i++) {
|
||||||
|
TRTPPacket::writeU64(buf, beatInfos[i].ts);
|
||||||
|
TRTPPacket::writeFloat(buf, beatInfos[i].beatValue);
|
||||||
|
TRTPPacket::writeFloat(buf, beatInfos[i].smoothedBeatValue);
|
||||||
|
TRTPPacket::writeU32(buf, beatInfos[i].sequenceNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TRTPMetaDataBlock* BeatDetectionAlgorithm::collectMetaData(bool flushOut) {
|
||||||
|
AudioBeatInfo beatInfo[kBeatQueueLen];
|
||||||
|
uint32_t min_read;
|
||||||
|
if (flushOut) {
|
||||||
|
min_read = 0;
|
||||||
|
} else {
|
||||||
|
min_read = mSegments * kAAHBeatInfoBufferTimeMS / 1000;
|
||||||
|
if (min_read > kBeatQueueLen) {
|
||||||
|
min_read = kBeatQueueLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int beats = mBeatInfoQueue.readBulk(beatInfo, min_read,
|
||||||
|
kBeatQueueLen);
|
||||||
|
if (beats > 0) {
|
||||||
|
uint32_t privateSize = TRTPMetaDataBeat::calculateItemLength(beats);
|
||||||
|
if (privateSize > 0xffff) {
|
||||||
|
LOGE("metadata packet too big");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return new TRTPMetaDataBeat(beats, beatInfo);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeatDetectionAlgorithm::flush() {
|
||||||
|
if (mEnergyTrain == NULL || mBeatTrain == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mEnergyTrainIdx = 0;
|
||||||
|
mBeatTrainIdx = 0;
|
||||||
|
mEnergyTrainFilled = false;
|
||||||
|
mBeatValue = 0;
|
||||||
|
mBeatValueSmoothed = 0;
|
||||||
|
mBeatLastTs = -1;
|
||||||
|
|
||||||
|
memset(mEnergyTrain, 0, mTrainMatrixSize * sizeof(uint64_t));
|
||||||
|
// IEEE745: all zero bytes generates 0.0f
|
||||||
|
memset(mBeatTrain, 0, mTrainMatrixSize * sizeof(float));
|
||||||
|
memset(&mEnergyTrainSum, 0, sizeof(mEnergyTrainSum));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
145
media/libaah_rtp/aah_audio_algorithm.h
Normal file
145
media/libaah_rtp/aah_audio_algorithm.h
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AAH_AUDIO_ALGORITHM_H__
|
||||||
|
#define __AAH_AUDIO_ALGORITHM_H__
|
||||||
|
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
#include <utils.h>
|
||||||
|
|
||||||
|
#include "aah_tx_packet.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class AudioAlgorithm : public virtual RefBase {
|
||||||
|
public:
|
||||||
|
explicit AudioAlgorithm() {}
|
||||||
|
virtual bool initialize(uint32_t samples_per_seg, uint32_t samplerate) = 0;
|
||||||
|
virtual void process(int64_t ts, int32_t* fft,
|
||||||
|
uint32_t samples_per_seg) = 0;
|
||||||
|
virtual void flush() = 0;
|
||||||
|
virtual void cleanup() = 0;
|
||||||
|
virtual TRTPMetaDataBlock* collectMetaData(bool flushOut) = 0;
|
||||||
|
virtual ~AudioAlgorithm() {}
|
||||||
|
private:
|
||||||
|
DISALLOW_EVIL_CONSTRUCTORS (AudioAlgorithm);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AudioBeatInfo {
|
||||||
|
AudioBeatInfo()
|
||||||
|
: ts(0)
|
||||||
|
, beatValue(0)
|
||||||
|
, smoothedBeatValue(0)
|
||||||
|
, sequenceNumber(0) {}
|
||||||
|
AudioBeatInfo(int64_t t, float b, float sb, uint32_t s)
|
||||||
|
: ts(t)
|
||||||
|
, beatValue(b)
|
||||||
|
, smoothedBeatValue(sb)
|
||||||
|
, sequenceNumber(s) {}
|
||||||
|
int64_t ts;
|
||||||
|
float beatValue;
|
||||||
|
float smoothedBeatValue;
|
||||||
|
uint32_t sequenceNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BeatDetectionAlgorithm : public virtual AudioAlgorithm {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static const int kItemLength = 20;
|
||||||
|
|
||||||
|
explicit BeatDetectionAlgorithm();
|
||||||
|
virtual bool initialize(uint32_t samples_per_seg, uint32_t samplerate);
|
||||||
|
// each 32 bits fft value consists of real part on high 16 bits
|
||||||
|
// and imaginary part on low 16 bits
|
||||||
|
virtual void process(int64_t ts, int32_t* fft, uint32_t samples_per_seg);
|
||||||
|
virtual void flush();
|
||||||
|
virtual void cleanup();
|
||||||
|
virtual TRTPMetaDataBlock* collectMetaData(bool flushOut);
|
||||||
|
virtual ~BeatDetectionAlgorithm();
|
||||||
|
protected:
|
||||||
|
// =======================
|
||||||
|
// constant definition
|
||||||
|
// =======================
|
||||||
|
|
||||||
|
// divide frequency domain to kBands
|
||||||
|
static const int32_t kBands = 128;
|
||||||
|
// we search from kBandStart(inclusive) to kBandEnd (exclusive)
|
||||||
|
static const int32_t kBandStart = 0;
|
||||||
|
static const int32_t kBandEnd = 64;
|
||||||
|
static const int32_t kSearchBands = kBandEnd - kBandStart;
|
||||||
|
// magic number, the bar should set higher if kBands is bigger
|
||||||
|
static const float kThreshHold;
|
||||||
|
static const float kSumThreshold;
|
||||||
|
|
||||||
|
static const float kBacktraceTime;
|
||||||
|
|
||||||
|
static const int64_t kBeatInterval;
|
||||||
|
|
||||||
|
static const float kMaxBeatValue;
|
||||||
|
|
||||||
|
static const int32_t kAAHBeatInfoBufferTimeMS;
|
||||||
|
|
||||||
|
// 128 maximum beat allowed, this is roughly 3 seconds data for 44KHZ, 1024
|
||||||
|
// fft samples per segment
|
||||||
|
static const uint32_t kBeatQueueLen = 128;
|
||||||
|
|
||||||
|
uint32_t mSamplesPerSegment;
|
||||||
|
uint32_t mSegments;
|
||||||
|
uint32_t mSamplesPerBand;
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// Energy train
|
||||||
|
// =======================
|
||||||
|
// circular energy value buffer for each BAND, each maintains one second
|
||||||
|
uint32_t mEnergyTrainIdx;
|
||||||
|
uint64_t* mEnergyTrain; // 2d array, size is kSearchBands * mSegments
|
||||||
|
uint32_t mTrainMatrixSize; // kSearchBands * mSegments
|
||||||
|
|
||||||
|
// sum of last second energy for each sub band
|
||||||
|
uint64_t mEnergyTrainSum[kSearchBands];
|
||||||
|
|
||||||
|
// if energy train has been filled for 1 second
|
||||||
|
bool mEnergyTrainFilled;
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// Beat train
|
||||||
|
// =======================
|
||||||
|
// beat value train buffer for each BAND
|
||||||
|
// It's not necessary keep a train now, we may need it for detecting peak
|
||||||
|
float* mBeatTrain; // 2d array of kSearchBands * mSegments
|
||||||
|
uint32_t mBeatTrainIdx;
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// Energy extraction stuff passed to outside
|
||||||
|
// There is multi thread issue, but not critical.
|
||||||
|
// So we not using synchronized or other mechanism
|
||||||
|
// =======================
|
||||||
|
float mBeatValue;
|
||||||
|
float mBeatValueSmoothed;
|
||||||
|
|
||||||
|
CircularArray<AudioBeatInfo, kBeatQueueLen> mBeatInfoQueue;
|
||||||
|
uint32_t mBeatSequenceNumber;
|
||||||
|
int64_t mBeatLastTs;
|
||||||
|
|
||||||
|
friend class TRTPMetaDataBeat;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DISALLOW_EVIL_CONSTRUCTORS (BeatDetectionAlgorithm);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // __AAH_AUDIO_ALGORITHM_H__
|
||||||
224
media/libaah_rtp/aah_audio_processor.cpp
Normal file
224
media/libaah_rtp/aah_audio_processor.cpp
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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_TAG "LibAAH_RTP"
|
||||||
|
//#define LOG_NDEBUG 0
|
||||||
|
#include <utils/Log.h>
|
||||||
|
|
||||||
|
#include <common_time/cc_helper.h>
|
||||||
|
#include <media/AudioSystem.h>
|
||||||
|
#include <media/AudioTrack.h>
|
||||||
|
#include <media/stagefright/foundation/ADebug.h>
|
||||||
|
#include <media/stagefright/MetaData.h>
|
||||||
|
#include <media/stagefright/OMXClient.h>
|
||||||
|
#include <media/stagefright/OMXCodec.h>
|
||||||
|
#include <media/stagefright/Utils.h>
|
||||||
|
#include <utils/Timers.h>
|
||||||
|
#include <utils/threads.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "aah_audio_processor.h"
|
||||||
|
|
||||||
|
extern void fixed_fft_real(int n, int32_t *v);
|
||||||
|
|
||||||
|
#define CLIP(x, low, high) ((x) < (low) ? (low) : ((x) > (high) ? (high) : (x)))
|
||||||
|
|
||||||
|
#define SAMPLES_TO_TS(samples, sample_rate) \
|
||||||
|
( static_cast<int64_t>(samples) * 1000000 / (sample_rate) )
|
||||||
|
|
||||||
|
|
||||||
|
// fill mono audio data into 16 bits workspace
|
||||||
|
template<int BITS, typename SRCTYPE>
|
||||||
|
static inline void fillWorkspaceOneChannel(int32_t* dst, SRCTYPE* src,
|
||||||
|
uint32_t to_fill) {
|
||||||
|
for (uint32_t inIdx = 0; inIdx + 1 < to_fill; inIdx += 2) {
|
||||||
|
int32_t smp1 = src[inIdx];
|
||||||
|
int32_t smp2 = src[inIdx + 1];
|
||||||
|
// following "if" clause will be optimized at compilation time
|
||||||
|
if (BITS < 16) {
|
||||||
|
smp1 = (smp1 << (16 - BITS)) & 0xffff;
|
||||||
|
smp2 = (smp2 << (16 - BITS)) & 0xffff;
|
||||||
|
} else {
|
||||||
|
smp1 = (smp1 >> (BITS - 16)) & 0xffff;
|
||||||
|
smp2 = (smp2 >> (BITS - 16)) & 0xffff;
|
||||||
|
}
|
||||||
|
*(dst++) = (smp1 << 16) | (smp2 << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill stereo audio data into 16 bits workspace, left/right are mixed
|
||||||
|
template<int BITS, typename SRCTYPE>
|
||||||
|
static inline void fillWorkspaceTwoChannel(int32_t* dst, SRCTYPE* src,
|
||||||
|
uint32_t to_fill) {
|
||||||
|
for (uint32_t inIdx = 0; inIdx + 3 < to_fill; inIdx += 4) {
|
||||||
|
int32_t smp1 = static_cast<int32_t>(src[inIdx]) + src[inIdx + 1];
|
||||||
|
int32_t smp2 = static_cast<int32_t>(src[inIdx + 2]) + src[inIdx + 3];
|
||||||
|
// following "if" clause will be optimized at compilation time
|
||||||
|
if (BITS < 16) {
|
||||||
|
smp1 = (smp1 << (15 - BITS)) & 0xffff;
|
||||||
|
smp2 = (smp2 << (15 - BITS)) & 0xffff;
|
||||||
|
} else {
|
||||||
|
smp1 = (smp1 >> (BITS - 15)) & 0xffff;
|
||||||
|
smp2 = (smp2 >> (BITS - 15)) & 0xffff;
|
||||||
|
}
|
||||||
|
*(dst++) = (smp1 << 16) | (smp2 << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int BITS, typename SRCTYPE>
|
||||||
|
static inline bool fillWorkspace(int32_t* dst, SRCTYPE* src,
|
||||||
|
uint32_t to_fill, int32_t channels) {
|
||||||
|
switch(channels) {
|
||||||
|
case 2:
|
||||||
|
fillWorkspaceTwoChannel<BITS, SRCTYPE>(dst, src, to_fill);
|
||||||
|
return true;
|
||||||
|
case 1:
|
||||||
|
fillWorkspaceOneChannel<BITS, SRCTYPE>(dst, src, to_fill);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
LOGE("Unsupported channel %d", channels);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
AAH_AudioProcessor::AAH_AudioProcessor(OMXClient& omx)
|
||||||
|
: AAH_DecoderPumpBase(omx),
|
||||||
|
filled_(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
AAH_AudioProcessor::~AAH_AudioProcessor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAH_AudioProcessor::setAlgorithm(const sp<AudioAlgorithm>& processor) {
|
||||||
|
processor_ = processor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sp<AudioAlgorithm>& AAH_AudioProcessor::getAlgorithm() {
|
||||||
|
return processor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AAH_AudioProcessor::shutdown_l() {
|
||||||
|
status_t ret = AAH_DecoderPumpBase::shutdown_l();
|
||||||
|
LOGV("Shutdown AAH_AudioProcessor");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAH_AudioProcessor::queueToSink(MediaBuffer* decoded_frames) {
|
||||||
|
sp<MetaData> meta;
|
||||||
|
int64_t ts;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
// Fetch the metadata and make sure the sample has a timestamp. We
|
||||||
|
// cannot process samples which are missing PTSs.
|
||||||
|
meta = decoded_frames->meta_data();
|
||||||
|
if ((meta == NULL) || (!meta->findInt64(kKeyTime, &ts))) {
|
||||||
|
LOGV("Decoded sample missing timestamp, cannot process.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processor_->initialize(kFFTSize, format_sample_rate_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* decoded_data = reinterpret_cast<uint8_t*>(decoded_frames->data());
|
||||||
|
uint32_t decoded_amt = decoded_frames->range_length();
|
||||||
|
decoded_data += decoded_frames->range_offset();
|
||||||
|
|
||||||
|
// timestamp for the current workspace start position is calculated by
|
||||||
|
// current ts minus filled samples.
|
||||||
|
int64_t start_ts = ts - SAMPLES_TO_TS(filled_, format_sample_rate_);
|
||||||
|
|
||||||
|
// following code is an excerpt of system visualizer, the differences are
|
||||||
|
// in three places in order to get a more accurate output fft value
|
||||||
|
// - full 16 bits are kept comparing to dynamic shifting in system
|
||||||
|
// visualizer
|
||||||
|
// - full audio stream are processed unlike the "sparse" sampling in system
|
||||||
|
// visualizer
|
||||||
|
// - system visualizer uses a weird dynamic shifting down of output fft
|
||||||
|
// values, we output full 16 bits
|
||||||
|
|
||||||
|
uint32_t sampleBytes = 2; // android system assumes 16bits for now
|
||||||
|
uint32_t frameBytes = sampleBytes * format_channels_;
|
||||||
|
int loopcount = 0; // how many fft chunks have been sent to algorithm
|
||||||
|
while (decoded_amt >= frameBytes * 2) { // at least two samples
|
||||||
|
uint32_t decoded_frames = decoded_amt / frameBytes;
|
||||||
|
decoded_frames &= (~1); // only handle even samples
|
||||||
|
uint32_t to_fill = MIN(kFFTSize - filled_, decoded_frames);
|
||||||
|
uint32_t to_fill_bytes = to_fill * frameBytes;
|
||||||
|
|
||||||
|
// workspace is array of 32bits integer, each 32bits has two samples.
|
||||||
|
// The integer order in CPU register is "S1 00 S2 00" from high to low.
|
||||||
|
// In memory, the workspace layout depends on endian order.
|
||||||
|
// In another word, memory layout is different on different endian
|
||||||
|
// system, but when they are read into CPU 32bits register, they are the
|
||||||
|
// same order to perform arithmetic and bitwise operations
|
||||||
|
// For details see fixedfft.cpp
|
||||||
|
int32_t* dst = workspace_ + (filled_ >> 1);
|
||||||
|
switch (sampleBytes) {
|
||||||
|
case 2:
|
||||||
|
if (!fillWorkspace<16, int16_t>(
|
||||||
|
dst, reinterpret_cast<int16_t*>(decoded_data), to_fill,
|
||||||
|
format_channels_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!fillWorkspace<8, int8_t>(
|
||||||
|
dst, reinterpret_cast<int8_t*>(decoded_data), to_fill,
|
||||||
|
format_channels_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGE("Unsupported sample size %d", sampleBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded_data += to_fill_bytes;
|
||||||
|
decoded_amt -= to_fill_bytes;
|
||||||
|
filled_ += to_fill;
|
||||||
|
|
||||||
|
if (filled_ == kFFTSize) {
|
||||||
|
// workspace_ is full, calcuate fft
|
||||||
|
fixed_fft_real(kFFTSize >> 1, workspace_);
|
||||||
|
// now workspace_ contains 16 bits fft values
|
||||||
|
processor_->process(
|
||||||
|
start_ts + SAMPLES_TO_TS((kFFTSize) * loopcount,
|
||||||
|
format_sample_rate_),
|
||||||
|
workspace_, kFFTSize);
|
||||||
|
// open business for next chunk of kFFTSize samples
|
||||||
|
filled_ = 0;
|
||||||
|
loopcount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAH_AudioProcessor::stopAndCleanupSink() {
|
||||||
|
processor_->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAH_AudioProcessor::flush() {
|
||||||
|
filled_ = 0;
|
||||||
|
if (processor_ != NULL) {
|
||||||
|
processor_->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
53
media/libaah_rtp/aah_audio_processor.h
Normal file
53
media/libaah_rtp/aah_audio_processor.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AAH_AUDIO_PROCESSOR_H__
|
||||||
|
#define __AAH_AUDIO_PROCESSOR_H__
|
||||||
|
|
||||||
|
#include "aah_decoder_pump.h"
|
||||||
|
#include "aah_audio_algorithm.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
// decode audio, calculate fft and invoke AudioAlgorithm
|
||||||
|
class AAH_AudioProcessor : public AAH_DecoderPumpBase {
|
||||||
|
public:
|
||||||
|
explicit AAH_AudioProcessor(OMXClient& omx);
|
||||||
|
void flush();
|
||||||
|
void setAlgorithm(const sp<AudioAlgorithm>& processor);
|
||||||
|
const sp<AudioAlgorithm>& getAlgorithm();
|
||||||
|
virtual ~AAH_AudioProcessor();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// fft array size must be 2^n
|
||||||
|
static const uint32_t kFFTSize = (1 << 10);
|
||||||
|
|
||||||
|
uint32_t filled_;
|
||||||
|
int32_t workspace_[kFFTSize >> 1];
|
||||||
|
|
||||||
|
sp<AudioAlgorithm> processor_;
|
||||||
|
|
||||||
|
virtual void queueToSink(MediaBuffer* decoded_sample);
|
||||||
|
virtual void stopAndCleanupSink();
|
||||||
|
virtual status_t shutdown_l();
|
||||||
|
|
||||||
|
DISALLOW_EVIL_CONSTRUCTORS (AAH_AudioProcessor);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // __AAH_AUDIO_PROCESSOR_H__
|
||||||
@@ -40,24 +40,17 @@ static const long long kLongDecodeErrorThreshold = 1000000ll;
|
|||||||
static const uint32_t kMaxLongErrorsBeforeFatal = 3;
|
static const uint32_t kMaxLongErrorsBeforeFatal = 3;
|
||||||
static const uint32_t kMaxErrorsBeforeFatal = 60;
|
static const uint32_t kMaxErrorsBeforeFatal = 60;
|
||||||
|
|
||||||
AAH_DecoderPump::AAH_DecoderPump(OMXClient& omx)
|
AAH_DecoderPumpBase::AAH_DecoderPumpBase(OMXClient& omx)
|
||||||
: omx_(omx)
|
: omx_(omx)
|
||||||
, thread_status_(OK)
|
, thread_status_(OK) {
|
||||||
, renderer_(NULL)
|
|
||||||
, last_queued_pts_valid_(false)
|
|
||||||
, last_queued_pts_(0)
|
|
||||||
, last_ts_transform_valid_(false)
|
|
||||||
, last_left_volume_(1.0f)
|
|
||||||
, last_right_volume_(1.0f)
|
|
||||||
, last_stream_type_(AUDIO_STREAM_DEFAULT) {
|
|
||||||
thread_ = new ThreadWrapper(this);
|
thread_ = new ThreadWrapper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
AAH_DecoderPump::~AAH_DecoderPump() {
|
AAH_DecoderPumpBase::~AAH_DecoderPumpBase() {
|
||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AAH_DecoderPump::initCheck() {
|
status_t AAH_DecoderPumpBase::initCheck() {
|
||||||
if (thread_ == NULL) {
|
if (thread_ == NULL) {
|
||||||
LOGE("Failed to allocate thread");
|
LOGE("Failed to allocate thread");
|
||||||
return NO_MEMORY;
|
return NO_MEMORY;
|
||||||
@@ -66,7 +59,7 @@ status_t AAH_DecoderPump::initCheck() {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AAH_DecoderPump::queueForDecode(MediaBuffer* buf) {
|
status_t AAH_DecoderPumpBase::queueForDecode(MediaBuffer* buf) {
|
||||||
if (NULL == buf) {
|
if (NULL == buf) {
|
||||||
return BAD_VALUE;
|
return BAD_VALUE;
|
||||||
}
|
}
|
||||||
@@ -85,7 +78,7 @@ status_t AAH_DecoderPump::queueForDecode(MediaBuffer* buf) {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AAH_DecoderPump::queueToRenderer(MediaBuffer* decoded_sample) {
|
void AAH_DecoderPump::queueToSink(MediaBuffer* decoded_sample) {
|
||||||
Mutex::Autolock lock(&render_lock_);
|
Mutex::Autolock lock(&render_lock_);
|
||||||
sp<MetaData> meta;
|
sp<MetaData> meta;
|
||||||
int64_t ts;
|
int64_t ts;
|
||||||
@@ -173,7 +166,7 @@ void AAH_DecoderPump::queueToRenderer(MediaBuffer* decoded_sample) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AAH_DecoderPump::stopAndCleanupRenderer() {
|
void AAH_DecoderPump::stopAndCleanupSink() {
|
||||||
if (NULL == renderer_) {
|
if (NULL == renderer_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -297,7 +290,7 @@ bool AAH_DecoderPump::isAboutToUnderflow(int64_t threshold) {
|
|||||||
return ((tt_now + threshold - last_queued_pts_tt) > 0);
|
return ((tt_now + threshold - last_queued_pts_tt) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* AAH_DecoderPump::workThread() {
|
void* AAH_DecoderPumpBase::workThread() {
|
||||||
// No need to lock when accessing decoder_ from the thread. The
|
// No need to lock when accessing decoder_ from the thread. The
|
||||||
// implementation of init and shutdown ensure that other threads never touch
|
// implementation of init and shutdown ensure that other threads never touch
|
||||||
// decoder_ while the work thread is running.
|
// decoder_ while the work thread is running.
|
||||||
@@ -367,7 +360,7 @@ void* AAH_DecoderPump::workThread() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the format has actually changed, destroy our current renderer
|
// If the format has actually changed, destroy our current renderer
|
||||||
// so that a new one can be created during queueToRenderer with the
|
// so that a new one can be created during queueToSink with the
|
||||||
// proper format.
|
// proper format.
|
||||||
//
|
//
|
||||||
// TODO : In order to transition seamlessly, we should change this
|
// TODO : In order to transition seamlessly, we should change this
|
||||||
@@ -375,7 +368,7 @@ void* AAH_DecoderPump::workThread() {
|
|||||||
// we destroy it. We can still create a new renderer, the timed
|
// we destroy it. We can still create a new renderer, the timed
|
||||||
// nature of the renderer should ensure a seamless splice.
|
// nature of the renderer should ensure a seamless splice.
|
||||||
if (formatActuallyChanged)
|
if (formatActuallyChanged)
|
||||||
stopAndCleanupRenderer();
|
stopAndCleanupSink();
|
||||||
|
|
||||||
res = OK;
|
res = OK;
|
||||||
}
|
}
|
||||||
@@ -445,17 +438,17 @@ void* AAH_DecoderPump::workThread() {
|
|||||||
consecutive_errors = 0;
|
consecutive_errors = 0;
|
||||||
consecutive_long_errors = 0;
|
consecutive_long_errors = 0;
|
||||||
|
|
||||||
queueToRenderer(bufOut);
|
queueToSink(bufOut);
|
||||||
bufOut->release();
|
bufOut->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder_->stop();
|
decoder_->stop();
|
||||||
stopAndCleanupRenderer();
|
stopAndCleanupSink();
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AAH_DecoderPump::init(sp<MetaData> params) {
|
status_t AAH_DecoderPumpBase::init(sp<MetaData> params) {
|
||||||
Mutex::Autolock lock(&init_lock_);
|
Mutex::Autolock lock(&init_lock_);
|
||||||
|
|
||||||
if (decoder_ != NULL) {
|
if (decoder_ != NULL) {
|
||||||
@@ -511,12 +504,12 @@ bailout:
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AAH_DecoderPump::shutdown() {
|
status_t AAH_DecoderPumpBase::shutdown() {
|
||||||
Mutex::Autolock lock(&init_lock_);
|
Mutex::Autolock lock(&init_lock_);
|
||||||
return shutdown_l();
|
return shutdown_l();
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AAH_DecoderPump::shutdown_l() {
|
status_t AAH_DecoderPumpBase::shutdown_l() {
|
||||||
thread_->requestExit();
|
thread_->requestExit();
|
||||||
thread_cond_.signal();
|
thread_cond_.signal();
|
||||||
thread_->requestExitAndWait();
|
thread_->requestExitAndWait();
|
||||||
@@ -527,10 +520,6 @@ status_t AAH_DecoderPump::shutdown_l() {
|
|||||||
}
|
}
|
||||||
in_queue_.clear();
|
in_queue_.clear();
|
||||||
|
|
||||||
last_queued_pts_valid_ = false;
|
|
||||||
last_ts_transform_valid_ = false;
|
|
||||||
last_left_volume_ = 1.0f;
|
|
||||||
last_right_volume_ = 1.0f;
|
|
||||||
thread_status_ = OK;
|
thread_status_ = OK;
|
||||||
|
|
||||||
decoder_ = NULL;
|
decoder_ = NULL;
|
||||||
@@ -539,7 +528,7 @@ status_t AAH_DecoderPump::shutdown_l() {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AAH_DecoderPump::read(MediaBuffer **buffer,
|
status_t AAH_DecoderPumpBase::read(MediaBuffer **buffer,
|
||||||
const ReadOptions *options) {
|
const ReadOptions *options) {
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
return BAD_VALUE;
|
return BAD_VALUE;
|
||||||
@@ -566,15 +555,35 @@ status_t AAH_DecoderPump::read(MediaBuffer **buffer,
|
|||||||
return (NULL == *buffer) ? INVALID_OPERATION : OK;
|
return (NULL == *buffer) ? INVALID_OPERATION : OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
AAH_DecoderPump::ThreadWrapper::ThreadWrapper(AAH_DecoderPump* owner)
|
AAH_DecoderPumpBase::ThreadWrapper::ThreadWrapper(AAH_DecoderPumpBase* owner)
|
||||||
: Thread(false /* canCallJava*/ )
|
: Thread(false /* canCallJava*/ )
|
||||||
, owner_(owner) {
|
, owner_(owner) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AAH_DecoderPump::ThreadWrapper::threadLoop() {
|
bool AAH_DecoderPumpBase::ThreadWrapper::threadLoop() {
|
||||||
CHECK(NULL != owner_);
|
CHECK(NULL != owner_);
|
||||||
owner_->workThread();
|
owner_->workThread();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AAH_DecoderPump::AAH_DecoderPump(OMXClient& omx)
|
||||||
|
: AAH_DecoderPumpBase(omx)
|
||||||
|
, renderer_(NULL)
|
||||||
|
, last_queued_pts_valid_(false)
|
||||||
|
, last_queued_pts_(0)
|
||||||
|
, last_ts_transform_valid_(false)
|
||||||
|
, last_left_volume_(1.0f)
|
||||||
|
, last_right_volume_(1.0f)
|
||||||
|
, last_stream_type_(AUDIO_STREAM_DEFAULT) {
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AAH_DecoderPump::shutdown_l() {
|
||||||
|
status_t ret = AAH_DecoderPumpBase::shutdown_l();
|
||||||
|
last_queued_pts_valid_ = false;
|
||||||
|
last_ts_transform_valid_ = false;
|
||||||
|
last_left_volume_ = 1.0f;
|
||||||
|
last_right_volume_ = 1.0f;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -31,9 +31,11 @@ class MetaData;
|
|||||||
class OMXClient;
|
class OMXClient;
|
||||||
class TimedAudioTrack;
|
class TimedAudioTrack;
|
||||||
|
|
||||||
class AAH_DecoderPump : public MediaSource {
|
// omx decoder wrapper, how to process the output buffer is to be defined in sub
|
||||||
|
// classes such as android audio render or an audio processor
|
||||||
|
class AAH_DecoderPumpBase : public MediaSource {
|
||||||
public:
|
public:
|
||||||
explicit AAH_DecoderPump(OMXClient& omx);
|
explicit AAH_DecoderPumpBase(OMXClient& omx);
|
||||||
status_t initCheck();
|
status_t initCheck();
|
||||||
|
|
||||||
status_t queueForDecode(MediaBuffer* buf);
|
status_t queueForDecode(MediaBuffer* buf);
|
||||||
@@ -41,10 +43,6 @@ class AAH_DecoderPump : public MediaSource {
|
|||||||
status_t init(sp<MetaData> params);
|
status_t init(sp<MetaData> params);
|
||||||
status_t shutdown();
|
status_t shutdown();
|
||||||
|
|
||||||
void setRenderTSTransform(const LinearTransform& trans);
|
|
||||||
void setRenderVolume(float left, float right);
|
|
||||||
void setRenderStreamType(int stream_type);
|
|
||||||
bool isAboutToUnderflow(int64_t threshold);
|
|
||||||
bool getStatus() const { return thread_status_; }
|
bool getStatus() const { return thread_status_; }
|
||||||
|
|
||||||
// MediaSource methods
|
// MediaSource methods
|
||||||
@@ -55,29 +53,32 @@ class AAH_DecoderPump : public MediaSource {
|
|||||||
const ReadOptions *options);
|
const ReadOptions *options);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~AAH_DecoderPump();
|
virtual ~AAH_DecoderPumpBase();
|
||||||
|
|
||||||
|
virtual status_t shutdown_l();
|
||||||
|
|
||||||
|
sp<MetaData> format_;
|
||||||
|
int32_t format_channels_;
|
||||||
|
int32_t format_sample_rate_;
|
||||||
|
|
||||||
|
CCHelper cc_helper_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class ThreadWrapper : public Thread {
|
class ThreadWrapper : public Thread {
|
||||||
public:
|
public:
|
||||||
friend class AAH_DecoderPump;
|
friend class AAH_DecoderPumpBase;
|
||||||
explicit ThreadWrapper(AAH_DecoderPump* owner);
|
explicit ThreadWrapper(AAH_DecoderPumpBase* owner);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool threadLoop();
|
virtual bool threadLoop();
|
||||||
AAH_DecoderPump* owner_;
|
AAH_DecoderPumpBase* owner_;
|
||||||
|
|
||||||
DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper);
|
DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper);
|
||||||
};
|
};
|
||||||
|
|
||||||
void* workThread();
|
void* workThread();
|
||||||
virtual status_t shutdown_l();
|
virtual void queueToSink(MediaBuffer* decoded_sample) = 0;
|
||||||
void queueToRenderer(MediaBuffer* decoded_sample);
|
virtual void stopAndCleanupSink() = 0;
|
||||||
void stopAndCleanupRenderer();
|
|
||||||
|
|
||||||
sp<MetaData> format_;
|
|
||||||
int32_t format_channels_;
|
|
||||||
int32_t format_sample_rate_;
|
|
||||||
|
|
||||||
sp<MediaSource> decoder_;
|
sp<MediaSource> decoder_;
|
||||||
OMXClient& omx_;
|
OMXClient& omx_;
|
||||||
@@ -88,8 +89,26 @@ class AAH_DecoderPump : public MediaSource {
|
|||||||
Mutex thread_lock_;
|
Mutex thread_lock_;
|
||||||
status_t thread_status_;
|
status_t thread_status_;
|
||||||
|
|
||||||
|
// protected by the thread_lock_
|
||||||
|
typedef List<MediaBuffer*> MBQueue;
|
||||||
|
MBQueue in_queue_;
|
||||||
|
|
||||||
|
DISALLOW_EVIL_CONSTRUCTORS(AAH_DecoderPumpBase);
|
||||||
|
};
|
||||||
|
|
||||||
|
// decode audio and write to TimeAudioTrack
|
||||||
|
class AAH_DecoderPump : public AAH_DecoderPumpBase {
|
||||||
|
public:
|
||||||
|
explicit AAH_DecoderPump(OMXClient& omx);
|
||||||
|
void setRenderTSTransform(const LinearTransform& trans);
|
||||||
|
void setRenderVolume(float left, float right);
|
||||||
|
void setRenderStreamType(int stream_type);
|
||||||
|
bool isAboutToUnderflow(int64_t threshold);
|
||||||
|
|
||||||
|
private:
|
||||||
Mutex render_lock_;
|
Mutex render_lock_;
|
||||||
TimedAudioTrack* renderer_;
|
TimedAudioTrack* renderer_;
|
||||||
|
|
||||||
bool last_queued_pts_valid_;
|
bool last_queued_pts_valid_;
|
||||||
int64_t last_queued_pts_;
|
int64_t last_queued_pts_;
|
||||||
bool last_ts_transform_valid_;
|
bool last_ts_transform_valid_;
|
||||||
@@ -97,11 +116,10 @@ class AAH_DecoderPump : public MediaSource {
|
|||||||
float last_left_volume_;
|
float last_left_volume_;
|
||||||
float last_right_volume_;
|
float last_right_volume_;
|
||||||
int last_stream_type_;
|
int last_stream_type_;
|
||||||
CCHelper cc_helper_;
|
|
||||||
|
|
||||||
// protected by the thread_lock_
|
virtual void queueToSink(MediaBuffer* decoded_sample);
|
||||||
typedef List<MediaBuffer*> MBQueue;
|
virtual void stopAndCleanupSink();
|
||||||
MBQueue in_queue_;
|
virtual status_t shutdown_l();
|
||||||
|
|
||||||
DISALLOW_EVIL_CONSTRUCTORS(AAH_DecoderPump);
|
DISALLOW_EVIL_CONSTRUCTORS(AAH_DecoderPump);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <utils/threads.h>
|
#include <utils/threads.h>
|
||||||
|
|
||||||
#include "aah_decoder_pump.h"
|
#include "aah_decoder_pump.h"
|
||||||
|
#include "IAAHMetaData.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
@@ -192,6 +193,8 @@ class AAH_RXPlayer : public MediaPlayerHWInterface {
|
|||||||
int32_t ts_lower);
|
int32_t ts_lower);
|
||||||
void processPayloadCont (uint8_t* buf,
|
void processPayloadCont (uint8_t* buf,
|
||||||
uint32_t amt);
|
uint32_t amt);
|
||||||
|
void processMetaData(uint8_t* buf, uint32_t amt);
|
||||||
|
void flushMetaDataService();
|
||||||
void processTSTransform(const LinearTransform& trans);
|
void processTSTransform(const LinearTransform& trans);
|
||||||
|
|
||||||
bool isAboutToUnderflow();
|
bool isAboutToUnderflow();
|
||||||
@@ -260,9 +263,14 @@ class AAH_RXPlayer : public MediaPlayerHWInterface {
|
|||||||
uint8_t audio_volume_remote_;
|
uint8_t audio_volume_remote_;
|
||||||
int audio_stream_type_;
|
int audio_stream_type_;
|
||||||
|
|
||||||
|
MediaToSystemTransform mMetaDataTsTransform;
|
||||||
|
|
||||||
static const int64_t kAboutToUnderflowThreshold;
|
static const int64_t kAboutToUnderflowThreshold;
|
||||||
static const int kInactivityTimeoutMsec;
|
static const int kInactivityTimeoutMsec;
|
||||||
|
|
||||||
|
// singleton of the IBinder service provider for metadata
|
||||||
|
sp<AAHMetaDataService> aah_metadata_service_;
|
||||||
|
|
||||||
DISALLOW_EVIL_CONSTRUCTORS(Substream);
|
DISALLOW_EVIL_CONSTRUCTORS(Substream);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -278,6 +286,8 @@ class AAH_RXPlayer : public MediaPlayerHWInterface {
|
|||||||
bool processRX(PacketBuffer* pb);
|
bool processRX(PacketBuffer* pb);
|
||||||
void processRingBuffer();
|
void processRingBuffer();
|
||||||
void processCommandPacket(PacketBuffer* pb);
|
void processCommandPacket(PacketBuffer* pb);
|
||||||
|
void processMetaDataPacket(uint8_t program_id,
|
||||||
|
PacketBuffer* pb);
|
||||||
bool processGaps();
|
bool processGaps();
|
||||||
bool processRetransmitNAK(const uint8_t* data, size_t amt);
|
bool processRetransmitNAK(const uint8_t* data, size_t amt);
|
||||||
void setGapStatus(GapStatus status);
|
void setGapStatus(GapStatus status);
|
||||||
|
|||||||
@@ -595,6 +595,8 @@ void AAH_RXPlayer::processRingBuffer() {
|
|||||||
goto process_next_packet;
|
goto process_next_packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t program_id = (ssrc >> 5) & 0x1F;
|
||||||
|
|
||||||
// Is there a timestamp transformation present on this packet? If
|
// Is there a timestamp transformation present on this packet? If
|
||||||
// so, extract it and pass it to the appropriate substreams.
|
// so, extract it and pass it to the appropriate substreams.
|
||||||
if (trtp_flags & 0x02) {
|
if (trtp_flags & 0x02) {
|
||||||
@@ -613,7 +615,6 @@ void AAH_RXPlayer::processRingBuffer() {
|
|||||||
trans.a_to_b_denom = U32_AT(data + offset + 12);
|
trans.a_to_b_denom = U32_AT(data + offset + 12);
|
||||||
foundTrans = true;
|
foundTrans = true;
|
||||||
|
|
||||||
uint32_t program_id = (ssrc >> 5) & 0x1F;
|
|
||||||
for (size_t i = 0; i < substreams_.size(); ++i) {
|
for (size_t i = 0; i < substreams_.size(); ++i) {
|
||||||
sp<Substream> iter = substreams_.valueAt(i);
|
sp<Substream> iter = substreams_.valueAt(i);
|
||||||
CHECK(iter != NULL);
|
CHECK(iter != NULL);
|
||||||
@@ -630,6 +631,10 @@ void AAH_RXPlayer::processRingBuffer() {
|
|||||||
if (4 == payload_type) {
|
if (4 == payload_type) {
|
||||||
processCommandPacket(pb);
|
processCommandPacket(pb);
|
||||||
goto process_next_packet;
|
goto process_next_packet;
|
||||||
|
} else if (5 == payload_type) {
|
||||||
|
// if it's MetaDataPacket, send to associated substream
|
||||||
|
processMetaDataPacket(program_id, pb);
|
||||||
|
goto process_next_packet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,6 +743,7 @@ void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) {
|
|||||||
for (size_t i = 0; i < substreams_.size(); ++i) {
|
for (size_t i = 0; i < substreams_.size(); ++i) {
|
||||||
const sp<Substream>& stream = substreams_.valueAt(i);
|
const sp<Substream>& stream = substreams_.valueAt(i);
|
||||||
if (stream->getProgramID() == program_id) {
|
if (stream->getProgramID() == program_id) {
|
||||||
|
stream->flushMetaDataService();
|
||||||
stream->clearInactivityTimeout();
|
stream->clearInactivityTimeout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -795,6 +801,50 @@ void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) {
|
|||||||
cleanoutExpiredSubstreams();
|
cleanoutExpiredSubstreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AAH_RXPlayer::processMetaDataPacket(uint8_t program_id, PacketBuffer* pb) {
|
||||||
|
CHECK(NULL != pb);
|
||||||
|
|
||||||
|
uint8_t* data = pb->data_;
|
||||||
|
ssize_t amt = pb->length_;
|
||||||
|
|
||||||
|
// verify that this packet meets the minimum length of a metadata packet
|
||||||
|
if (amt < 22) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t trtp_version = data[12];
|
||||||
|
uint8_t trtp_flags = data[13] & 0xF;
|
||||||
|
|
||||||
|
if (1 != trtp_version) {
|
||||||
|
LOGV("Dropping packet, bad trtp version %hhu", trtp_version);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the start of the metadata payload
|
||||||
|
ssize_t offset = 18;
|
||||||
|
if (trtp_flags & 0x01) {
|
||||||
|
// timestamp is present (4 bytes)
|
||||||
|
offset += 4;
|
||||||
|
// we don't sent timestamp in metadata packet header
|
||||||
|
// however the content of metadata may contain timestamp
|
||||||
|
}
|
||||||
|
if (trtp_flags & 0x02) {
|
||||||
|
// transform is present (24 bytes)
|
||||||
|
offset += 24;
|
||||||
|
// ignore for now, we don't sent transform in metadata packet
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < substreams_.size(); ++i) {
|
||||||
|
sp<Substream> iter = substreams_.valueAt(i);
|
||||||
|
CHECK(iter != NULL);
|
||||||
|
|
||||||
|
if (iter->getProgramID() == program_id) {
|
||||||
|
iter->processMetaData(data + offset, amt - offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool AAH_RXPlayer::processGaps() {
|
bool AAH_RXPlayer::processGaps() {
|
||||||
// Deal with the current gap situation. Specifically...
|
// Deal with the current gap situation. Specifically...
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -28,8 +28,7 @@
|
|||||||
|
|
||||||
#include "aah_rx_player.h"
|
#include "aah_rx_player.h"
|
||||||
#include "aah_tx_packet.h"
|
#include "aah_tx_packet.h"
|
||||||
|
#include "aah_audio_algorithm.h"
|
||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -57,6 +56,8 @@ AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) {
|
|||||||
LOGE("%s failed to initialize decoder pump!", __PRETTY_FUNCTION__);
|
LOGE("%s failed to initialize decoder pump!", __PRETTY_FUNCTION__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aah_metadata_service_ = AAHMetaDataService::getInstance();
|
||||||
|
|
||||||
// cleanupBufferInProgress will reset most of the internal state variables.
|
// cleanupBufferInProgress will reset most of the internal state variables.
|
||||||
// Just need to make sure that buffer_in_progress_ is NULL before calling.
|
// Just need to make sure that buffer_in_progress_ is NULL before calling.
|
||||||
cleanupBufferInProgress();
|
cleanupBufferInProgress();
|
||||||
@@ -642,6 +643,9 @@ void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) {
|
|||||||
if (decoder_ != NULL) {
|
if (decoder_ != NULL) {
|
||||||
decoder_->setRenderTSTransform(trans);
|
decoder_->setRenderTSTransform(trans);
|
||||||
}
|
}
|
||||||
|
if (aah_metadata_service_ != NULL) {
|
||||||
|
mMetaDataTsTransform.setMediaToCommonTransform(trans);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AAH_RXPlayer::Substream::signalEOS() {
|
void AAH_RXPlayer::Substream::signalEOS() {
|
||||||
@@ -732,4 +736,44 @@ void AAH_RXPlayer::Substream::applyVolume() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AAH_RXPlayer::Substream::processMetaData(uint8_t* data, uint32_t amt) {
|
||||||
|
if (aah_metadata_service_ == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t offset = 0;
|
||||||
|
// the packet must contain 4 bytes of TRTPMetaDataBlock payload
|
||||||
|
// beyond the TRTP header
|
||||||
|
while (offset <= amt - 4) {
|
||||||
|
uint16_t typeId = U16_AT(data + offset);
|
||||||
|
uint16_t item_length = U16_AT(data + offset + 2);
|
||||||
|
offset += 4;
|
||||||
|
if (offset <= amt - item_length) {
|
||||||
|
if (kMetaDataBeat == typeId) {
|
||||||
|
uint16_t count = U16_AT(data + offset);
|
||||||
|
uint8_t* buf = data + offset + 2;
|
||||||
|
mMetaDataTsTransform.prepareCommonToSystem();
|
||||||
|
for (int i = 0, c = count * BeatDetectionAlgorithm::kItemLength;
|
||||||
|
i < c;
|
||||||
|
i += BeatDetectionAlgorithm::kItemLength) {
|
||||||
|
uint8_t* ptr = buf + i;
|
||||||
|
int64_t ts = static_cast<int64_t>(U64_AT(ptr));
|
||||||
|
mMetaDataTsTransform.mediaToSystem(&ts);
|
||||||
|
TRTPPacket::writeU64(ptr, ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: in future, we may pass programID to java layer to identify
|
||||||
|
// the song; the java layer has no knowledge of ID at this moment
|
||||||
|
aah_metadata_service_->broadcast(typeId, item_length,
|
||||||
|
data + offset);
|
||||||
|
}
|
||||||
|
offset += item_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAH_RXPlayer::Substream::flushMetaDataService() {
|
||||||
|
if (aah_metadata_service_ != NULL) {
|
||||||
|
aah_metadata_service_->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -19,8 +19,7 @@
|
|||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <media/stagefright/foundation/ADebug.h>
|
||||||
#include <media/stagefright/MediaDebug.h>
|
|
||||||
|
|
||||||
#include "aah_tx_packet.h"
|
#include "aah_tx_packet.h"
|
||||||
|
|
||||||
@@ -190,6 +189,12 @@ void TRTPPacket::writeU64(uint8_t*& buf, uint64_t val) {
|
|||||||
buf += 8;
|
buf += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TRTPPacket::writeFloat(uint8_t*& buf, float val) {
|
||||||
|
uint32_t intBits = *(reinterpret_cast<uint32_t *>(&val));
|
||||||
|
*reinterpret_cast<uint32_t*>(buf) = htonl(intBits);
|
||||||
|
buf += 4;
|
||||||
|
}
|
||||||
|
|
||||||
void TRTPPacket::writeTRTPHeader(uint8_t*& buf,
|
void TRTPPacket::writeTRTPHeader(uint8_t*& buf,
|
||||||
bool isFirstFragment,
|
bool isFirstFragment,
|
||||||
int totalPacketLen) {
|
int totalPacketLen) {
|
||||||
@@ -381,4 +386,58 @@ bool TRTPActiveProgramUpdatePacket::pack() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TRTPMetaDataBlock::writeBlockHead(uint8_t*& buf) const {
|
||||||
|
TRTPPacket::writeU16(buf, mTypeId);
|
||||||
|
TRTPPacket::writeU16(buf, mItemLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRTPMetaDataPacket::~TRTPMetaDataPacket() {
|
||||||
|
for (size_t i=0; i < mBlocks.size(); i++) {
|
||||||
|
delete mBlocks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TRTPMetaDataPacket::append(TRTPMetaDataBlock* block) {
|
||||||
|
mBlocks.add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TRTPMetaDataPacket::pack() {
|
||||||
|
if (mIsPacked) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active program update packets contain a list of block, each block
|
||||||
|
// is 2 bytes typeId, 2 bytes item_length and item_length buffer
|
||||||
|
int packetLen = kRTPHeaderLen + TRTPHeaderLen();
|
||||||
|
for (size_t i = 0; i < mBlocks.size(); i++) {
|
||||||
|
const TRTPMetaDataBlock* block = mBlocks[i];
|
||||||
|
packetLen += 4 + block->mItemLength;
|
||||||
|
}
|
||||||
|
// compare to maximum UDP payload length
|
||||||
|
// (IP header 20, UDP header 8)
|
||||||
|
// TODO: split by MTU (normally 1500)
|
||||||
|
if (packetLen > (0xffff - 28)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPacket = new uint8_t[packetLen];
|
||||||
|
if (!mPacket) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPacketLen = packetLen;
|
||||||
|
|
||||||
|
uint8_t* cur = mPacket;
|
||||||
|
|
||||||
|
writeTRTPHeader(cur, true, packetLen);
|
||||||
|
for (size_t i = 0; i < mBlocks.size(); i++) {
|
||||||
|
const TRTPMetaDataBlock* block = mBlocks[i];
|
||||||
|
block->write(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsPacked = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <utils/LinearTransform.h>
|
#include <utils/LinearTransform.h>
|
||||||
#include <utils/RefBase.h>
|
#include <utils/RefBase.h>
|
||||||
#include <utils/Timers.h>
|
#include <utils/Timers.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ class TRTPPacket : public RefBase {
|
|||||||
kHeaderTypeVideo = 2,
|
kHeaderTypeVideo = 2,
|
||||||
kHeaderTypeSubpicture = 3,
|
kHeaderTypeSubpicture = 3,
|
||||||
kHeaderTypeControl = 4,
|
kHeaderTypeControl = 4,
|
||||||
|
kHeaderTypeMetaData = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
TRTPPacket(TRTPHeaderType headerType)
|
TRTPPacket(TRTPHeaderType headerType)
|
||||||
@@ -86,6 +88,12 @@ class TRTPPacket : public RefBase {
|
|||||||
static const uint32_t kCNC_LeaveGroupID = 'Tlgp';
|
static const uint32_t kCNC_LeaveGroupID = 'Tlgp';
|
||||||
static const uint32_t kCNC_NakJoinGroupID = 'Tngp';
|
static const uint32_t kCNC_NakJoinGroupID = 'Tngp';
|
||||||
|
|
||||||
|
static void writeU8(uint8_t*& buf, uint8_t val);
|
||||||
|
static void writeU16(uint8_t*& buf, uint16_t val);
|
||||||
|
static void writeU32(uint8_t*& buf, uint32_t val);
|
||||||
|
static void writeU64(uint8_t*& buf, uint64_t val);
|
||||||
|
static void writeFloat(uint8_t*& buf, float val);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const int kRTPHeaderLen = 12;
|
static const int kRTPHeaderLen = 12;
|
||||||
virtual int TRTPHeaderLen() const;
|
virtual int TRTPHeaderLen() const;
|
||||||
@@ -94,11 +102,6 @@ class TRTPPacket : public RefBase {
|
|||||||
bool isFirstFragment,
|
bool isFirstFragment,
|
||||||
int totalPacketLen);
|
int totalPacketLen);
|
||||||
|
|
||||||
void writeU8(uint8_t*& buf, uint8_t val);
|
|
||||||
void writeU16(uint8_t*& buf, uint16_t val);
|
|
||||||
void writeU32(uint8_t*& buf, uint32_t val);
|
|
||||||
void writeU64(uint8_t*& buf, uint64_t val);
|
|
||||||
|
|
||||||
bool mIsPacked;
|
bool mIsPacked;
|
||||||
|
|
||||||
uint8_t mVersion;
|
uint8_t mVersion;
|
||||||
@@ -215,6 +218,42 @@ class TRTPActiveProgramUpdatePacket : public TRTPControlPacket {
|
|||||||
uint8_t mProgramIDs[kMaxProgramIDs];
|
uint8_t mProgramIDs[kMaxProgramIDs];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum TRTPMetaDataTypeID {
|
||||||
|
kMetaDataNone = 0,
|
||||||
|
kMetaDataBeat = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class TRTPMetaDataBlock {
|
||||||
|
public:
|
||||||
|
TRTPMetaDataBlock(TRTPMetaDataTypeID typeId, uint16_t item_length)
|
||||||
|
: mTypeId(typeId)
|
||||||
|
, mItemLength(item_length) {}
|
||||||
|
void writeBlockHead(uint8_t*& buf) const;
|
||||||
|
virtual void write(uint8_t*& buf) const = 0;
|
||||||
|
virtual ~TRTPMetaDataBlock() {}
|
||||||
|
|
||||||
|
TRTPMetaDataTypeID mTypeId;
|
||||||
|
uint16_t mItemLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TRTPMetaDataPacket contains multiple TRTPMetaDataBlocks of different types
|
||||||
|
class TRTPMetaDataPacket : public TRTPPacket {
|
||||||
|
public:
|
||||||
|
TRTPMetaDataPacket()
|
||||||
|
: TRTPPacket(kHeaderTypeMetaData) {}
|
||||||
|
|
||||||
|
void append(TRTPMetaDataBlock* block);
|
||||||
|
|
||||||
|
virtual bool pack();
|
||||||
|
|
||||||
|
virtual ~TRTPMetaDataPacket();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Vector<TRTPMetaDataBlock*> mBlocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
||||||
#endif // __AAH_TX_PLAYER_H__
|
#endif // __AAH_TX_PLAYER_H__
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <media/stagefright/foundation/AMessage.h>
|
#include <media/stagefright/foundation/AMessage.h>
|
||||||
#include <media/stagefright/FileSource.h>
|
#include <media/stagefright/FileSource.h>
|
||||||
#include <media/stagefright/MediaBuffer.h>
|
#include <media/stagefright/MediaBuffer.h>
|
||||||
#include <media/stagefright/MediaDebug.h>
|
|
||||||
#include <media/stagefright/MediaDefs.h>
|
#include <media/stagefright/MediaDefs.h>
|
||||||
#include <media/stagefright/MetaData.h>
|
#include <media/stagefright/MetaData.h>
|
||||||
#include <utils/Timers.h>
|
#include <utils/Timers.h>
|
||||||
@@ -114,6 +113,10 @@ AAH_TXPlayer::~AAH_TXPlayer() {
|
|||||||
if (mQueueStarted) {
|
if (mQueueStarted) {
|
||||||
mQueue.stop();
|
mQueue.stop();
|
||||||
}
|
}
|
||||||
|
if (mAudioProcessor != NULL) {
|
||||||
|
mAudioProcessor->shutdown();
|
||||||
|
}
|
||||||
|
omx_.disconnect();
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@@ -140,7 +143,7 @@ status_t AAH_TXPlayer::initCheck() {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return omx_.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AAH_TXPlayer::setDataSource(
|
status_t AAH_TXPlayer::setDataSource(
|
||||||
@@ -486,6 +489,25 @@ void AAH_TXPlayer::onPrepareAsyncEvent() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mAudioProcessor == NULL) {
|
||||||
|
mAudioProcessor = new AAH_AudioProcessor(omx_);
|
||||||
|
if (mAudioProcessor == NULL || OK != mAudioProcessor->initCheck()) {
|
||||||
|
LOGE("%s failed to initialize audio processor",
|
||||||
|
__PRETTY_FUNCTION__);
|
||||||
|
mAudioProcessor = NULL;
|
||||||
|
abortPrepare_l(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mEnergyProcessor == NULL) {
|
||||||
|
mEnergyProcessor = new BeatDetectionAlgorithm();
|
||||||
|
if (mEnergyProcessor == NULL) {
|
||||||
|
LOGE("%s failed to initialize audio processor",
|
||||||
|
__PRETTY_FUNCTION__);
|
||||||
|
}
|
||||||
|
mAudioProcessor->setAlgorithm(mEnergyProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
mFlags |= PREPARING_CONNECTED;
|
mFlags |= PREPARING_CONNECTED;
|
||||||
|
|
||||||
if (mCachedSource != NULL) {
|
if (mCachedSource != NULL) {
|
||||||
@@ -642,6 +664,10 @@ void AAH_TXPlayer::sendEOS_l() {
|
|||||||
|
|
||||||
void AAH_TXPlayer::sendFlush_l() {
|
void AAH_TXPlayer::sendFlush_l() {
|
||||||
if (mAAH_TXGroup != NULL) {
|
if (mAAH_TXGroup != NULL) {
|
||||||
|
if (mEnergyProcessor != NULL) {
|
||||||
|
mEnergyProcessor->flush();
|
||||||
|
}
|
||||||
|
sendMetaPacket_l(true);
|
||||||
sp<TRTPControlPacket> packet = new TRTPControlPacket();
|
sp<TRTPControlPacket> packet = new TRTPControlPacket();
|
||||||
if (packet != NULL) {
|
if (packet != NULL) {
|
||||||
packet->setCommandID(TRTPControlPacket::kCommandFlush);
|
packet->setCommandID(TRTPControlPacket::kCommandFlush);
|
||||||
@@ -1349,8 +1375,26 @@ void AAH_TXPlayer::onPumpAudio() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaBuffer->release();
|
if (mAudioProcessor != NULL) {
|
||||||
|
// FIXME init() should detect metadata change
|
||||||
|
status_t res = mAudioProcessor->init(mAudioFormat);
|
||||||
|
if (OK != res) {
|
||||||
|
LOGE("Failed to init decoder (res = %d)", res);
|
||||||
|
cleanupDecoder();
|
||||||
|
mediaBuffer->release();
|
||||||
|
} else {
|
||||||
|
// decoder_pump steals the reference, so no need to call release()
|
||||||
|
res = mAudioProcessor->queueForDecode(mediaBuffer);
|
||||||
|
if (OK != res) {
|
||||||
|
LOGE("Failed in queueForDecode (res = %d)", res);
|
||||||
|
cleanupDecoder();
|
||||||
|
mediaBuffer->release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendMetaPacket_l(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bailout:
|
||||||
mLastQueuedMediaTimePTSValid = true;
|
mLastQueuedMediaTimePTSValid = true;
|
||||||
mLastQueuedMediaTimePTS = mediaTimeUs;
|
mLastQueuedMediaTimePTS = mediaTimeUs;
|
||||||
}
|
}
|
||||||
@@ -1371,6 +1415,20 @@ void AAH_TXPlayer::onPumpAudio() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AAH_TXPlayer::sendMetaPacket_l(bool flushOut) {
|
||||||
|
// collect metadata from different components and send TRTPMetaDataPacket
|
||||||
|
// currently only support beat processor
|
||||||
|
if (mEnergyProcessor == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TRTPMetaDataBlock* block = mEnergyProcessor->collectMetaData(flushOut);
|
||||||
|
if (block != NULL) {
|
||||||
|
sp<TRTPMetaDataPacket> packet = new TRTPMetaDataPacket();
|
||||||
|
packet->append(block);
|
||||||
|
sendPacket_l(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AAH_TXPlayer::sendPacket_l(const sp<TRTPPacket>& packet) {
|
void AAH_TXPlayer::sendPacket_l(const sp<TRTPPacket>& packet) {
|
||||||
CHECK(mAAH_TXGroup != NULL);
|
CHECK(mAAH_TXGroup != NULL);
|
||||||
CHECK(packet != NULL);
|
CHECK(packet != NULL);
|
||||||
@@ -1378,4 +1436,11 @@ void AAH_TXPlayer::sendPacket_l(const sp<TRTPPacket>& packet) {
|
|||||||
mAAH_TXGroup->sendPacket(packet);
|
mAAH_TXGroup->sendPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AAH_TXPlayer::cleanupDecoder() {
|
||||||
|
if (mAudioProcessor != NULL) {
|
||||||
|
mAudioProcessor->shutdown();
|
||||||
|
mAudioProcessor = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -24,11 +24,16 @@
|
|||||||
#include <media/MediaPlayerInterface.h>
|
#include <media/MediaPlayerInterface.h>
|
||||||
#include <media/stagefright/MediaExtractor.h>
|
#include <media/stagefright/MediaExtractor.h>
|
||||||
#include <media/stagefright/MediaSource.h>
|
#include <media/stagefright/MediaSource.h>
|
||||||
|
#include <media/stagefright/MediaBuffer.h>
|
||||||
|
#include <media/stagefright/MediaDefs.h>
|
||||||
|
#include <media/stagefright/MetaData.h>
|
||||||
|
#include <media/stagefright/OMXClient.h>
|
||||||
#include <utils/LinearTransform.h>
|
#include <utils/LinearTransform.h>
|
||||||
#include <utils/String8.h>
|
#include <utils/String8.h>
|
||||||
#include <utils/threads.h>
|
#include <utils/threads.h>
|
||||||
|
|
||||||
#include "aah_tx_group.h"
|
#include "aah_tx_group.h"
|
||||||
|
#include "aah_audio_processor.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -109,6 +114,7 @@ class AAH_TXPlayer : public MediaPlayerHWInterface {
|
|||||||
void updateClockTransform_l(bool pause);
|
void updateClockTransform_l(bool pause);
|
||||||
void sendEOS_l();
|
void sendEOS_l();
|
||||||
void sendFlush_l();
|
void sendFlush_l();
|
||||||
|
void sendMetaPacket_l(bool flushOut);
|
||||||
void sendTSUpdateNop_l();
|
void sendTSUpdateNop_l();
|
||||||
void cancelPlayerEvents(bool keepBufferingGoing = false);
|
void cancelPlayerEvents(bool keepBufferingGoing = false);
|
||||||
void reset_l();
|
void reset_l();
|
||||||
@@ -122,9 +128,14 @@ class AAH_TXPlayer : public MediaPlayerHWInterface {
|
|||||||
void onBufferingUpdate();
|
void onBufferingUpdate();
|
||||||
void onPumpAudio();
|
void onPumpAudio();
|
||||||
void sendPacket_l(const sp<TRTPPacket>& packet);
|
void sendPacket_l(const sp<TRTPPacket>& packet);
|
||||||
|
void cleanupDecoder();
|
||||||
|
|
||||||
Mutex mLock;
|
Mutex mLock;
|
||||||
|
|
||||||
|
OMXClient omx_;
|
||||||
|
sp<AAH_AudioProcessor> mAudioProcessor;
|
||||||
|
sp<AudioAlgorithm> mEnergyProcessor;
|
||||||
|
|
||||||
TimedEventQueue mQueue;
|
TimedEventQueue mQueue;
|
||||||
bool mQueueStarted;
|
bool mQueueStarted;
|
||||||
|
|
||||||
|
|||||||
28
media/libaah_rtp/java/Android.mk
Normal file
28
media/libaah_rtp/java/Android.mk
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Copyright 2012, 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)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
# builds the separate AAHMetaDataService.jar as part of 'make dist'
|
||||||
|
LOCAL_MODULE := AAHMetaDataService
|
||||||
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
LOCAL_SDK_VERSION := 8
|
||||||
|
LOCAL_JAVA_LAYERS_FILE := layers.txt
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
$(call all-java-files-under, src) \
|
||||||
|
$(call all-Iaidl-files-under, src)
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||||
8
media/libaah_rtp/java/layers.txt
Normal file
8
media/libaah_rtp/java/layers.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Dependency file for use by layers.py
|
||||||
|
|
||||||
|
# These packages are high-level and no other packages may import them.
|
||||||
|
android.media.libaah
|
||||||
|
|
||||||
|
# There are no legacy packages, but if there were, they would be listed like this.
|
||||||
|
# They would be allowed to freely import and be imported.
|
||||||
|
# -com.example.some.legacy.package
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.media.libaah;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public interface BeatListener {
|
||||||
|
|
||||||
|
public static class BeatInfo {
|
||||||
|
|
||||||
|
public volatile long timestamp;
|
||||||
|
public volatile float beatValue;
|
||||||
|
public volatile float smoothedBeatValue;
|
||||||
|
public volatile int sequenceNumber;
|
||||||
|
|
||||||
|
public BeatInfo() {
|
||||||
|
this(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatInfo(long t, float b, float sb, int s) {
|
||||||
|
timestamp = t;
|
||||||
|
beatValue = b;
|
||||||
|
smoothedBeatValue = sb;
|
||||||
|
sequenceNumber = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatInfo(BeatInfo other) {
|
||||||
|
copyFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyFrom(BeatInfo other) {
|
||||||
|
timestamp = other.timestamp;
|
||||||
|
beatValue = other.beatValue;
|
||||||
|
smoothedBeatValue = other.smoothedBeatValue;
|
||||||
|
sequenceNumber = other.sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void onBeat(short count, BeatInfo[] info);
|
||||||
|
|
||||||
|
void onFlush();
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.media.libaah;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MetaDataService class enables application to retrieve metadata e.g.
|
||||||
|
* beat information of current played track
|
||||||
|
*/
|
||||||
|
public class MetaDataService {
|
||||||
|
|
||||||
|
protected final static String TAG = "AAHMetaData-JAVA";
|
||||||
|
|
||||||
|
protected List<BeatListener> mBeatListeners;
|
||||||
|
|
||||||
|
protected BeatListener.BeatInfo[] mCachedBeats;
|
||||||
|
|
||||||
|
protected static final int TYPEID_BEAT = 1;
|
||||||
|
|
||||||
|
public static final int BEAT_FIXED_LENGTH = 20;
|
||||||
|
|
||||||
|
protected MetaDataService() {
|
||||||
|
mBeatListeners = new ArrayList<BeatListener>();
|
||||||
|
mCachedBeats = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetaDataService create()
|
||||||
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
|
Class cls = Class.forName("android.media.libaah.MetaDataServiceRtp");
|
||||||
|
return (MetaDataService) cls.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* release native resources, it's suggested to call this instead of relying
|
||||||
|
* on java garbage collection
|
||||||
|
*/
|
||||||
|
public void release() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addBeatListener(BeatListener aahBeatListener) {
|
||||||
|
if (!mBeatListeners.contains(aahBeatListener)) {
|
||||||
|
mBeatListeners.add(aahBeatListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeBeatListener(BeatListener aahBeatListener) {
|
||||||
|
mBeatListeners.remove(aahBeatListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void processBeat(int item_len, byte[] buffer) {
|
||||||
|
if (buffer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, item_len);
|
||||||
|
// buffer is in network order (big endian)
|
||||||
|
byteBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
short count = byteBuffer.getShort();
|
||||||
|
if (count * BEAT_FIXED_LENGTH + 2 != item_len ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mCachedBeats == null || mCachedBeats.length < count) {
|
||||||
|
BeatListener.BeatInfo[] beats = new BeatListener.BeatInfo[count];
|
||||||
|
int i = 0;
|
||||||
|
if (mCachedBeats != null) {
|
||||||
|
for (; i < mCachedBeats.length; i++) {
|
||||||
|
beats[i] = mCachedBeats[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < count; i++) {
|
||||||
|
beats[i] = new BeatListener.BeatInfo();
|
||||||
|
}
|
||||||
|
mCachedBeats = beats;
|
||||||
|
}
|
||||||
|
for (int idx = 0; idx < count; idx++) {
|
||||||
|
mCachedBeats[idx].timestamp = byteBuffer.getLong();
|
||||||
|
mCachedBeats[idx].beatValue = byteBuffer.getFloat();
|
||||||
|
mCachedBeats[idx].smoothedBeatValue = byteBuffer.getFloat();
|
||||||
|
mCachedBeats[idx].sequenceNumber = byteBuffer.getInt();
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
for (int i = 0, c = mBeatListeners.size(); i < c; i++) {
|
||||||
|
mBeatListeners.get(i).onBeat(count, mCachedBeats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void flush() {
|
||||||
|
synchronized (this) {
|
||||||
|
for (int i = 0, c = mBeatListeners.size(); i < c; i++) {
|
||||||
|
mBeatListeners.get(i).onFlush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.media.libaah;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The implementation reads data from libaah_rtp
|
||||||
|
*/
|
||||||
|
public class MetaDataServiceRtp extends MetaDataService {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.loadLibrary("aah_rtp");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static String TAG = "AAHMetaData-JAVA";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of a MetaDataService object that was not successfully
|
||||||
|
* initialized upon creation
|
||||||
|
*/
|
||||||
|
private static final int STATE_UNINITIALIZED = 0;
|
||||||
|
/**
|
||||||
|
* State of a MetaDataService object that is ready to be used.
|
||||||
|
*/
|
||||||
|
private static final int STATE_INITIALIZED = 1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Member variables
|
||||||
|
// --------------------
|
||||||
|
/**
|
||||||
|
* Indicates the state of the MetaDataService instance
|
||||||
|
*/
|
||||||
|
private int mState = STATE_UNINITIALIZED;
|
||||||
|
|
||||||
|
private int mCookie;
|
||||||
|
/*
|
||||||
|
* Successful operation.
|
||||||
|
*/
|
||||||
|
private static final int SUCCESS = 0;
|
||||||
|
/*
|
||||||
|
* Unspecified error.
|
||||||
|
*/
|
||||||
|
private static final int ERROR = -1;
|
||||||
|
/*
|
||||||
|
* Internal operation status. Not returned by any method.
|
||||||
|
*/
|
||||||
|
private static final int ALREADY_EXISTS = -2;
|
||||||
|
/*
|
||||||
|
* Operation failed due to bad object initialization.
|
||||||
|
*/
|
||||||
|
private static final int ERROR_NO_INIT = -3;
|
||||||
|
/*
|
||||||
|
* Operation failed due to bad parameter value.
|
||||||
|
*/
|
||||||
|
private static final int ERROR_BAD_VALUE = -4;
|
||||||
|
/*
|
||||||
|
* Operation failed because it was requested in wrong state.
|
||||||
|
*/
|
||||||
|
private static final int ERROR_INVALID_OPERATION = -5;
|
||||||
|
/*
|
||||||
|
* Operation failed due to lack of memory.
|
||||||
|
*/
|
||||||
|
private static final int ERROR_NO_MEMORY = -6;
|
||||||
|
/*
|
||||||
|
* Operation failed due to dead remote object.
|
||||||
|
*/
|
||||||
|
private static final int ERROR_DEAD_OBJECT = -7;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* only called by MetaDataService.create()
|
||||||
|
*/
|
||||||
|
MetaDataServiceRtp() {
|
||||||
|
mState = STATE_UNINITIALIZED;
|
||||||
|
// native initialization
|
||||||
|
int result = native_setup(new WeakReference<MetaDataServiceRtp>(this));
|
||||||
|
if (result != SUCCESS) {
|
||||||
|
Log.e(TAG, "Error code " + result + " when initializing.");
|
||||||
|
}
|
||||||
|
mState = STATE_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() {
|
||||||
|
native_finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
native_finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable() {
|
||||||
|
native_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
native_disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native final int native_setup(Object metadataservice_this);
|
||||||
|
|
||||||
|
private native final void native_enable();
|
||||||
|
|
||||||
|
private native final void native_disable();
|
||||||
|
|
||||||
|
private native final void native_finalize();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Java methods called from the native side
|
||||||
|
// --------------------
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static void postMetaDataFromNative(Object s_ref,
|
||||||
|
short type, int item_len, byte[] buffer) {
|
||||||
|
MetaDataService service =
|
||||||
|
(MetaDataService) ((WeakReference) s_ref).get();
|
||||||
|
if (service == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case TYPEID_BEAT:
|
||||||
|
service.processBeat(item_len, buffer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "unknown type metadata type " + type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static void flushFromNative(Object s_ref) {
|
||||||
|
MetaDataService service =
|
||||||
|
(MetaDataService) ((WeakReference) s_ref).get();
|
||||||
|
if (service == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
service.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,11 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
#include <utils/SystemClock.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
|
// check ICommonTime every 60 seconds, common to local difference
|
||||||
|
// shouldn't drift a lot
|
||||||
|
#define CHECK_CC_INTERNAL 60000
|
||||||
|
|
||||||
void Timeout::setTimeout(int msec) {
|
void Timeout::setTimeout(int msec) {
|
||||||
if (msec < 0) {
|
if (msec < 0) {
|
||||||
mSystemEndTime = 0;
|
mSystemEndTime = 0;
|
||||||
@@ -46,4 +51,61 @@ int Timeout::msecTillTimeout(nsecs_t nowTime) {
|
|||||||
return static_cast<int>(delta);
|
return static_cast<int>(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommonToSystemTransform::CommonToSystemTransform()
|
||||||
|
: mLastTs(-1) {
|
||||||
|
mCCHelper.getCommonFreq(&mCommonFreq);
|
||||||
|
mCommonToSystem.a_to_b_numer = 1000;
|
||||||
|
mCommonToSystem.a_to_b_denom = mCommonFreq;
|
||||||
|
LinearTransform::reduce(&mCommonToSystem.a_to_b_numer,
|
||||||
|
&mCommonToSystem.a_to_b_denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinearTransform& CommonToSystemTransform::getCommonToSystem() {
|
||||||
|
int64_t st = elapsedRealtime();
|
||||||
|
if (mLastTs == -1 || st - mLastTs > CHECK_CC_INTERNAL) {
|
||||||
|
int64_t ct;
|
||||||
|
mCCHelper.getCommonTime(&ct);
|
||||||
|
mCommonToSystem.a_zero = ct;
|
||||||
|
mCommonToSystem.b_zero = st;
|
||||||
|
mLastTs = st;
|
||||||
|
}
|
||||||
|
return mCommonToSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaToSystemTransform::MediaToSystemTransform()
|
||||||
|
: mMediaToCommonValid(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaToSystemTransform::prepareCommonToSystem() {
|
||||||
|
memcpy(&mCommonToSystem, &mCommonToSystemTrans.getCommonToSystem(),
|
||||||
|
sizeof(mCommonToSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaToSystemTransform::setMediaToCommonTransform(
|
||||||
|
const LinearTransform& t) {
|
||||||
|
mMediaToCommon = t;
|
||||||
|
mMediaToCommonValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaToSystemTransform::mediaToSystem(int64_t* ts) {
|
||||||
|
if (!mMediaToCommonValid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// TODO: this is not efficient, we could combine two transform into one
|
||||||
|
// during prepareCommonToSystem() and setMediaToCommonTransform()
|
||||||
|
int64_t media_time = *ts;
|
||||||
|
int64_t common_time;
|
||||||
|
int64_t system_time;
|
||||||
|
if (!mMediaToCommon.doForwardTransform(media_time, &common_time)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mCommonToSystem.doForwardTransform(common_time, &system_time)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*ts = system_time;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -23,7 +23,13 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include <media/stagefright/foundation/ABase.h>
|
#include <media/stagefright/foundation/ABase.h>
|
||||||
|
#include <media/stagefright/foundation/ADebug.h>
|
||||||
#include <utils/Timers.h>
|
#include <utils/Timers.h>
|
||||||
|
#include <utils/threads.h>
|
||||||
|
#include <utils/LinearTransform.h>
|
||||||
|
#include <common_time/cc_helper.h>
|
||||||
|
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
#define IP_PRINTF_HELPER(a) ((a >> 24) & 0xFF), ((a >> 16) & 0xFF), \
|
#define IP_PRINTF_HELPER(a) ((a >> 24) & 0xFF), ((a >> 16) & 0xFF), \
|
||||||
((a >> 8) & 0xFF), (a & 0xFF)
|
((a >> 8) & 0xFF), (a & 0xFF)
|
||||||
@@ -97,6 +103,100 @@ inline void clearEventFD(int fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// thread safe circular array
|
||||||
|
template<typename T, uint32_t LEN>
|
||||||
|
class CircularArray {
|
||||||
|
public:
|
||||||
|
CircularArray()
|
||||||
|
: mReadIndex(0)
|
||||||
|
, mWriteIndex(0)
|
||||||
|
, mLength(0) {}
|
||||||
|
bool write(const T& t) {
|
||||||
|
Mutex::Autolock autolock(&mLock);
|
||||||
|
if (mLength < LEN) {
|
||||||
|
mData[mWriteIndex] = t;
|
||||||
|
mWriteIndex = (mWriteIndex + 1) % LEN;
|
||||||
|
mLength++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void writeAllowOverflow(const T& t) {
|
||||||
|
Mutex::Autolock autolock(&mLock);
|
||||||
|
mData[mWriteIndex] = t;
|
||||||
|
mWriteIndex = (mWriteIndex + 1) % LEN;
|
||||||
|
if (mLength < LEN) {
|
||||||
|
mLength++;
|
||||||
|
} else {
|
||||||
|
mReadIndex = (mReadIndex + 1) % LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool read(T* t) {
|
||||||
|
CHECK(t != NULL);
|
||||||
|
Mutex::Autolock autolock(&mLock);
|
||||||
|
if (mLength > 0) {
|
||||||
|
*t = mData[mReadIndex];
|
||||||
|
mReadIndex = (mReadIndex + 1) % LEN;
|
||||||
|
mLength--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t readBulk(T* t, uint32_t count) {
|
||||||
|
return readBulk(t, 0, count);
|
||||||
|
}
|
||||||
|
uint32_t readBulk(T* t, uint32_t mincount, uint32_t count) {
|
||||||
|
CHECK(t != NULL);
|
||||||
|
Mutex::Autolock autolock(&mLock);
|
||||||
|
if (mincount > count) {
|
||||||
|
// illegal argument
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (mincount > mLength) {
|
||||||
|
// not enough items
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < count && mLength; i++) {
|
||||||
|
*t = mData[mReadIndex];
|
||||||
|
mReadIndex = (mReadIndex + 1) % LEN;
|
||||||
|
mLength--;
|
||||||
|
t++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Mutex mLock;
|
||||||
|
T mData[LEN];
|
||||||
|
uint32_t mReadIndex;
|
||||||
|
uint32_t mWriteIndex;
|
||||||
|
uint32_t mLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommonToSystemTransform {
|
||||||
|
public:
|
||||||
|
CommonToSystemTransform();
|
||||||
|
const LinearTransform& getCommonToSystem();
|
||||||
|
private:
|
||||||
|
LinearTransform mCommonToSystem;
|
||||||
|
uint64_t mCommonFreq;
|
||||||
|
CCHelper mCCHelper;
|
||||||
|
int64_t mLastTs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaToSystemTransform {
|
||||||
|
public:
|
||||||
|
MediaToSystemTransform();
|
||||||
|
void setMediaToCommonTransform(const LinearTransform&);
|
||||||
|
void prepareCommonToSystem();
|
||||||
|
bool mediaToSystem(int64_t* ts);
|
||||||
|
private:
|
||||||
|
bool mMediaToCommonValid;
|
||||||
|
LinearTransform mMediaToCommon;
|
||||||
|
LinearTransform mCommonToSystem;
|
||||||
|
CommonToSystemTransform mCommonToSystemTrans;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
||||||
#endif // __UTILS_H__
|
#endif // __UTILS_H__
|
||||||
|
|||||||
Reference in New Issue
Block a user