mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 710051 - Port the android libsydneyaudio backend to gonk, r=kinetik a=gonk-only
This commit is contained in:
parent
40de82fbfe
commit
fe94f50942
@ -52,8 +52,8 @@ CSRCS = \
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
|
||||
CSRCS = \
|
||||
sydney_audio_gonk.c \
|
||||
CPPSRCS = \
|
||||
sydney_audio_gonk.cpp \
|
||||
$(NULL)
|
||||
else ifeq ($(MOZ_WIDGET_TOOLKIT),android)
|
||||
CSRCS = \
|
||||
|
@ -36,9 +36,12 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
extern "C" {
|
||||
#include "sydney_audio.h"
|
||||
}
|
||||
|
||||
#include "android/log.h"
|
||||
#include "media/AudioTrack.h"
|
||||
|
||||
#ifndef ALOG
|
||||
#if defined(DEBUG) || defined(FORCE_ALOG)
|
||||
@ -46,15 +49,19 @@
|
||||
#else
|
||||
#define ALOG(args...)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Gonk implementation based on sydney_audio_mac.c */
|
||||
/* XXX This is temporary until we figure out a way to hook ALSA up */
|
||||
/* Gonk implementation based on sydney_audio_android.c */
|
||||
|
||||
#define NANOSECONDS_PER_SECOND 1000000000
|
||||
#define NANOSECONDS_IN_MILLISECOND 1000000
|
||||
#define MILLISECONDS_PER_SECOND 1000
|
||||
|
||||
using namespace android;
|
||||
|
||||
struct sa_stream {
|
||||
AudioTrack *output_unit;
|
||||
|
||||
unsigned int rate;
|
||||
unsigned int channels;
|
||||
unsigned int isPaused;
|
||||
@ -103,10 +110,11 @@ sa_stream_create_pcm(
|
||||
* Allocate the instance and required resources.
|
||||
*/
|
||||
sa_stream_t *s;
|
||||
if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
|
||||
if ((s = (sa_stream_t *)malloc(sizeof(sa_stream_t))) == NULL) {
|
||||
return SA_ERROR_OOM;
|
||||
}
|
||||
|
||||
s->output_unit = NULL;
|
||||
s->rate = rate;
|
||||
s->channels = channels;
|
||||
s->isPaused = 0;
|
||||
@ -115,7 +123,7 @@ sa_stream_create_pcm(
|
||||
s->timePlaying = 0;
|
||||
s->amountWritten = 0;
|
||||
|
||||
s->bufferSize = rate * channels;
|
||||
s->bufferSize = 0;
|
||||
|
||||
*_s = s;
|
||||
return SA_SUCCESS;
|
||||
@ -128,8 +136,47 @@ sa_stream_open(sa_stream_t *s) {
|
||||
if (s == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
if (s->output_unit != NULL) {
|
||||
return SA_ERROR_INVALID;
|
||||
}
|
||||
|
||||
return SA_ERROR_NO_DEVICE;
|
||||
int32_t chanConfig = s->channels == 1 ?
|
||||
AudioSystem::CHANNEL_OUT_MONO : AudioSystem::CHANNEL_OUT_STEREO;
|
||||
|
||||
int frameCount;
|
||||
if (AudioTrack::getMinFrameCount(&frameCount, AudioSystem::DEFAULT,
|
||||
s->rate) != NO_ERROR) {
|
||||
return SA_ERROR_INVALID;
|
||||
}
|
||||
int minsz = frameCount * s->channels * sizeof(int16_t);
|
||||
|
||||
s->bufferSize = s->rate * s->channels * sizeof(int16_t);
|
||||
if (s->bufferSize < minsz) {
|
||||
s->bufferSize = minsz;
|
||||
}
|
||||
|
||||
AudioTrack *track =
|
||||
new AudioTrack(AudioSystem::SYSTEM,
|
||||
s->rate,
|
||||
AudioSystem::PCM_16_BIT,
|
||||
chanConfig,
|
||||
frameCount,
|
||||
0,
|
||||
NULL, NULL,
|
||||
0,
|
||||
0);
|
||||
|
||||
if (track->initCheck() != NO_ERROR) {
|
||||
delete track;
|
||||
return SA_ERROR_INVALID;
|
||||
}
|
||||
|
||||
s->output_unit = track;
|
||||
|
||||
ALOG("%p - New stream %u %u bsz=%u min=%u", s, s->rate, s->channels,
|
||||
s->bufferSize, minsz);
|
||||
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -139,7 +186,20 @@ sa_stream_destroy(sa_stream_t *s) {
|
||||
if (s == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
// XXX
|
||||
|
||||
static bool firstLeaked = 0;
|
||||
if (s->output_unit) {
|
||||
s->output_unit->stop();
|
||||
s->output_unit->flush();
|
||||
// XXX: Figure out why we crash if we don't leak the first AudioTrack
|
||||
if (firstLeaked)
|
||||
delete s->output_unit;
|
||||
else
|
||||
firstLeaked = true;
|
||||
}
|
||||
free(s);
|
||||
|
||||
ALOG("%p - Stream destroyed", s);
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
||||
@ -153,14 +213,42 @@ sa_stream_destroy(sa_stream_t *s) {
|
||||
int
|
||||
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
|
||||
|
||||
if (s == NULL) {
|
||||
if (s == NULL || s->output_unit == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
if (nbytes == 0) {
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
// XXX
|
||||
return SA_SUCCESS;
|
||||
|
||||
const char *p = (char *)data;
|
||||
ssize_t r = 0;
|
||||
size_t wrote = 0;
|
||||
do {
|
||||
size_t towrite = nbytes - wrote;
|
||||
|
||||
r = s->output_unit->write(p, towrite);
|
||||
if (r < 0) {
|
||||
ALOG("%p - Write failed %d", s, r);
|
||||
break;
|
||||
}
|
||||
|
||||
/* AudioTrack::write is blocking when the AudioTrack is playing. When
|
||||
it's not playing, it's a non-blocking call that will return a short
|
||||
write when the buffer is full. Use a short write to indicate a good
|
||||
time to start the AudioTrack playing. */
|
||||
if (r != towrite) {
|
||||
ALOG("%p - Buffer full, starting playback", s);
|
||||
sa_stream_resume(s);
|
||||
}
|
||||
|
||||
p += r;
|
||||
wrote += r;
|
||||
} while (wrote < nbytes);
|
||||
|
||||
ALOG("%p - Wrote %u", s, nbytes);
|
||||
s->amountWritten += nbytes;
|
||||
|
||||
return r < 0 ? SA_ERROR_INVALID : SA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -173,11 +261,21 @@ sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
|
||||
int
|
||||
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
|
||||
|
||||
if (s == NULL) {
|
||||
if (s == NULL || s->output_unit == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
// XXX
|
||||
/* No android API for this, so estimate based on how much we have played and
|
||||
* how much we have written. */
|
||||
*size = s->bufferSize - ((s->timePlaying * s->channels * s->rate * sizeof(int16_t) /
|
||||
MILLISECONDS_PER_SECOND) - s->amountWritten);
|
||||
|
||||
/* Available buffer space can't exceed bufferSize. */
|
||||
if (*size > s->bufferSize) {
|
||||
*size = s->bufferSize;
|
||||
}
|
||||
ALOG("%p - Write Size tp=%lld aw=%u sz=%zu", s, s->timePlaying, s->amountWritten, *size);
|
||||
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
||||
@ -185,11 +283,20 @@ sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
|
||||
int
|
||||
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
|
||||
|
||||
if (s == NULL) {
|
||||
if (s == NULL || s->output_unit == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
// XXX
|
||||
ALOG("%p - get position", s);
|
||||
|
||||
uint32_t framePosition;
|
||||
if (s->output_unit->getPosition(&framePosition) != NO_ERROR)
|
||||
return SA_ERROR_INVALID;
|
||||
|
||||
/* android returns number of frames, so:
|
||||
position = frames * (PCM_16_BIT == 2 bytes) * channels
|
||||
*/
|
||||
*pos = framePosition * s->channels * sizeof(int16_t);
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
||||
@ -197,11 +304,23 @@ sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
|
||||
int
|
||||
sa_stream_pause(sa_stream_t *s) {
|
||||
|
||||
if (s == NULL) {
|
||||
if (s == NULL || s->output_unit == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
// XXX
|
||||
s->isPaused = 1;
|
||||
|
||||
/* Update stats */
|
||||
if (s->lastStartTime != 0) {
|
||||
/* if lastStartTime is not zero, so playback has started */
|
||||
struct timespec current_time;
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
||||
int64_t ticker = current_time.tv_sec * 1000 + current_time.tv_nsec / 1000000;
|
||||
s->timePlaying += ticker - s->lastStartTime;
|
||||
}
|
||||
ALOG("%p - Pause total time playing: %lld total written: %lld", s, s->timePlaying, s->amountWritten);
|
||||
|
||||
s->output_unit->pause();
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
||||
@ -209,11 +328,21 @@ sa_stream_pause(sa_stream_t *s) {
|
||||
int
|
||||
sa_stream_resume(sa_stream_t *s) {
|
||||
|
||||
if (s == NULL) {
|
||||
if (s == NULL || s->output_unit == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
// XXX
|
||||
ALOG("%p - resume", s);
|
||||
|
||||
s->isPaused = 0;
|
||||
|
||||
/* Update stats */
|
||||
struct timespec current_time;
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
||||
int64_t ticker = current_time.tv_sec * 1000 + current_time.tv_nsec / 1000000;
|
||||
s->lastStartTime = ticker;
|
||||
|
||||
s->output_unit->start();
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
||||
@ -221,11 +350,36 @@ sa_stream_resume(sa_stream_t *s) {
|
||||
int
|
||||
sa_stream_drain(sa_stream_t *s)
|
||||
{
|
||||
if (s == NULL) {
|
||||
if (s == NULL || s->output_unit == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
// XXX
|
||||
/* This is somewhat of a hack (see bug 693131). The AudioTrack documentation
|
||||
doesn't make it clear how much data must be written before a chunk of data is
|
||||
played, and experimentation with short streams required filling the entire
|
||||
allocated buffer. To guarantee that short streams (and the end of longer
|
||||
streams) are audible, write an entire bufferSize of silence before sleeping.
|
||||
This guarantees the short write logic in sa_stream_write is hit and the
|
||||
stream is playing before sleeping. Note that the sleep duration is
|
||||
calculated from the duration of audio written before writing silence. */
|
||||
size_t available;
|
||||
sa_stream_get_write_size(s, &available);
|
||||
|
||||
void *p = calloc(1, s->bufferSize);
|
||||
sa_stream_write(s, p, s->bufferSize);
|
||||
free(p);
|
||||
|
||||
/* There is no way with the Android SDK to determine exactly how
|
||||
long to playback. So estimate and sleep for that long. */
|
||||
unsigned long long x = (s->bufferSize - available) * 1000 / s->channels / s->rate /
|
||||
sizeof(int16_t) * NANOSECONDS_IN_MILLISECOND;
|
||||
ALOG("%p - Drain - flush %u, sleep for %llu ns", s, available, x);
|
||||
|
||||
struct timespec ts = {(time_t)(x / NANOSECONDS_PER_SECOND),
|
||||
(time_t)(x % NANOSECONDS_PER_SECOND)};
|
||||
nanosleep(&ts, NULL);
|
||||
s->output_unit->flush();
|
||||
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
||||
@ -239,11 +393,11 @@ sa_stream_drain(sa_stream_t *s)
|
||||
int
|
||||
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
|
||||
|
||||
if (s == NULL) {
|
||||
if (s == NULL || s->output_unit == NULL) {
|
||||
return SA_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
// XXX
|
||||
s->output_unit->setVolume(vol, vol);
|
||||
return SA_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user