# NOTE: mpv has to be rebuilt and version bumped after updating ffmpeg.
TERMUX_PKG_VERSION=3.4.1
TERMUX_PKG_SHA256=5a77278a63741efa74e26bf197b9bb09ac6381b9757391b922407210f0f991c0
+TERMUX_PKG_REVISION=1
TERMUX_PKG_SRCURL=https://www.ffmpeg.org/releases/ffmpeg-${TERMUX_PKG_VERSION}.tar.xz
# libbz2 is used by matroska decoder:
# libvpx is the VP8 & VP9 video encoder for WebM, see
# https://trac.ffmpeg.org/wiki/Encode/VP8 and https://trac.ffmpeg.org/wiki/Encode/VP9
-TERMUX_PKG_DEPENDS="libbz2, libx264, libx265, xvidcore, libvorbis, libmp3lame, libopus, libvpx, libgnutls, libandroid-glob"
+TERMUX_PKG_DEPENDS="libbz2, libsoxr, libx264, libx265, xvidcore, libvorbis, libmp3lame, libopus, libvpx, libgnutls, libandroid-glob"
TERMUX_PKG_INCLUDE_IN_DEVPACKAGE="share/ffmpeg/examples"
TERMUX_PKG_CONFLICTS="libav"
--enable-libxvid \
--enable-libvpx \
--enable-shared \
+ --enable-libsoxr \
--prefix=$TERMUX_PREFIX \
--target-os=android \
--extra-libs="-landroid-glob" \
TERMUX_PKG_HOMEPAGE=https://www.freedesktop.org/wiki/Software/PulseAudio
TERMUX_PKG_DESCRIPTION="A featureful, general-purpose sound server - shared libraries"
TERMUX_PKG_VERSION=11.1
-TERMUX_PKG_REVISION=3
+TERMUX_PKG_REVISION=4
TERMUX_PKG_SHA256=f2521c525a77166189e3cb9169f75c2ee2b82fa3fcf9476024fbc2c3a6c9cd9e
TERMUX_PKG_SRCURL=https://www.freedesktop.org/software/pulseaudio/releases/pulseaudio-${TERMUX_PKG_VERSION}.tar.xz
-TERMUX_PKG_DEPENDS="libltdl, libsndfile, libandroid-glob"
+TERMUX_PKG_DEPENDS="libltdl, libsndfile, libandroid-glob, libsoxr"
TERMUX_PKG_BUILD_DEPENDS="libtool"
TERMUX_PKG_INCLUDE_IN_DEVPACKAGE="share/vala"
TERMUX_PKG_EXTRA_CONFIGURE_ARGS="--disable-neon-opt
--disable-openssl
--without-caps
--with-database=simple
---disable-memfd"
+--disable-memfd
+--bindir=$TERMUX_PREFIX/libexec"
TERMUX_PKG_CONFFILES="etc/pulse/client.conf etc/pulse/daemon.conf etc/pulse/default.pa etc/pulse/system.pa"
termux_step_pre_configure () {
sed -i $TERMUX_PREFIX/etc/pulse/default.pa \
-e '/^load-module module-detect$/s/^/#/'
echo "load-module module-sles-sink" >> $TERMUX_PREFIX/etc/pulse/default.pa
- cd $TERMUX_PREFIX/bin
+ cd $TERMUX_PREFIX/libexec
for bin in esdcompat pacat pacmd pactl pasuspender pulseaudio; do
- mv $bin ../libexec
+ rm -f ../bin/$bin
local PA_LIBS="" lib
for lib in android-glob pulse pulsecommon-11.1 pulsecore-11.1; do
if [ -n "$PA_LIBS" ]; then PA_LIBS+=":"; fi
PA_LIBS+="$TERMUX_PREFIX/lib/lib${lib}.so"
done
+ echo "#!$TERMUX_PREFIX/bin/sh" >> $TERMUX_PREFIX/bin/$bin
echo "export LD_PRELOAD=$PA_LIBS" >> $TERMUX_PREFIX/bin/$bin
- echo "LD_LIBRARY_PATH=/system/$SYSTEM_LIB:/system/vendor/$SYSTEM_LIB:/data/data/com.termux/files/usr/lib /data/data/com.termux/files/usr/libexec/$bin \$@" >> $TERMUX_PREFIX/bin/$bin
- chmod +x $bin
+ echo "LD_LIBRARY_PATH=/system/$SYSTEM_LIB:/system/vendor/$SYSTEM_LIB:$TERMUX_PREFIX/lib exec $TERMUX_PREFIX/libexec/$bin \$@" >> $TERMUX_PREFIX/bin/$bin
+ chmod +x $TERMUX_PREFIX/bin/$bin
done
}
#include "module-sles-sink-symdef.h"
+//Only certain interfaces are supported by the fast mixer. These are:
+//SL_IID_ANDROIDSIMPLEBUFFERQUEUE
+//SL_IID_VOLUME
+//SL_IID_MUTESOLO
+#define USE_ANDROID_SIMPLE_BUFFER_QUEUE
+
#ifdef USE_ANDROID_SIMPLE_BUFFER_QUEUE
#include <SLES/OpenSLES_Android.h>
#define DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
else fprintf(stderr, "error %d at %s:%d\n", (int) r, __FILE__, __LINE__); \
} \
} while (0)
-typedef struct {
- short left;
- short right;
-} frame_t;
PA_MODULE_AUTHOR("Lennart Poettering, Nathan Martynov");
PA_MODULE_DESCRIPTION("Android OpenSL ES sink");
#define DEFAULT_SINK_NAME "OpenSL ES sink"
#define BLOCK_USEC (PA_USEC_PER_SEC * 2)
+typedef struct pa_memblock_queue_t {
+ pa_memblock *memblock;
+ struct pa_memblock_queue_t* next;
+} pa_memblock_queue;
+
struct userdata {
pa_core *core;
pa_module *module;
SLObjectItf bqPlayerObject;
SLPlayItf bqPlayerPlay;
BufferQueueItf bqPlayerBufferQueue;
+
+ pa_memblock_queue* current;
+ pa_memblock_queue* last;
};
static const char* const valid_modargs[] = {
pa_sink_set_max_request_within_thread(s, nbytes);
}
+static void pa_sles_callback(BufferQueueItf bq, void *context){
+ struct userdata* s = (struct userdata*) context;
+ pa_memblock_queue* next;
+ if (s->current != NULL){
+ if (s->current->memblock != NULL) pa_memblock_unref(s->current->memblock);
+ next = s->current->next;
+ free(s->current);
+ s->current = next;
+ }
+}
+
static int pa_init_sles_player(struct userdata *s, SLint32 sl_rate)
{
if (s == NULL) return -1;
locator_bufferqueue.locatorType = DATALOCATOR_BUFFERQUEUE;
locator_bufferqueue.numBuffers = 50;
- if (sl_rate < 0) {
+ if (sl_rate < SL_SAMPLINGRATE_8 || sl_rate > SL_SAMPLINGRATE_192) {
pa_log("Incompatible sample rate");
return -1;
}
result = (*s->bqPlayerObject)->GetInterface(s->bqPlayerObject, SL_IID_PLAY, &s->bqPlayerPlay); checkResult(result);
result = (*s->bqPlayerObject)->GetInterface(s->bqPlayerObject, IID_BUFFERQUEUE_USED, &s->bqPlayerBufferQueue); checkResult(result);
+ result = (*s->bqPlayerBufferQueue)->RegisterCallback(s->bqPlayerBufferQueue, pa_sles_callback, s); checkResult(result);
+
result = (*s->bqPlayerPlay)->SetPlayState(s->bqPlayerPlay, SL_PLAYSTATE_PLAYING); checkResult(result);
+
return 0;
}
}
static void process_render(struct userdata *u, pa_usec_t now) {
+ pa_memblock_queue* current_block;
size_t ate = 0;
pa_assert(u);
u->timestamp += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
ate += u->memchunk.length;
+
+ current_block = malloc(sizeof(pa_memblock_queue));
+ memset(current_block, 0, sizeof(pa_memblock_queue));
+
+ current_block->memblock = u->memchunk.memblock;
+ if (u->current == NULL) { u->current = current_block; }
+ if (u->last == NULL) { u->last = current_block; }
+ else {
+ u->last->next = current_block;
+ u->last = current_block;
+ }
+
+ //pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
if (ate >= u->sink->thread_info.max_request) break;
}
}
finish:
pa_log_debug("Thread shutting down");
}
-
-static SLint32 PA2SLrate(int32_t rate){
- if (!(rate >= 8000 && rate <= 192000)) return -1;
- switch(rate){
- case 8000:
- return SL_SAMPLINGRATE_8;
- case 11025:
- return SL_SAMPLINGRATE_11_025;
- case 12000:
- return SL_SAMPLINGRATE_12;
- case 16000:
- return SL_SAMPLINGRATE_16;
- case 22050:
- return SL_SAMPLINGRATE_22_05;
- case 24000:
- return SL_SAMPLINGRATE_24;
- case 32000:
- return SL_SAMPLINGRATE_32;
- case 44100:
- return SL_SAMPLINGRATE_44_1;
- case 48000:
- return SL_SAMPLINGRATE_48;
- case 64000:
- return SL_SAMPLINGRATE_64;
- case 88200:
- return SL_SAMPLINGRATE_88_2;
- case 96000:
- return SL_SAMPLINGRATE_96;
- case 192000:
- return SL_SAMPLINGRATE_192;
- default:
- return -1;
- }
+
+static int getenv_int(const char * env, size_t min_len){
+ char * got_env = getenv(env);
+ int ret = 0;
+ if (got_env != NULL && strlen(got_env) >= min_len) ret = atoi(got_env); //"8000" is 4 symbols
+ return ret;
}
int pa__init(pa_module*m) {
//Needed. Don't touch
ss.channels = 2;
ss.format = PA_SAMPLE_S16LE;
- int forceFormat = atoi(getenv("PROPERTY_OUTPUT_SAMPLE_RATE"));
- if (forceFormat >= 8000 && forceFormat <= 192000)
- ss.rate = forceFormat;
m->userdata = u = pa_xnew0(struct userdata, 1);
+
+ int forceFormat = getenv_int("PROPERTY_OUTPUT_SAMPLE_RATE", 4); //"8000" is 4 symbols
+ if (forceFormat >= 8000 && forceFormat <= 192000) {
+ ss.rate = forceFormat;
+ pa_log_info("Sample rate was forced to be %u\n", ss.rate);
+ }
+
u->core = m->core;
u->module = m;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
- if (pa_init_sles_player(u, PA2SLrate(ss.rate)) < 0)
+ //Pulseaudio uses samples per sec but OpenSL ES uses samples per ms
+ if (pa_init_sles_player(u, ss.rate * 1000) < 0)
goto fail;
- int buff[2] = {0, 0};
- (*u->bqPlayerBufferQueue)->Enqueue(u->bqPlayerBufferQueue, buff, 1);
+ //int buff[2] = {0, 0};
+ //(*u->bqPlayerBufferQueue)->Enqueue(u->bqPlayerBufferQueue, buff, 1);
pa_sink_new_data_init(&data);
data.driver = __FILE__;
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
pa_thread_free(u->thread);
}
+
+ if (u->engineObject){
+ pa_destroy_sles_player(u);
+ }
pa_thread_mq_done(&u->thread_mq);