Merged back audiotrack support from felix lang.
This commit is contained in:
parent
403a9fff62
commit
4748d0e4dd
51
channels/rdpsnd/client/audiotrack/CMakeLists.txt
Normal file
51
channels/rdpsnd/client/audiotrack/CMakeLists.txt
Normal file
@ -0,0 +1,51 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2013 Armin Novak <armin.novak@gmail.com>
|
||||
#
|
||||
# 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_channel_client_subsystem("rdpsnd" "audiotrack" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
audiotrack.cpp
|
||||
rdpsnd_audiotrack.c)
|
||||
|
||||
include_directories(.)
|
||||
include_directories(..)
|
||||
include_directories(${AUDIOTRACK_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
|
||||
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE freerdp
|
||||
MODULES freerdp-codec freerdp-utils)
|
||||
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE winpr
|
||||
MODULES winpr-utils)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${AUDIOTRACK_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
if(NOT STATIC_CHANNELS)
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/AudioTrack")
|
88
channels/rdpsnd/client/audiotrack/Errors.h
Normal file
88
channels/rdpsnd/client/audiotrack/Errors.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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_ERRORS_H
|
||||
#define ANDROID_ERRORS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// use this type to return error codes
|
||||
#ifdef HAVE_MS_C_RUNTIME
|
||||
typedef int status_t;
|
||||
#else
|
||||
typedef int32_t status_t;
|
||||
#endif
|
||||
|
||||
/* the MS C runtime lacks a few error codes */
|
||||
|
||||
/*
|
||||
* Error codes.
|
||||
* All error codes are negative values.
|
||||
*/
|
||||
|
||||
// Win32 #defines NO_ERROR as well. It has the same value, so there's no
|
||||
// real conflict, though it's a bit awkward.
|
||||
#ifdef _WIN32
|
||||
# undef NO_ERROR
|
||||
#endif
|
||||
|
||||
enum {
|
||||
OK = 0, // Everything's swell.
|
||||
NO_ERROR = 0, // No errors.
|
||||
|
||||
UNKNOWN_ERROR = 0x80000000,
|
||||
|
||||
NO_MEMORY = -ENOMEM,
|
||||
INVALID_OPERATION = -ENOSYS,
|
||||
BAD_VALUE = -EINVAL,
|
||||
BAD_TYPE = 0x80000001,
|
||||
NAME_NOT_FOUND = -ENOENT,
|
||||
PERMISSION_DENIED = -EPERM,
|
||||
NO_INIT = -ENODEV,
|
||||
ALREADY_EXISTS = -EEXIST,
|
||||
DEAD_OBJECT = -EPIPE,
|
||||
FAILED_TRANSACTION = 0x80000002,
|
||||
JPARKS_BROKE_IT = -EPIPE,
|
||||
#if !defined(HAVE_MS_C_RUNTIME)
|
||||
BAD_INDEX = -EOVERFLOW,
|
||||
NOT_ENOUGH_DATA = -ENODATA,
|
||||
WOULD_BLOCK = -EWOULDBLOCK,
|
||||
TIMED_OUT = -ETIME,
|
||||
UNKNOWN_TRANSACTION = -EBADMSG,
|
||||
#else
|
||||
BAD_INDEX = -E2BIG,
|
||||
NOT_ENOUGH_DATA = 0x80000003,
|
||||
WOULD_BLOCK = 0x80000004,
|
||||
TIMED_OUT = 0x80000005,
|
||||
UNKNOWN_TRANSACTION = 0x80000006,
|
||||
#endif
|
||||
};
|
||||
|
||||
// Restore define; enumeration is in "android" namespace, so the value defined
|
||||
// there won't work for Win32 code in a different namespace.
|
||||
#ifdef _WIN32
|
||||
# define NO_ERROR 0L
|
||||
#endif
|
||||
|
||||
}; // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_ERRORS_H
|
||||
|
327
channels/rdpsnd/client/audiotrack/audiotrack.cpp
Normal file
327
channels/rdpsnd/client/audiotrack/audiotrack.cpp
Normal file
@ -0,0 +1,327 @@
|
||||
#include <dlfcn.h>
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#define TAG "freerdp_android_audiotrack"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
|
||||
|
||||
#include "audiotrack.h"
|
||||
|
||||
#define SIZE_OF_AUDIOTRACK 256
|
||||
|
||||
// _ZN7android11AudioSystem19getOutputFrameCountEPii
|
||||
typedef int (*AudioSystem_getOutputFrameCount)(int *, int);
|
||||
// _ZN7android11AudioSystem16getOutputLatencyEPji
|
||||
typedef int (*AudioSystem_getOutputLatency)(unsigned int *, int);
|
||||
// _ZN7android11AudioSystem21getOutputSamplingRateEPii
|
||||
typedef int (*AudioSystem_getOutputSamplingRate)(int *, int);
|
||||
|
||||
// _ZN7android10AudioTrack16getMinFrameCountEPiij
|
||||
typedef int (*AudioTrack_getMinFrameCount)(int *, int, unsigned int);
|
||||
|
||||
// _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii
|
||||
typedef void (*AudioTrack_ctor)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int, int);
|
||||
// _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i
|
||||
typedef void (*AudioTrack_ctor_legacy)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int);
|
||||
// _ZN7android10AudioTrackD1Ev
|
||||
typedef void (*AudioTrack_dtor)(void *);
|
||||
// _ZNK7android10AudioTrack9initCheckEv
|
||||
typedef int (*AudioTrack_initCheck)(void *);
|
||||
typedef uint32_t (*AudioTrack_latency)(void *);
|
||||
// _ZN7android10AudioTrack5startEv
|
||||
typedef void (*AudioTrack_start)(void *);
|
||||
// _ZN7android10AudioTrack4stopEv
|
||||
typedef void (*AudioTrack_stop)(void *);
|
||||
// _ZN7android10AudioTrack5writeEPKvj
|
||||
typedef int (*AudioTrack_write)(void *, void const*, unsigned int);
|
||||
// _ZN7android10AudioTrack5flushEv
|
||||
typedef void (*AudioTrack_flush)(void *);
|
||||
|
||||
static void *libmedia;
|
||||
static AudioSystem_getOutputFrameCount as_getOutputFrameCount;
|
||||
static AudioSystem_getOutputLatency as_getOutputLatency;
|
||||
static AudioSystem_getOutputSamplingRate as_getOutputSamplingRate;
|
||||
|
||||
static AudioTrack_getMinFrameCount at_getMinFrameCount;
|
||||
static AudioTrack_ctor at_ctor;
|
||||
static AudioTrack_ctor_legacy at_ctor_legacy;
|
||||
static AudioTrack_dtor at_dtor;
|
||||
static AudioTrack_initCheck at_initCheck;
|
||||
static AudioTrack_latency at_latency;
|
||||
static AudioTrack_start at_start;
|
||||
static AudioTrack_stop at_stop;
|
||||
static AudioTrack_write at_write;
|
||||
static AudioTrack_flush at_flush;
|
||||
|
||||
class AndroidAudioTrack
|
||||
{
|
||||
private:
|
||||
void* mAudioTrack;
|
||||
|
||||
public:
|
||||
AndroidAudioTrack()
|
||||
: mAudioTrack(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~AndroidAudioTrack()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (mAudioTrack) {
|
||||
if (at_stop)
|
||||
at_stop(mAudioTrack);
|
||||
if (at_flush)
|
||||
at_flush(mAudioTrack);
|
||||
if (at_dtor)
|
||||
at_dtor(mAudioTrack);
|
||||
free(mAudioTrack);
|
||||
mAudioTrack = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int set(int streamType, uint32_t sampleRate, int format, int channels)
|
||||
{
|
||||
int status;
|
||||
int minFrameCount = 0;
|
||||
int size = 0;
|
||||
|
||||
LOGI("streamTyp = %d, sampleRate = %d, format = %d, channels = %d\n", streamType, sampleRate, format, channels);
|
||||
close();
|
||||
|
||||
if (at_getMinFrameCount) {
|
||||
status = at_getMinFrameCount(&minFrameCount, streamType, sampleRate);
|
||||
LOGI("at_getMinFrameCount %d, %d\n", minFrameCount, status);
|
||||
}
|
||||
//size = minFrameCount * (channels == CHANNEL_OUT_STEREO ? 2 : 1) * 4;
|
||||
|
||||
mAudioTrack = malloc(SIZE_OF_AUDIOTRACK);
|
||||
*((uint32_t *) ((uint32_t)mAudioTrack + SIZE_OF_AUDIOTRACK - 4)) = 0xbaadbaad;
|
||||
if (at_ctor) {
|
||||
at_ctor(mAudioTrack, streamType, sampleRate, format, channels, size, 0, NULL, NULL, 0, 0);
|
||||
} else if (at_ctor_legacy) {
|
||||
at_ctor_legacy(mAudioTrack, streamType, sampleRate, format, channels, size, 0, NULL, NULL, 0);
|
||||
} else {
|
||||
LOGI("Cannot create AudioTrack!");
|
||||
free(mAudioTrack);
|
||||
mAudioTrack = NULL;
|
||||
return -1;
|
||||
}
|
||||
assert( (*((uint32_t *) ((uint32_t)mAudioTrack + SIZE_OF_AUDIOTRACK - 4)) == 0xbaadbaad) );
|
||||
|
||||
/* And Init */
|
||||
status = at_initCheck(mAudioTrack);
|
||||
LOGI("at_initCheck = %d\n", status);
|
||||
|
||||
/* android 1.6 uses channel count instead of stream_type */
|
||||
if (status != 0 && at_ctor_legacy) {
|
||||
channels = (channels == CHANNEL_OUT_STEREO) ? 2 : 1;
|
||||
at_ctor_legacy(mAudioTrack, streamType, sampleRate, format, channels, size, 0, NULL, NULL, 0);
|
||||
status = at_initCheck(mAudioTrack);
|
||||
LOGI("at_initCheck2 = %d\n", status);
|
||||
}
|
||||
if (status != 0) {
|
||||
LOGI("Cannot create AudioTrack!");
|
||||
free(mAudioTrack);
|
||||
mAudioTrack = NULL;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
uint32_t latency()
|
||||
{
|
||||
if (mAudioTrack && at_latency) {
|
||||
return at_latency(mAudioTrack);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int start()
|
||||
{
|
||||
if (mAudioTrack && at_start) {
|
||||
at_start(mAudioTrack);
|
||||
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
|
||||
}
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
}
|
||||
int write(void* buffer, int size)
|
||||
{
|
||||
if (mAudioTrack && at_write) {
|
||||
return at_write(mAudioTrack, buffer, size);
|
||||
}
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
}
|
||||
int flush()
|
||||
{
|
||||
if (mAudioTrack && at_flush) {
|
||||
at_flush(mAudioTrack);
|
||||
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
|
||||
}
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
}
|
||||
int stop()
|
||||
{
|
||||
if (mAudioTrack && at_stop) {
|
||||
at_stop(mAudioTrack);
|
||||
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
|
||||
}
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
}
|
||||
int reload()
|
||||
{
|
||||
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void* InitLibrary()
|
||||
{
|
||||
/* DL Open libmedia */
|
||||
void *p_library;
|
||||
p_library = dlopen("libmedia.so", RTLD_NOW);
|
||||
if (!p_library)
|
||||
return NULL;
|
||||
|
||||
/* Register symbols */
|
||||
as_getOutputFrameCount = (AudioSystem_getOutputFrameCount)(dlsym(p_library, "_ZN7android11AudioSystem19getOutputFrameCountEPii"));
|
||||
as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
|
||||
as_getOutputSamplingRate = (AudioSystem_getOutputSamplingRate)(dlsym(p_library, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"));
|
||||
at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPiij"));
|
||||
at_ctor = (AudioTrack_ctor)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii"));
|
||||
at_ctor_legacy = (AudioTrack_ctor_legacy)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i"));
|
||||
at_dtor = (AudioTrack_dtor)(dlsym(p_library, "_ZN7android10AudioTrackD1Ev"));
|
||||
at_initCheck = (AudioTrack_initCheck)(dlsym(p_library, "_ZNK7android10AudioTrack9initCheckEv"));
|
||||
at_latency = (AudioTrack_latency)(dlsym(p_library, "_ZNK7android10AudioTrack7latencyEv"));
|
||||
at_start = (AudioTrack_start)(dlsym(p_library, "_ZN7android10AudioTrack5startEv"));
|
||||
at_stop = (AudioTrack_stop)(dlsym(p_library, "_ZN7android10AudioTrack4stopEv"));
|
||||
at_write = (AudioTrack_write)(dlsym(p_library, "_ZN7android10AudioTrack5writeEPKvj"));
|
||||
at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
|
||||
|
||||
LOGI("p_library : %p\n", p_library);
|
||||
LOGI("as_getOutputFrameCount : %p\n", as_getOutputFrameCount);
|
||||
LOGI("as_getOutputLatency : %p\n", as_getOutputLatency);
|
||||
LOGI("as_getOutputSamplingRate : %p\n", as_getOutputSamplingRate);
|
||||
LOGI("at_getMinFrameCount : %p\n", at_getMinFrameCount);
|
||||
LOGI("at_ctor : %p\n", at_ctor);
|
||||
LOGI("at_ctor_legacy : %p\n", at_ctor_legacy);
|
||||
LOGI("at_dtor : %p\n", at_dtor);
|
||||
LOGI("at_initCheck : %p\n", at_initCheck);
|
||||
LOGI("at_latency : %p\n", at_latency);
|
||||
LOGI("at_start : %p\n", at_start);
|
||||
LOGI("at_stop : %p\n", at_stop);
|
||||
LOGI("at_write : %p\n", at_write);
|
||||
LOGI("at_flush : %p\n", at_flush);
|
||||
|
||||
/* We need the first 3 or the last 1 */
|
||||
#if 0
|
||||
if (!((as_getOutputFrameCount && as_getOutputLatency && as_getOutputSamplingRate)
|
||||
|| at_getMinFrameCount)) {
|
||||
dlclose(p_library);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We need all the other Symbols
|
||||
if (!((at_ctor || at_ctor_legacy) && at_dtor && at_initCheck &&
|
||||
at_start && at_stop && at_write && at_flush)) {
|
||||
dlclose(p_library);
|
||||
return NULL;
|
||||
}
|
||||
return p_library;
|
||||
}
|
||||
|
||||
int freerdp_android_at_open(AUDIO_DRIVER_HANDLE* outHandle)
|
||||
{
|
||||
int ret = ANDROID_AUDIOTRACK_RESULT_SUCCESS;
|
||||
AndroidAudioTrack* audioTrack = new AndroidAudioTrack();
|
||||
|
||||
*outHandle = audioTrack;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int freerdp_android_at_close(AUDIO_DRIVER_HANDLE handle)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
delete audioTrack;
|
||||
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
int freerdp_android_at_set(AUDIO_DRIVER_HANDLE handle, int streamType, uint32_t sampleRate, int format, int channels)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return audioTrack->set(streamType, sampleRate, format, channels);
|
||||
}
|
||||
|
||||
int freerdp_android_at_set_volume(AUDIO_DRIVER_HANDLE handle, float left, float right)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t freerdp_android_at_latency(AUDIO_DRIVER_HANDLE handle)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return audioTrack->latency();
|
||||
}
|
||||
|
||||
int freerdp_android_at_start(AUDIO_DRIVER_HANDLE handle)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return audioTrack->start();
|
||||
}
|
||||
|
||||
int freerdp_android_at_write(AUDIO_DRIVER_HANDLE handle, void *buffer, int buffer_size)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return audioTrack->write(buffer, buffer_size);
|
||||
}
|
||||
|
||||
int freerdp_android_at_flush(AUDIO_DRIVER_HANDLE handle)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return audioTrack->flush();
|
||||
}
|
||||
|
||||
int freerdp_android_at_stop(AUDIO_DRIVER_HANDLE handle)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return audioTrack->stop();
|
||||
}
|
||||
|
||||
int freerdp_android_at_reload(AUDIO_DRIVER_HANDLE handle)
|
||||
{
|
||||
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
|
||||
if (!audioTrack)
|
||||
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
|
||||
return audioTrack->reload();
|
||||
}
|
||||
|
||||
int freerdp_android_at_init_library()
|
||||
{
|
||||
if (libmedia == NULL)
|
||||
{
|
||||
libmedia = InitLibrary();
|
||||
LOGI("loaded");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
109
channels/rdpsnd/client/audiotrack/audiotrack.h
Normal file
109
channels/rdpsnd/client/audiotrack/audiotrack.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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_AUDIOTRACK_WRAPPER_H
|
||||
#define ANDROID_AUDIOTRACK_WRAPPER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <jni.h>
|
||||
|
||||
#define ANDROID_AUDIOTRACK_RESULT_SUCCESS 0
|
||||
#define ANDROID_AUDIOTRACK_RESULT_BAD_PARAMETER -1
|
||||
#define ANDROID_AUDIOTRACK_RESULT_JNI_EXCEPTION -2
|
||||
#define ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED -3
|
||||
#define ANDROID_AUDIOTRACK_RESULT_ERRNO -4
|
||||
|
||||
enum stream_type {
|
||||
DEFAULT =-1,
|
||||
VOICE_CALL = 0,
|
||||
SYSTEM = 1,
|
||||
RING = 2,
|
||||
MUSIC = 3,
|
||||
ALARM = 4,
|
||||
NOTIFICATION = 5,
|
||||
BLUETOOTH_SCO = 6,
|
||||
ENFORCED_AUDIBLE = 7, // Sounds that cannot be muted by user and must be routed to speaker
|
||||
DTMF = 8,
|
||||
TTS = 9,
|
||||
NUM_STREAM_TYPES
|
||||
};
|
||||
|
||||
enum {
|
||||
NO_MORE_BUFFERS = 0x80000001,
|
||||
STOPPED = 1
|
||||
};
|
||||
|
||||
// Audio sub formats (see AudioSystem::audio_format).
|
||||
enum pcm_sub_format {
|
||||
PCM_SUB_16_BIT = 0x1, // must be 1 for backward compatibility
|
||||
PCM_SUB_8_BIT = 0x2, // must be 2 for backward compatibility
|
||||
};
|
||||
|
||||
// Audio format consists in a main format field (upper 8 bits) and a sub format field (lower 24 bits).
|
||||
// The main format indicates the main codec type. The sub format field indicates options and parameters
|
||||
// for each format. The sub format is mainly used for record to indicate for instance the requested bitrate
|
||||
// or profile. It can also be used for certain formats to give informations not present in the encoded
|
||||
// audio stream (e.g. octet alignement for AMR).
|
||||
enum audio_format {
|
||||
INVALID_FORMAT = -1,
|
||||
FORMAT_DEFAULT = 0,
|
||||
PCM = 0x00000000, // must be 0 for backward compatibility
|
||||
MP3 = 0x01000000,
|
||||
AMR_NB = 0x02000000,
|
||||
AMR_WB = 0x03000000,
|
||||
AAC = 0x04000000,
|
||||
HE_AAC_V1 = 0x05000000,
|
||||
HE_AAC_V2 = 0x06000000,
|
||||
VORBIS = 0x07000000,
|
||||
MAIN_FORMAT_MASK = 0xFF000000,
|
||||
SUB_FORMAT_MASK = 0x00FFFFFF,
|
||||
// Aliases
|
||||
PCM_16_BIT = (PCM|PCM_SUB_16_BIT),
|
||||
PCM_8_BIT = (PCM|PCM_SUB_8_BIT)
|
||||
};
|
||||
|
||||
// Channel mask definitions must be kept in sync with JAVA values in /media/java/android/media/AudioFormat.java
|
||||
enum audio_channels {
|
||||
// output channels
|
||||
CHANNEL_OUT_FRONT_LEFT = 0x4,
|
||||
CHANNEL_OUT_FRONT_RIGHT = 0x8,
|
||||
CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
|
||||
CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT)
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void* AUDIO_DRIVER_HANDLE;
|
||||
|
||||
int freerdp_android_at_init_library();
|
||||
int freerdp_android_at_open(AUDIO_DRIVER_HANDLE* outHandle);
|
||||
int freerdp_android_at_set(AUDIO_DRIVER_HANDLE handle, int streamType, uint32_t sampleRate, int format, int channels);
|
||||
int freerdp_android_at_set_volume(AUDIO_DRIVER_HANDLE handle, float left, float right);
|
||||
int freerdp_android_at_close(AUDIO_DRIVER_HANDLE handle);
|
||||
uint32_t freerdp_android_at_latency(AUDIO_DRIVER_HANDLE handle);
|
||||
int freerdp_android_at_start(AUDIO_DRIVER_HANDLE handle);
|
||||
int freerdp_android_at_write(AUDIO_DRIVER_HANDLE handle, void *buffer, int buffer_size);
|
||||
int freerdp_android_at_flush(AUDIO_DRIVER_HANDLE handle);
|
||||
int freerdp_android_at_stop(AUDIO_DRIVER_HANDLE handle);
|
||||
int freerdp_android_at_reload(AUDIO_DRIVER_HANDLE handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
308
channels/rdpsnd/client/audiotrack/rdpsnd_audiotrack.c
Normal file
308
channels/rdpsnd/client/audiotrack/rdpsnd_audiotrack.c
Normal file
@ -0,0 +1,308 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol client.
|
||||
* Audio Output Virtual Channel
|
||||
*
|
||||
* Copyright 2009-2011 Jay Sorg
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2012 Felix Long
|
||||
* Copyright 2013 Armin Novak
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <audiotrack.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
|
||||
#include "rdpsnd_main.h"
|
||||
|
||||
typedef struct rdpsnd_audiotrack_plugin rdpsndAudioTrackPlugin;
|
||||
struct rdpsnd_audiotrack_plugin
|
||||
{
|
||||
rdpsndDevicePlugin device;
|
||||
|
||||
AUDIO_DRIVER_HANDLE out_handle;
|
||||
|
||||
UINT32 source_rate;
|
||||
UINT32 actual_rate;
|
||||
int format;
|
||||
UINT32 source_channels;
|
||||
UINT32 actual_channels;
|
||||
int bytes_per_channel;
|
||||
int wformat;
|
||||
int block_size;
|
||||
int latency;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
};
|
||||
|
||||
static void rdpsnd_audiotrack_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
|
||||
|
||||
if (format != NULL)
|
||||
{
|
||||
audiotrack->source_rate = format->nSamplesPerSec;
|
||||
audiotrack->actual_rate = format->nSamplesPerSec;
|
||||
audiotrack->source_channels = format->nChannels;
|
||||
audiotrack->actual_channels = format->nChannels;
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case 1: /* PCM */
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 8:
|
||||
audiotrack->format = PCM_8_BIT;
|
||||
audiotrack->bytes_per_channel = 1;
|
||||
break;
|
||||
case 16:
|
||||
audiotrack->format = PCM_16_BIT;
|
||||
audiotrack->bytes_per_channel = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: /* MS ADPCM */
|
||||
case 0x11: /* IMA ADPCM */
|
||||
audiotrack->format = PCM_16_BIT;
|
||||
audiotrack->bytes_per_channel = 2;
|
||||
break;
|
||||
}
|
||||
audiotrack->wformat = format->wFormatTag;
|
||||
audiotrack->block_size = format->nBlockAlign;
|
||||
}
|
||||
audiotrack->latency = latency;
|
||||
|
||||
freerdp_android_at_set(audiotrack->out_handle,
|
||||
MUSIC,
|
||||
audiotrack->actual_rate,
|
||||
audiotrack->format,
|
||||
audiotrack->actual_channels == 2 ? CHANNEL_OUT_STEREO : CHANNEL_OUT_MONO);
|
||||
}
|
||||
|
||||
static void rdpsnd_audiotrack_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
|
||||
int error;
|
||||
|
||||
if (audiotrack->out_handle != 0)
|
||||
return;
|
||||
|
||||
DEBUG_SVC("opening");
|
||||
|
||||
error = freerdp_android_at_open(&audiotrack->out_handle);
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
DEBUG_WARN("freerdp_android_at_open failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
freerdp_dsp_context_reset_adpcm(audiotrack->dsp_context);
|
||||
rdpsnd_audiotrack_set_format(device, format, latency);
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_audiotrack_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
|
||||
|
||||
if (audiotrack->out_handle != 0)
|
||||
{
|
||||
DEBUG_SVC("close");
|
||||
freerdp_android_at_close(audiotrack->out_handle);
|
||||
audiotrack->out_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_audiotrack_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
|
||||
|
||||
rdpsnd_audiotrack_close(device);
|
||||
freerdp_dsp_context_free(audiotrack->dsp_context);
|
||||
free(audiotrack);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_audiotrack_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case 1: /* PCM */
|
||||
if (format->cbSize == 0 &&
|
||||
format->nSamplesPerSec <= 48000 &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case 2: /* MS ADPCM */
|
||||
case 0x11: /* IMA ADPCM */
|
||||
if (format->nSamplesPerSec <= 48000 &&
|
||||
format->wBitsPerSample == 4 &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void rdpsnd_audiotrack_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
|
||||
float left;
|
||||
float right;
|
||||
|
||||
if (audiotrack->out_handle == 0)
|
||||
return;
|
||||
|
||||
left = ((value & 0xFFFF) * 1.0) / 0xFFFF;
|
||||
right = (((value >> 16) & 0xFFFF) * 1.0) / 0xFFFF;
|
||||
freerdp_android_at_set_volume(audiotrack->out_handle, left, right);
|
||||
}
|
||||
|
||||
static void rdpsnd_audiotrack_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
|
||||
BYTE* src;
|
||||
int len;
|
||||
int error;
|
||||
int frames;
|
||||
int rbytes_per_frame;
|
||||
int sbytes_per_frame;
|
||||
BYTE* pindex;
|
||||
BYTE* end;
|
||||
|
||||
if (audiotrack->out_handle == 0)
|
||||
return;
|
||||
|
||||
if (audiotrack->wformat == 2)
|
||||
{
|
||||
audiotrack->dsp_context->decode_ms_adpcm(audiotrack->dsp_context,
|
||||
data, size, audiotrack->source_channels, audiotrack->block_size);
|
||||
size = audiotrack->dsp_context->adpcm_size;
|
||||
src = audiotrack->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else if (audiotrack->wformat == 0x11)
|
||||
{
|
||||
audiotrack->dsp_context->decode_ima_adpcm(audiotrack->dsp_context,
|
||||
data, size, audiotrack->source_channels, audiotrack->block_size);
|
||||
size = audiotrack->dsp_context->adpcm_size;
|
||||
src = audiotrack->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = data;
|
||||
}
|
||||
|
||||
sbytes_per_frame = audiotrack->source_channels * audiotrack->bytes_per_channel;
|
||||
rbytes_per_frame = audiotrack->actual_channels * audiotrack->bytes_per_channel;
|
||||
if ((size % sbytes_per_frame) != 0)
|
||||
{
|
||||
DEBUG_WARN("error len mod");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((audiotrack->source_rate == audiotrack->actual_rate) &&
|
||||
(audiotrack->source_channels == audiotrack->actual_channels))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
audiotrack->dsp_context->resample(audiotrack->dsp_context, src, audiotrack->bytes_per_channel,
|
||||
audiotrack->source_channels, audiotrack->source_rate, size / sbytes_per_frame,
|
||||
audiotrack->actual_channels, audiotrack->actual_rate);
|
||||
frames = audiotrack->dsp_context->resampled_frames;
|
||||
DEBUG_SVC("resampled %d frames at %d to %d frames at %d",
|
||||
size / sbytes_per_frame, audiotrack->source_rate, frames, audiotrack->actual_rate);
|
||||
size = frames * rbytes_per_frame;
|
||||
src = audiotrack->dsp_context->resampled_buffer;
|
||||
}
|
||||
|
||||
pindex = src;
|
||||
end = pindex + size;
|
||||
while (pindex < end)
|
||||
{
|
||||
len = end - pindex;
|
||||
|
||||
error = freerdp_android_at_write(audiotrack->out_handle, pindex, len);
|
||||
if (error < 0)
|
||||
{
|
||||
DEBUG_WARN("error %d", error);
|
||||
freerdp_android_at_close(audiotrack->out_handle);
|
||||
audiotrack->out_handle = 0;
|
||||
rdpsnd_audiotrack_open(device, NULL, audiotrack->latency);
|
||||
break;
|
||||
}
|
||||
pindex += error;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_audiotrack_start(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
|
||||
|
||||
if (audiotrack->out_handle == 0)
|
||||
return;
|
||||
|
||||
freerdp_android_at_start(audiotrack->out_handle);
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#define freerdp_rdpsnd_client_subsystem_entry audiotrack_freerdp_rdpsnd_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
rdpsndAudioTrackPlugin* audiotrack;
|
||||
|
||||
freerdp_android_at_init_library();
|
||||
audiotrack = malloc(sizeof(rdpsndAudioTrackPlugin));
|
||||
|
||||
audiotrack->device.Open = rdpsnd_audiotrack_open;
|
||||
audiotrack->device.FormatSupported = rdpsnd_audiotrack_format_supported;
|
||||
audiotrack->device.SetFormat = rdpsnd_audiotrack_set_format;
|
||||
audiotrack->device.SetVolume = rdpsnd_audiotrack_set_volume;
|
||||
audiotrack->device.Play = rdpsnd_audiotrack_play;
|
||||
audiotrack->device.Start = rdpsnd_audiotrack_start;
|
||||
audiotrack->device.Close = rdpsnd_audiotrack_close;
|
||||
audiotrack->device.Free = rdpsnd_audiotrack_free;
|
||||
|
||||
audiotrack->out_handle = 0;
|
||||
audiotrack->source_rate = 22050;
|
||||
audiotrack->actual_rate = 22050;
|
||||
audiotrack->format = PCM_16_BIT;
|
||||
audiotrack->source_channels = 2;
|
||||
audiotrack->actual_channels = 2;
|
||||
audiotrack->bytes_per_channel = 2;
|
||||
|
||||
audiotrack->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)audiotrack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user