Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  aaudio.c   Sprache: C

 

/*****************************************************************************
 * aaudio.c: Android AAudio audio stream module
 *****************************************************************************
 * Copyright © 2018-2022 VLC authors, VideoLAN and VideoLabs
 *
 * Authors: Romain Vimont <rom1v@videolabs.io>
 *          Thomas Guillem <thomas@gllm.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>
#include <assert.h>
#include <dlfcn.h>

#include "device.h"
#include "dynamicsprocessing_jni.h"

#include <aaudio/AAudio.h>

#define TIMING_REPORT_DELAY_TICKS VLC_TICK_FROM_MS(1000)

struct sys
{
    struct
    {
        aaudio_format_t format;
        int32_t session_id;
        int32_t device_id;
        bool low_latency;
    } cfg;

    AAudioStream *as;

    jobject dp;
    float volume;
    bool muted;

    audio_sample_format_t fmt;
    bool error;

    /* Spinlock that protect all the following variables */
    vlc_mutex_t lock;

    /* Frame FIFO */
    vlc_frame_t *frame_chain;
    vlc_frame_t **frame_last;

    /* Size of the frame FIFO */
    size_t frames_total_bytes;
    /* Bytes of silence written until it started */
    size_t start_silence_bytes;
    /* Bytes of silence written after it started, used for warning */
    size_t underrun_warn_bytes;
    /* Bytes of silence written after it started */
    size_t underrun_total_bytes;

    /* Date when the data callback should start to process audio */
    vlc_tick_t first_play_date;
    /* True is the data callback started to process audio from the frame FIFO */
    bool started;
    bool draining;
    /* Bytes written since the last timing report */
    size_t timing_report_last_written_bytes;
    /* Number of bytes to write before sending a timing report */
    size_t timing_report_delay_bytes;
    vlc_tick_t first_pts;

    /* Channel reordering */
    uint8_t chans_to_reorder;
    uint8_t chan_table[AOUT_CHAN_MAX];
};

/* dlopen/dlsym symbols */
static struct {
    void *handle;
    aaudio_result_t       (*AAudio_createStreamBuilder)(AAudioStreamBuilder **);
    const char           *(*AAudio_convertResultToText)(aaudio_result_t);
    void                  (*AAudioStreamBuilder_setDeviceId)(AAudioStreamBuilder *, int32_t);
    void                  (*AAudioStreamBuilder_setFormat)(AAudioStreamBuilder *, aaudio_format_t);
    void                  (*AAudioStreamBuilder_setChannelCount)(AAudioStreamBuilder *, int32_t);
    void                  (*AAudioStreamBuilder_setChannelMask)(AAudioStreamBuilder *, aaudio_channel_mask_t);
    void                  (*AAudioStreamBuilder_setDataCallback)(AAudioStreamBuilder *, AAudioStream_dataCallback, void *);
    void                  (*AAudioStreamBuilder_setErrorCallback)(AAudioStreamBuilder *, AAudioStream_errorCallback,  void *);
    void                  (*AAudioStreamBuilder_setPerformanceMode)(AAudioStreamBuilder *, aaudio_performance_mode_t);
    void                  (*AAudioStreamBuilder_setSessionId)(AAudioStreamBuilder *, aaudio_session_id_t);
    void                  (*AAudioStreamBuilder_setUsage)(AAudioStreamBuilder *, aaudio_usage_t);
    aaudio_result_t       (*AAudioStreamBuilder_openStream)(AAudioStreamBuilder *, AAudioStream **);
    void                  (*AAudioStreamBuilder_delete)(AAudioStreamBuilder *);
    aaudio_result_t       (*AAudioStream_requestStart)(AAudioStream *);
    aaudio_result_t       (*AAudioStream_requestStop)(AAudioStream *);
    aaudio_result_t       (*AAudioStream_requestPause)(AAudioStream *);
    aaudio_result_t       (*AAudioStream_requestFlush)(AAudioStream *);
    int32_t               (*AAudioStream_getDeviceId)(AAudioStream *);
    int32_t               (*AAudioStream_getSampleRate)(AAudioStream *);
    aaudio_result_t       (*AAudioStream_getTimestamp)(AAudioStream *, clockid_t,
                                                       int64_t *framePosition, int64_t *timeNanoseconds);
    aaudio_result_t       (*AAudioStream_write)(AAudioStream *, void *, int32_t numFrames, int64_t timeoutNanoseconds);
    aaudio_result_t       (*AAudioStream_close)(AAudioStream *);
    aaudio_stream_state_t (*AAudioStream_getState)(AAudioStream *);
    aaudio_result_t       (*AAudioStream_waitForStateChange)(AAudioStream *, aaudio_stream_state_t current_state, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds);
    aaudio_session_id_t   (*AAudioStream_getSessionId)(AAudioStream *);
} vt;

