#include "audio_player.h" #include "util/log.h" /** Downcast frame_sink to sc_audio_player */ #define DOWNCAST(SINK) container_of(SINK, struct sc_audio_player, frame_sink) #define SC_SDL_SAMPLE_FMT AUDIO_F32 static void SDLCALL sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) { struct sc_audio_player *ap = userdata; assert(len_int > 0); size_t len = len_int; assert(len % ap->audioreg.sample_size == 0); uint32_t out_samples = len / ap->audioreg.sample_size; sc_audio_regulator_pull(&ap->audioreg, stream, out_samples); } static bool sc_audio_player_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { struct sc_audio_player *ap = DOWNCAST(sink); return sc_audio_regulator_push(&ap->audioreg, frame); } static bool sc_audio_player_frame_sink_open(struct sc_frame_sink *sink, const AVCodecContext *ctx) { struct sc_audio_player *ap = DOWNCAST(sink); #ifdef SCRCPY_LAVU_HAS_CHLAYOUT assert(ctx->ch_layout.nb_channels > 0 && ctx->ch_layout.nb_channels < 256); uint8_t nb_channels = ctx->ch_layout.nb_channels; #else int tmp = av_get_channel_layout_nb_channels(ctx->channel_layout); assert(tmp > 0 && tmp < 256); uint8_t nb_channels = tmp; #endif assert(ctx->sample_rate > 0); assert(!av_sample_fmt_is_planar(SC_AV_SAMPLE_FMT)); int out_bytes_per_sample = av_get_bytes_per_sample(SC_AV_SAMPLE_FMT); assert(out_bytes_per_sample > 0); uint32_t target_buffering_samples = ap->target_buffering_delay * ctx->sample_rate / SC_TICK_FREQ; size_t sample_size = nb_channels * out_bytes_per_sample; bool ok = sc_audio_regulator_init(&ap->audioreg, sample_size, ctx, target_buffering_samples); if (!ok) { return false; } uint64_t aout_samples = ap->output_buffer_duration * ctx->sample_rate / SC_TICK_FREQ; assert(aout_samples <= 0xFFFF); SDL_AudioSpec desired = { .freq = ctx->sample_rate, .format = SC_SDL_SAMPLE_FMT, .channels = nb_channels, .samples = aout_samples, .callback = sc_audio_player_sdl_callback, .userdata = ap, }; SDL_AudioSpec obtained; ap->device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0); if (!ap->device) { LOGE("Could not open audio device: %s", SDL_GetError()); sc_audio_regulator_destroy(&ap->audioreg); return false; } // The thread calling open() is the thread calling push(), which fills the // audio buffer consumed by the SDL audio thread. ok = sc_thread_set_priority(SC_THREAD_PRIORITY_TIME_CRITICAL); if (!ok) { ok = sc_thread_set_priority(SC_THREAD_PRIORITY_HIGH); (void) ok; // We don't care if it worked, at least we tried } SDL_PauseAudioDevice(ap->device, 0); return true; } static void sc_audio_player_frame_sink_close(struct sc_frame_sink *sink) { struct sc_audio_player *ap = DOWNCAST(sink); assert(ap->device); SDL_PauseAudioDevice(ap->device, 1); SDL_CloseAudioDevice(ap->device); sc_audio_regulator_destroy(&ap->audioreg); } void sc_audio_player_init(struct sc_audio_player *ap, sc_tick target_buffering, sc_tick output_buffer_duration) { ap->target_buffering_delay = target_buffering; ap->output_buffer_duration = output_buffer_duration; static const struct sc_frame_sink_ops ops = { .open = sc_audio_player_frame_sink_open, .close = sc_audio_player_frame_sink_close, .push = sc_audio_player_frame_sink_push, }; ap->frame_sink.ops = &ops; }