]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/audiotrack.c
waveout: copy ALSA fake hotplug code (fixes #8482)
[vlc] / modules / audio_output / audiotrack.c
index 314125641361142b09b43dab814071ed109ee503..8f46a8d7325ef67bb5bad4c66d2a558edb94ec7b 100644 (file)
@@ -71,6 +71,8 @@ typedef int (*AudioSystem_getOutputSamplingRate)(int *, int);
 // _ZN7android10AudioTrack16getMinFrameCountEPiij
 typedef int (*AudioTrack_getMinFrameCount)(int *, int, unsigned int);
 
+// _ZN7android11AudioSystem17getRenderPositionEPjS1_i
+typedef int (*AudioTrack_getRenderPosition)(uint32_t *, uint32_t *, 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
@@ -91,6 +93,14 @@ typedef int (*AudioTrack_flush)(void *);
 typedef int (*AudioTrack_pause)(void *);
 
 struct aout_sys_t {
+    float soft_gain;
+    bool soft_mute;
+
+    int rate;
+    uint32_t samples_written;
+    uint32_t initial;
+    int bytes_per_frame;
+
     void *libmedia;
     void *AudioTrack;
 
@@ -108,14 +118,19 @@ struct aout_sys_t {
     AudioTrack_write at_write;
     AudioTrack_flush at_flush;
     AudioTrack_pause at_pause;
+    AudioTrack_getRenderPosition at_getRenderPosition;
 };
 
+/* Soft volume helper */
+#include "volume.h"
+
 static void *InitLibrary(struct aout_sys_t *p_sys);
 
 static int  Open(vlc_object_t *);
 static void Close(vlc_object_t *);
-static void Play(audio_output_t *, block_t *);
+static void Play(audio_output_t*, block_t*);
 static void Pause (audio_output_t *, bool, mtime_t);
+static void Flush (audio_output_t *, bool);
 
 vlc_module_begin ()
     set_shortname("AudioTrack")
@@ -123,6 +138,7 @@ vlc_module_begin ()
     set_capability("audio output", 225)
     set_category(CAT_AUDIO)
     set_subcategory(SUBCAT_AUDIO_AOUT)
+    add_sw_gain()
     add_shortcut("android")
     set_callbacks(Open, Close)
 vlc_module_end ()
@@ -158,6 +174,11 @@ static void *InitLibrary(struct aout_sys_t *p_sys)
     p_sys->at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
     p_sys->at_pause = (AudioTrack_pause)(dlsym(p_library, "_ZN7android10AudioTrack5pauseEv"));
 
+    /* this symbol can have different names depending on the mangling */
+    p_sys->at_getRenderPosition = (AudioTrack_getRenderPosition)(dlsym(p_library, "_ZN7android11AudioSystem17getRenderPositionEPjS1_i"));
+    if (!p_sys->at_getRenderPosition)
+        p_sys->at_getRenderPosition = (AudioTrack_getRenderPosition)(dlsym(p_library, "_ZN7android11AudioSystem17getRenderPositionEPjS1_19audio_stream_type_t"));
+
     /* We need the first 3 or the last 1 */
     if (!((p_sys->as_getOutputFrameCount && p_sys->as_getOutputLatency && p_sys->as_getOutputSamplingRate)
         || p_sys->at_getMinFrameCount)) {
@@ -174,28 +195,44 @@ static void *InitLibrary(struct aout_sys_t *p_sys)
     return p_library;
 }
 
-static int Open(vlc_object_t *p_this)
+static int TimeGet(audio_output_t *p_aout, mtime_t *restrict delay)
 {
-    struct aout_sys_t *p_sys;
-    audio_output_t *p_aout = (audio_output_t*)(p_this);
+    aout_sys_t *p_sys = p_aout->sys;
+    uint32_t hal, dsp;
 
-    int status, size;
-    int afSampleRate, afFrameCount, afLatency, minBufCount, minFrameCount;
-    int stream_type, channel, rate, format;
+    if (!p_sys->at_getRenderPosition)
+        return -1;
 
-    p_sys = (struct aout_sys_t*) malloc(sizeof(aout_sys_t));
-    if (!p_sys)
-        return VLC_ENOMEM;
+    if (p_sys->at_getRenderPosition(&hal, &dsp, MUSIC))
+        return -1;
 
-    p_sys->libmedia = InitLibrary(p_sys);
-    if (!p_sys->libmedia) {
-        msg_Err(p_aout, "Could not initialize libmedia.so!");
-        free(p_sys);
-        return VLC_EGENERIC;
+    hal = (uint32_t)((uint64_t)hal * p_sys->rate / 44100);
+
+    if (p_sys->samples_written == 0) {
+        p_sys->initial = hal;
+        return -1;
     }
 
+    hal -= p_sys->initial;
+    if (hal == 0)
+        return -1;
+
+    if (delay)
+        *delay = ((mtime_t)p_sys->samples_written - hal) * CLOCK_FREQ / p_sys->rate;
+
+    return 0;
+}
+
+static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
+{
+    struct aout_sys_t *p_sys = aout->sys;
+
+    int status, size;
+    int afSampleRate, afFrameCount, afLatency, minBufCount, minFrameCount;
+    int stream_type, channel, rate, format;
+
     /* 4000 <= frequency <= 48000 */
-    rate = p_aout->format.i_rate;
+    rate = fmt->i_rate;
     if (rate < 4000)
         rate = 4000;
     if (rate > 48000)
@@ -203,23 +240,23 @@ static int Open(vlc_object_t *p_this)
 
     stream_type = MUSIC;
 
-    /* We can only accept U8 and S16L */
-    if (p_aout->format.i_format != VLC_CODEC_U8 && p_aout->format.i_format != VLC_CODEC_S16L)
-        p_aout->format.i_format = VLC_CODEC_S16L;
-    format = (p_aout->format.i_format == VLC_CODEC_S16L) ? PCM_16_BIT : PCM_8_BIT;
+    /* We can only accept U8 and S16N */
+    if (fmt->i_format != VLC_CODEC_U8 && fmt->i_format != VLC_CODEC_S16N)
+        fmt->i_format = VLC_CODEC_S16N;
+    format = (fmt->i_format == VLC_CODEC_S16N) ? PCM_16_BIT : PCM_8_BIT;
 
     /* TODO: android supports more channels */
-    p_aout->format.i_original_channels = aout_FormatNbChannels(&p_aout->format);
-    switch(p_aout->format.i_original_channels)
+    fmt->i_original_channels = fmt->i_physical_channels;
+    switch(aout_FormatNbChannels(fmt))
     {
     case 1:
         channel = CHANNEL_OUT_MONO;
-        p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
+        fmt->i_physical_channels = AOUT_CHAN_CENTER;
         break;
     case 2:
     default:
         channel = CHANNEL_OUT_STEREO;
-        p_aout->format.i_physical_channels = AOUT_CHANS_STEREO;
+        fmt->i_physical_channels = AOUT_CHANS_STEREO;
         break;
     }
 
@@ -229,9 +266,7 @@ static int Open(vlc_object_t *p_this)
         status ^= p_sys->as_getOutputFrameCount(&afFrameCount, stream_type);
         status ^= p_sys->as_getOutputLatency((uint32_t*)(&afLatency), stream_type);
         if (status != 0) {
-            msg_Err(p_aout, "Could not query the AudioStream parameters");
-            dlclose(p_sys->libmedia);
-            free(p_sys);
+            msg_Err(aout, "Could not query the AudioStream parameters");
             return VLC_EGENERIC;
         }
         minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
@@ -242,9 +277,7 @@ static int Open(vlc_object_t *p_this)
     else {
         status = p_sys->at_getMinFrameCount(&minFrameCount, stream_type, rate);
         if (status != 0) {
-            msg_Err(p_aout, "Could not query the AudioTrack parameters");
-            dlclose(p_sys->libmedia);
-            free(p_sys);
+            msg_Err(aout, "Could not query the AudioTrack parameters");
             return VLC_EGENERIC;
         }
     }
@@ -253,11 +286,8 @@ static int Open(vlc_object_t *p_this)
 
     /* Sizeof(AudioTrack) == 0x58 (not sure) on 2.2.1, this should be enough */
     p_sys->AudioTrack = malloc(SIZE_OF_AUDIOTRACK);
-    if (!p_sys->AudioTrack) {
-        dlclose(p_sys->libmedia);
-        free(p_sys);
+    if (!p_sys->AudioTrack)
         return VLC_ENOMEM;
-    }
 
     *((uint32_t *) ((uint32_t)p_sys->AudioTrack + SIZE_OF_AUDIOTRACK - 4)) = 0xbaadbaad;
     // Higher than android 2.2
@@ -279,45 +309,56 @@ static int Open(vlc_object_t *p_this)
         status = p_sys->at_initCheck(p_sys->AudioTrack);
     }
     if (status != 0) {
-        msg_Err(p_aout, "Cannot create AudioTrack!");
-        dlclose(p_sys->libmedia);
+        msg_Err(aout, "Cannot create AudioTrack!");
         free(p_sys->AudioTrack);
-        free(p_sys);
         return VLC_EGENERIC;
     }
 
-    p_aout->sys = p_sys;
-    p_aout->pf_play = Play;
-    p_aout->pf_pause = Pause;
+    aout_SoftVolumeStart(aout);
+
+    aout->sys = p_sys;
+    aout->time_get = NULL;
+    aout->play = Play;
+    aout->pause = Pause;
+    aout->flush = Flush;
+    aout->time_get = TimeGet;
+
+    p_sys->rate = rate;
+    p_sys->samples_written = 0;
+    p_sys->bytes_per_frame = aout_FormatNbChannels(fmt) * (format == PCM_16_BIT) ? 2 : 1;
 
     p_sys->at_start(p_sys->AudioTrack);
+    TimeGet(aout, NULL); /* Gets the initial value of DAC samples counter */
 
-    p_aout->format.i_rate = rate;
+    fmt->i_rate = rate;
 
     return VLC_SUCCESS;
 }
 
-static void Close(vlc_object_t *p_this)
+static void Stop(audio_output_t* p_aout)
 {
-    audio_output_t *p_aout = (audio_output_t*)p_this;
     aout_sys_t *p_sys = p_aout->sys;
 
     p_sys->at_stop(p_sys->AudioTrack);
     p_sys->at_flush(p_sys->AudioTrack);
     p_sys->at_dtor(p_sys->AudioTrack);
     free(p_sys->AudioTrack);
-    dlclose(p_sys->libmedia);
-    free(p_sys);
 }
 
-/* FIXME: lipsync */
-static void Play(audio_output_t *p_aout, block_t *p_buffer)
+static void Play(audio_output_t* p_aout, block_t* p_buffer)
 {
     aout_sys_t *p_sys = p_aout->sys;
 
-    size_t length = 0;
-    while (length < p_buffer->i_buffer) {
-        length += p_sys->at_write(p_sys->AudioTrack, (char*)(p_buffer->p_buffer) + length, p_buffer->i_buffer - length);
+    while (p_buffer->i_buffer) {
+        int ret = p_sys->at_write(p_sys->AudioTrack, p_buffer->p_buffer, p_buffer->i_buffer);
+        if (ret < 0) {
+            msg_Err(p_aout, "Write failed (error %d)", ret);
+            break;
+        }
+
+        p_sys->samples_written += ret / p_sys->bytes_per_frame;
+        p_buffer->p_buffer += ret;
+        p_buffer->i_buffer -= ret;
     }
 
     block_Release( p_buffer );
@@ -325,6 +366,8 @@ static void Play(audio_output_t *p_aout, block_t *p_buffer)
 
 static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
 {
+    VLC_UNUSED(date);
+
     aout_sys_t *p_sys = p_aout->sys;
 
     if (pause) {
@@ -333,3 +376,49 @@ static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
         p_sys->at_start(p_sys->AudioTrack);
     }
 }
+
+static void Flush (audio_output_t *p_aout, bool wait)
+{
+    aout_sys_t *p_sys = p_aout->sys;
+    if (wait) {
+        mtime_t delay;
+        if (!TimeGet(p_aout, &delay))
+            msleep(delay);
+    } else {
+        p_sys->at_stop(p_sys->AudioTrack);
+        p_sys->at_flush(p_sys->AudioTrack);
+        p_sys->samples_written = 0;
+        p_sys->at_start(p_sys->AudioTrack);
+    }
+}
+
+static int Open(vlc_object_t *obj)
+{
+    audio_output_t *aout = (audio_output_t *)obj;
+    aout_sys_t *sys = malloc(sizeof (*sys));
+
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+
+    sys->libmedia = InitLibrary(sys);
+    if (sys->libmedia == NULL) {
+        msg_Err(aout, "Could not initialize libmedia.so!");
+        free(sys);
+        return VLC_EGENERIC;
+    }
+
+    aout->sys = sys;
+    aout->start = Start;
+    aout->stop = Stop;
+    aout_SoftVolumeInit(aout);
+    return VLC_SUCCESS;
+}
+
+static void Close(vlc_object_t *obj)
+{
+    audio_output_t *aout = (audio_output_t *)obj;
+    aout_sys_t *sys = aout->sys;
+
+    dlclose(sys->libmedia);
+    free(sys);
+}