static int
LoadSymbols(aout_stream_t *stream)
{
    /* Force failure on Android < 8.1, where multiple restarts may cause
     * segfault (unless we leak the AAudioStream on Close). The problem is
     * fixed in AOSP by:
     * <https://android.googlesource.com/platform/frameworks/av/+/8a8a9e5d91c8cc110b9916982f4c5242efca33e3%5E%21/>
     *
     * Require AAudioStreamBuilder_setSessionId() that is available in Android
     * 9 to enforce that the previous issue is fixed and to allow us attaching
     * an AudioEffect to modify the gain.
     */


    static vlc_mutex_t lock = VLC_STATIC_MUTEX;
    static int init_state = -1;

    vlc_mutex_lock(&lock);

    if (init_state != -1)
        goto end;

    vt.handle = dlopen("libaaudio.so", RTLD_NOW);
    if (vt.handle == NULL)
    {
        msg_Err(stream, "Failed to load libaaudio");
        init_state = 0;
        goto end;
    }

#define AAUDIO_DLSYM(name, critical) \
    do { \
        void *sym = dlsym(vt.handle, #name); \
        if (unlikely(!sym) && critical) { \
            msg_Err(stream, "Failed to load symbol "#name); \
            init_state = 0; \
            goto end; \
        } \
        *(void **) &vt.name = sym; \
    } while(0)

    AAUDIO_DLSYM(AAudio_createStreamBuilder, true);
    AAUDIO_DLSYM(AAudio_convertResultToText, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setChannelCount, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setChannelMask, false);
    AAUDIO_DLSYM(AAudioStreamBuilder_setDeviceId, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setFormat, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setDataCallback, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setErrorCallback, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setPerformanceMode, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setSessionId, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_setUsage, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_openStream, true);
    AAUDIO_DLSYM(AAudioStreamBuilder_delete, true);
    AAUDIO_DLSYM(AAudioStream_requestStart, true);
    AAUDIO_DLSYM(AAudioStream_requestStop, true);
    AAUDIO_DLSYM(AAudioStream_requestPause, true);
    AAUDIO_DLSYM(AAudioStream_requestFlush, true);
    AAUDIO_DLSYM(AAudioStream_getDeviceId, true);
    AAUDIO_DLSYM(AAudioStream_getSampleRate, true);
    AAUDIO_DLSYM(AAudioStream_getTimestamp, true);
    AAUDIO_DLSYM(AAudioStream_write, true);
    AAUDIO_DLSYM(AAudioStream_close, true);
    AAUDIO_DLSYM(AAudioStream_getState, true);
    AAUDIO_DLSYM(AAudioStream_waitForStateChange, true);
    AAUDIO_DLSYM(AAudioStream_getSessionId, true);
#undef AAUDIO_DLSYM

    init_state = 1;
end:
    vlc_mutex_unlock(&lock);
    return init_state == 1 ? VLC_SUCCESS : VLC_EGENERIC;
}

struct role
{
    char vlc[16];
    aaudio_usage_t aaudio;
};

static const struct role roles[] =
{
    { "accessibility", AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY },
    { "animation",     AAUDIO_USAGE_ASSISTANCE_SONIFICATION  },
    { "communication", AAUDIO_USAGE_VOICE_COMMUNICATION      },
    { "game",          AAUDIO_USAGE_GAME                     },
    { "music",         AAUDIO_USAGE_MEDIA                    },
    { "notification",  AAUDIO_USAGE_NOTIFICATION_EVENT       },
    { "production",    AAUDIO_USAGE_MEDIA                    },
    { "test",          AAUDIO_USAGE_MEDIA                    },
    { "video",         AAUDIO_USAGE_MEDIA                    },
};

static int
role_cmp(const void *vlc_role, const void *entry)
{
    const struct role *role = entry;
    return strcmp(vlc_role, role->vlc);
}

static aaudio_usage_t
GetUsageFromVLCRole(const char *vlc_role)
{
    struct role *role = bsearch(vlc_role, roles, ARRAY_SIZE(roles),
                                sizeof(*roles), role_cmp);

    return role == NULL ? AAUDIO_USAGE_MEDIA : role->aaudio;
}

static inline void
LogAAudioError(aout_stream_t *stream, const char *msg, aaudio_result_t result)
{
    msg_Err(stream, "%s: %s", msg, vt.AAudio_convertResultToText(result));
}

static int
WaitState(aout_stream_t *stream, aaudio_result_t wait_state)
{
#define WAIT_TIMEOUT VLC_TICK_FROM_SEC(2)
    struct sys *sys = stream->sys;

    if (sys->error)
        return VLC_EGENERIC;

    assert(sys->as);

    aaudio_stream_state_t next_state = vt.AAudioStream_getState(sys->as);
    aaudio_stream_state_t current_state = next_state;

    while (next_state != wait_state)
    {
        aaudio_result_t result =
            vt.AAudioStream_waitForStateChange(sys->as, current_state,
                                               &next_state, WAIT_TIMEOUT);
        if (result != AAUDIO_OK)
        {
            sys->error = true;
            return VLC_EGENERIC;
        }
        current_state = next_state;
    }
    return VLC_SUCCESS;
}

static aaudio_stream_state_t
GetState(aout_stream_t *stream)
{
    struct sys *sys = stream->sys;

    if (sys->error)
        return AAUDIO_STREAM_STATE_UNINITIALIZED;

    assert(sys->as);

    return vt.AAudioStream_getState(sys->as);
}

#define Request(x) do { \
    struct sys *sys = stream->sys; \
    aaudio_result_t result = vt.AAudioStream_request##x(sys->as); \
    if (result == AAUDIO_OK) \
        return VLC_SUCCESS; \
    LogAAudioError(stream, "Failed to start AAudio stream", result); \
    sys->error = true; \
    return VLC_EGENERIC; \
} while (0)

static inline int
RequestStart(aout_stream_t *stream)
{
    Request(Start);
}

static inline int
RequestStop(aout_stream_t *stream)
{
    Request(Stop);
}

static inline int
RequestPause(aout_stream_t *stream)
{
    Request(Pause);
}

static inline int
RequestFlush(aout_stream_t *stream)
{
    Request(Flush);
}

static inline uint64_t
BytesToFrames(struct sys *sys, size_t bytes)
{
    return bytes * sys->fmt.i_frame_length / sys->fmt.i_bytes_per_frame;
}

static inline vlc_tick_t
FramesToTicks(struct sys *sys, int64_t frames)
{
    return vlc_tick_from_samples(frames, sys->fmt.i_rate);
}


static inline vlc_tick_t
BytesToTicks(struct sys *sys, size_t bytes)
{
    return FramesToTicks(sys, BytesToFrames(sys, bytes));
}

static inline size_t
FramesToBytes(struct sys *sys, uint64_t frames)
{
    return frames * sys->fmt.i_bytes_per_frame / sys->fmt.i_frame_length;
}

static inline int64_t
TicksToFrames(struct sys *sys, vlc_tick_t ticks)
{
    return samples_from_vlc_tick(ticks, sys->fmt.i_rate);
}

static inline size_t
TicksToBytes(struct sys *sys, vlc_tick_t ticks)
{
    return FramesToBytes(sys, TicksToFrames(sys, ticks));
}

static int
GetFrameTimestampLocked(aout_stream_t *stream, int64_t *pos_frames,
                        vlc_tick_t *frame_time_us)
{
    struct sys *sys = stream->sys;

    int64_t time_ns;
    aaudio_result_t result =
            vt.AAudioStream_getTimestamp(sys->as, CLOCK_MONOTONIC,
                                         pos_frames, &time_ns);
    if (result != AAUDIO_OK)
    {
        LogAAudioError(stream, "AAudioStream_getTimestamp failed", result);
        return VLC_EGENERIC;
    }
    if (*pos_frames <= 0)
        return VLC_EGENERIC;

    *frame_time_us = VLC_TICK_FROM_NS(time_ns);
    return VLC_SUCCESS;
}

static aaudio_data_callback_result_t
DataCallback(AAudioStream *as, void *user, void *data_, int32_t num_frames)
{
    aout_stream_t *stream = user;
    struct sys *sys = stream->sys;
    assert(as == sys->as); (void) as;

    size_t bytes = FramesToBytes(sys, num_frames);
    uint8_t *data = data_;

    vlc_mutex_lock(&sys->lock);
    aaudio_stream_state_t state = GetState(stream);

    if (!sys->started)
    {
        size_t tocopy;

        if (sys->first_play_date == VLC_TICK_INVALID)
            tocopy = bytes;
        else
        {
            vlc_tick_t now = vlc_tick_now();

            /* Write silence to reach the first play date */
            vlc_tick_t silence_ticks = sys->first_play_date - now;
            if (silence_ticks > 0)
            {
                tocopy = TicksToBytes(sys, silence_ticks);
                if (tocopy > bytes)
                    tocopy = bytes;
            }
            else
                tocopy = 0;
        }

        if (tocopy > 0)
        {
            memset(data, 0, tocopy);

            data += tocopy;
            bytes -= tocopy;

            sys->start_silence_bytes += tocopy;
        }
    }

    while (bytes > 0)
    {
        vlc_frame_t *f = sys->frame_chain;
        if (f == NULL)
        {
            aaudio_data_callback_result_t res;

            if (!sys->draining)
            {
                sys->underrun_warn_bytes += bytes;
                sys->underrun_total_bytes += bytes;
                res = AAUDIO_CALLBACK_RESULT_CONTINUE;
            }
            else
                res = AAUDIO_CALLBACK_RESULT_STOP;

            vlc_mutex_unlock(&sys->lock);

            memset(data, 0, bytes);

            if (res == AAUDIO_CALLBACK_RESULT_STOP)
                aout_stream_DrainedReport(stream);
            return res;
        }

        size_t tocopy = f->i_buffer > bytes ? bytes : f->i_buffer;

        sys->started = true;

        sys->frames_total_bytes -= tocopy;
        sys->timing_report_last_written_bytes += tocopy;

        int64_t pos_frames;
        vlc_tick_t system_ts;
        if (state == AAUDIO_STREAM_STATE_STARTED
         && sys->timing_report_last_written_bytes >= sys->timing_report_delay_bytes
         && GetFrameTimestampLocked(stream, &pos_frames, &system_ts) == VLC_SUCCESS)
        {

            vlc_tick_t pos_ticks = FramesToTicks(sys, pos_frames);
            /* underrun 0s don't count in timing reports */
            vlc_tick_t underrun_ticks = BytesToTicks(sys, sys->underrun_total_bytes);

            /* pos_frame might not have taken the last underrun update */
            if (likely(pos_ticks > underrun_ticks))
            {
                pos_ticks -= underrun_ticks;

                /* From now on, fetch the timestamp every 1 seconds */
                sys->timing_report_last_written_bytes = 0;
                sys->timing_report_delay_bytes =
                    TicksToBytes(sys, TIMING_REPORT_DELAY_TICKS);

                /* Add the start silence to the system time and don't subtract
                 * it from pos_ticks to avoid (unlikely) negatives ts */

                system_ts += BytesToTicks(sys, sys->start_silence_bytes);
                aout_stream_TimingReport(stream, system_ts,
                                         pos_ticks + sys->first_pts);
            }
        }

        memcpy(data, f->p_buffer, tocopy);

        data += tocopy;
        bytes -= tocopy;
        f->i_buffer -= tocopy;
        f->p_buffer += tocopy;

        if (f->i_buffer == 0)
        {
            sys->frame_chain = f->p_next;
            if (sys->frame_chain == NULL)
                sys->frame_last = &sys->frame_chain;

            vlc_frame_Release(f);
        }
    }

    vlc_mutex_unlock(&sys->lock);

    return AAUDIO_CALLBACK_RESULT_CONTINUE;
}

static void
ErrorCallback(AAudioStream *as, void *user, aaudio_result_t error)
{
    aout_stream_t *stream = user;
    (void) as;

    LogAAudioError(stream, "AAudio stream error or disconnected:", error);
    aout_stream_RestartRequest(stream, false);
}

static void
CloseAAudioStream(aout_stream_t *stream)
{
    struct sys *sys = stream->sys;

    if (!sys->error)
    {
        RequestStop(stream);

        if (WaitState(stream, AAUDIO_STREAM_STATE_STOPPED) != VLC_SUCCESS)
            msg_Warn(stream, "Error waiting for stopped state");
    }

    vt.AAudioStream_close(sys->as);

    sys->as = NULL;
    sys->error = false;
}

static aaudio_channel_mask_t
VLCMaskToAaudio(uint16_t vlc_mask)
{
    aaudio_channel_mask_t mask = 0;
    if (vlc_mask & AOUT_CHAN_CENTER)
        mask |= AAUDIO_CHANNEL_FRONT_CENTER;
    if (vlc_mask & AOUT_CHAN_LEFT)
        mask |= AAUDIO_CHANNEL_FRONT_LEFT;
    if (vlc_mask & AOUT_CHAN_RIGHT)
        mask |= AAUDIO_CHANNEL_FRONT_RIGHT;
    if (vlc_mask & AOUT_CHAN_REARCENTER)
        mask |= AAUDIO_CHANNEL_BACK_CENTER;
    if (vlc_mask & AOUT_CHAN_REARLEFT)
        mask |= AAUDIO_CHANNEL_BACK_LEFT;
    if (vlc_mask & AOUT_CHAN_REARRIGHT)
        mask |= AAUDIO_CHANNEL_BACK_RIGHT;
    if (vlc_mask & AOUT_CHAN_MIDDLELEFT)
        mask |= AAUDIO_CHANNEL_SIDE_LEFT;
    if (vlc_mask & AOUT_CHAN_MIDDLERIGHT)
        mask |= AAUDIO_CHANNEL_SIDE_RIGHT;
    if (vlc_mask & AOUT_CHAN_LFE)
        mask |= AAUDIO_CHANNEL_LOW_FREQUENCY;
    return mask;
}

static int
OpenAAudioStream(aout_stream_t *stream)
{
    struct sys *sys = stream->sys;
    aaudio_result_t result;

    AAudioStreamBuilder *builder;
    result = vt.AAudio_createStreamBuilder(&builder);
    if (result != AAUDIO_OK)
    {
        LogAAudioError(stream, "Failed to create AAudio stream builder", result);
        return VLC_EGENERIC;
    }

    vt.AAudioStreamBuilder_setFormat(builder, sys->cfg.format);

    uint32_t chans_out[AOUT_CHAN_MAX];
    AndroidDevice_GetChanOrder(sys->fmt.i_physical_channels, chans_out,
                               AOUT_CHAN_MAX );
    sys->chans_to_reorder =
        aout_CheckChannelReorder(NULL, chans_out,
                                 sys->fmt.i_physical_channels,
                                 sys->chan_table );

    if (vt.AAudioStreamBuilder_setChannelMask != NULL)
    {
        aaudio_channel_mask_t mask =
            VLCMaskToAaudio(sys->fmt.i_physical_channels);
        vt.AAudioStreamBuilder_setChannelMask(builder, mask);
    }
    else
        vt.AAudioStreamBuilder_setChannelCount(builder, sys->fmt.i_channels);

    /* Setup the session-id */
    vt.AAudioStreamBuilder_setSessionId(builder, sys->cfg.session_id);

    /* Setup the role */
    char *vlc_role = var_InheritString(stream, "role");
    if (vlc_role != NULL)
    {
        aaudio_usage_t usage = GetUsageFromVLCRole(vlc_role);
        vt.AAudioStreamBuilder_setUsage(builder, usage);
        free(vlc_role);
    } /* else if not set, default is MEDIA usage */

    vt.AAudioStreamBuilder_setDataCallback(builder, DataCallback, stream);
    vt.AAudioStreamBuilder_setErrorCallback(builder, ErrorCallback, stream);

    if (sys->cfg.low_latency)
        vt.AAudioStreamBuilder_setPerformanceMode(builder,
            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

    if (sys->cfg.device_id != AAUDIO_UNSPECIFIED)
        vt.AAudioStreamBuilder_setDeviceId(builder, sys->cfg.device_id);

    AAudioStream *as;
    result = vt.AAudioStreamBuilder_openStream(builder, &as);
    if (result != AAUDIO_OK)
    {
        LogAAudioError(stream, "Failed to open AAudio stream", result);
        vt.AAudioStreamBuilder_delete(builder);
        return VLC_EGENERIC;
    }
    vt.AAudioStreamBuilder_delete(builder);

    sys->as = as;

    return VLC_SUCCESS;
}

static void
PrepareAudioFormat(aout_stream_t *stream, audio_sample_format_t *fmt)
{
    struct sys *sys = stream->sys;

    sys->cfg.device_id = AAUDIO_UNSPECIFIED;

    sys->cfg.session_id = var_InheritInteger(stream, "audiotrack-session-id");
    if (sys->cfg.session_id == 0)
        sys->cfg.session_id = AAUDIO_SESSION_ID_ALLOCATE;

    if (vt.AAudioStreamBuilder_setChannelMask == NULL)
    {
        /* Without the proper setChannelMask API, support only mono, stereo,
         * 5.1 and 7.1 */

        unsigned channels = aout_FormatNbChannels( &sys->fmt );
        if (channels > 7)
            sys->fmt.i_physical_channels = AOUT_CHANS_7_1;
        else if (channels > 5)
            sys->fmt.i_physical_channels = AOUT_CHANS_5_1;
        else if (channels == 1)
            sys->fmt.i_physical_channels = AOUT_CHAN_LEFT;
        else
            sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;
        aout_FormatPrepare(fmt);
    }

    if (fmt->i_format == VLC_CODEC_S16N)
        sys->cfg.format = AAUDIO_FORMAT_PCM_I16;
    else
    {
        if (fmt->i_format != VLC_CODEC_FL32)
        {
            /* override to request conversion */
            fmt->i_format = VLC_CODEC_FL32;
            fmt->i_bytes_per_frame = 4 * fmt->i_channels;
        }
        sys->cfg.format = AAUDIO_FORMAT_PCM_FLOAT;
    }

    if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
    {
        fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
        sys->cfg.low_latency = true;
    }
    else
        sys->cfg.low_latency = var_InheritBool(stream, "low-delay");

    sys->fmt = *fmt;
}

static void
Play(aout_stream_t *stream, vlc_frame_t *frame, vlc_tick_t date)
{
    struct sys *sys = stream->sys;

    aaudio_stream_state_t state = GetState(stream);
    if (state == AAUDIO_STREAM_STATE_OPEN)
    {
        if (RequestStart(stream) != VLC_SUCCESS)
            goto bailout;
    }
    else if (state != AAUDIO_STREAM_STATE_STARTING
          && state != AAUDIO_STREAM_STATE_STARTED)
        goto bailout;

    assert(sys->as);

    if (sys->chans_to_reorder > 0)
       aout_ChannelReorder(frame->p_buffer, frame->i_buffer,
                           sys->chans_to_reorder, sys->chan_table,
                           sys->fmt.i_format );

    vlc_mutex_lock(&sys->lock);

    if (!sys->started)
    {
        vlc_tick_t now = vlc_tick_now();
        sys->first_play_date = date - BytesToTicks(sys, sys->frames_total_bytes);
        if (sys->first_pts == VLC_TICK_INVALID)
            sys->first_pts = frame->i_pts;

        if (sys->first_play_date > now)
            msg_Dbg(stream, "deferring start (%"PRId64" us)",
                    sys->first_play_date - now);
        else
            msg_Dbg(stream, "starting late (%"PRId64" us)",
                    sys->first_play_date - now);
    }


    vlc_frame_ChainLastAppend(&sys->frame_last, frame);
    sys->frames_total_bytes += frame->i_buffer;

    size_t underrun_bytes = sys->underrun_warn_bytes;
    sys->underrun_warn_bytes = 0;
    vlc_mutex_unlock(&sys->lock);

    if (underrun_bytes > 0)
        msg_Warn(stream, "underflow of %" PRId64 " us",
                 BytesToTicks(sys, underrun_bytes));

    return;
bailout:
    vlc_frame_Release(frame);
}

static void
Pause(aout_stream_t *stream, bool pause, vlc_tick_t pause_date)
{
    struct sys *sys = stream->sys;
    (void) pause_date;

    aaudio_stream_state_t state = GetState(stream);
    if (state == AAUDIO_STREAM_STATE_FLUSHED)
        return; /* already paused, will be resumed from Play() */

    if (pause)
    {
        if (state == AAUDIO_STREAM_STATE_STARTING
         || state == AAUDIO_STREAM_STATE_STARTED)
            RequestPause(stream);

        state = GetState(stream);
        if (state == AAUDIO_STREAM_STATE_PAUSING
         && WaitState(stream, AAUDIO_STREAM_STATE_PAUSED) != VLC_SUCCESS)

        sys->started = false;
    }
    else
    {
        if (state == AAUDIO_STREAM_STATE_PAUSING
         || state == AAUDIO_STREAM_STATE_PAUSED)
            RequestStart(stream);
    }
}

static void
Flush(aout_stream_t *stream)
{
    struct sys *sys = stream->sys;
    aaudio_stream_state_t state = GetState(stream);

    if (state == AAUDIO_STREAM_STATE_UNINITIALIZED)
        goto error;
    if (state == AAUDIO_STREAM_STATE_OPEN)
        return;

    /* Flush must be requested while PAUSED */

    if (state != AAUDIO_STREAM_STATE_PAUSING
     && state != AAUDIO_STREAM_STATE_PAUSED
     && RequestPause(stream) != VLC_SUCCESS)
        goto error;

    state = GetState(stream);
    if (state == AAUDIO_STREAM_STATE_PAUSING
     && WaitState(stream, AAUDIO_STREAM_STATE_PAUSED) != VLC_SUCCESS)
        goto error;

    if (RequestFlush(stream) != VLC_SUCCESS)
        goto error;

    if (WaitState(stream, AAUDIO_STREAM_STATE_FLUSHED) != VLC_SUCCESS)
        goto error;

error:
    CloseAAudioStream(stream);

    vlc_frame_ChainRelease(sys->frame_chain);
    sys->frame_chain = NULL;
    sys->frame_last = &sys->frame_chain;
    sys->frames_total_bytes = 0;

    sys->started = false;
    sys->draining = false;
    sys->first_pts = sys->first_play_date = VLC_TICK_INVALID;
    sys->start_silence_bytes = 0;
    sys->timing_report_last_written_bytes = 0;
    sys->timing_report_delay_bytes = 0;
    sys->underrun_warn_bytes = 0;
    sys->underrun_total_bytes = 0;

    int ret = OpenAAudioStream(stream);
    if (ret != VLC_SUCCESS)
    {
        sys->error = true;
        return;
    }

    int32_t new_rate = vt.AAudioStream_getSampleRate(sys->as);
    assert(new_rate > 0);
    if ((unsigned) new_rate != sys->fmt.i_rate)
    {
        msg_Err(stream, "rate changed after flush, from %u to %d",
                sys->fmt.i_rate, new_rate);
        aout_stream_RestartRequest(stream, false);
    }
}

static void
Drain(aout_stream_t *stream)
{
    struct sys *sys = stream->sys;

    vlc_mutex_lock(&sys->lock);
    sys->draining = true;
    vlc_mutex_unlock(&sys->lock);

    if (unlikely(sys->first_play_date == VLC_TICK_INVALID))
        aout_stream_DrainedReport(stream);
}

static void
VolumeSet(aout_stream_t *stream, float vol)
{
    struct sys *sys = stream->sys;

    if (vol > 1.0f)
        vol = vol * vol * vol;

    sys->volume = vol;

    if (sys->muted || sys->dp == NULL)
        return;

    int ret = DynamicsProcessing_SetVolume(stream, sys->dp, vol);
    if (ret != VLC_SUCCESS)
        msg_Warn(stream, "failed to set the volume via DynamicsProcessing");
}

static void
MuteSet(aout_stream_t *stream, bool mute)
{
    struct sys *sys = stream->sys;

    sys->muted = mute;

    if (sys->dp == NULL)
        return;

    int ret = DynamicsProcessing_SetVolume(stream, sys->dp,
                                           mute ? 0.0f : sys->volume);
    if (ret != VLC_SUCCESS)
        msg_Warn(stream, "failed to mute via DynamicsProcessing");
}

static void
Stop(aout_stream_t *stream)
{
    struct sys *sys = stream->sys;

    if (sys->as != NULL)
        CloseAAudioStream(stream);

    if (sys->dp != NULL)
        DynamicsProcessing_Delete(stream, sys->dp);

    vlc_frame_ChainRelease(sys->frame_chain);

    free(sys);
}

static int
Start(aout_stream_t *stream, audio_sample_format_t *fmt,
      enum android_audio_device_type adev)
{
    if (!AOUT_FMT_LINEAR(fmt))
        return VLC_EGENERIC;

    if (LoadSymbols(stream) != VLC_SUCCESS)
        return VLC_EGENERIC;

    if (adev == ANDROID_AUDIO_DEVICE_STEREO)
    {
        fmt->i_physical_channels = AOUT_CHANS_STEREO;
        aout_FormatPrepare(fmt);
    }

    struct sys *sys = stream->sys = malloc(sizeof(*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

    sys->volume = 1.0f;
    sys->muted = false;
    sys->error = false;

    vlc_mutex_init(&sys->lock);
    sys->frame_chain = NULL;
    sys->frame_last = &sys->frame_chain;
    sys->frames_total_bytes = 0;
    sys->start_silence_bytes = 0;
    sys->underrun_warn_bytes = 0;
    sys->underrun_total_bytes = 0;

    sys->started = false;
    sys->draining = false;
    sys->first_pts = sys->first_play_date = VLC_TICK_INVALID;
    sys->timing_report_last_written_bytes = 0;
    sys->timing_report_delay_bytes = 0;

    PrepareAudioFormat(stream, fmt);

    int ret = OpenAAudioStream(stream);
    if (ret != VLC_SUCCESS)
    {
        free(sys);
        return ret;
    }

    /* Use the native sample rate of the device */
    sys->fmt.i_rate = fmt->i_rate = vt.AAudioStream_getSampleRate(sys->as);
    assert(fmt->i_rate > 0);

    sys->cfg.session_id = vt.AAudioStream_getSessionId(sys->as);

    if (sys->cfg.session_id != AAUDIO_SESSION_ID_NONE)
        sys->dp = DynamicsProcessing_New(stream, sys->cfg.session_id);
    else
        sys->dp = NULL;

    if (sys->dp == NULL)
        msg_Warn(stream, "failed to attach DynamicsProcessing to the stream)");

    sys->cfg.device_id = vt.AAudioStream_getDeviceId(sys->as);

    stream->stop = Stop;
    stream->play = Play;
    stream->pause = Pause;
    stream->flush = Flush;
    stream->drain = Drain;
    stream->volume_set = VolumeSet;
    stream->mute_set = MuteSet;

    msg_Dbg(stream, "using AAudio API");
    return VLC_SUCCESS;
}

vlc_module_begin ()
    set_shortname("AAudio")
    set_description("Android AAudio output")
    set_subcategory(SUBCAT_AUDIO_AOUT)
    set_capability("aout android stream", 190)
    set_callback(Start)
vlc_module_end ()

Messung
CHZG
829810093

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104851
#Domains=417124