summaryrefslogtreecommitdiffstats
path: root/audio/out
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out')
-rw-r--r--audio/out/ao_alsa.c78
-rw-r--r--audio/out/ao_coreaudio.c5
-rw-r--r--audio/out/ao_coreaudio_utils.c27
-rw-r--r--audio/out/ao_coreaudio_utils.h1
-rw-r--r--audio/out/ao_lavc.c82
-rw-r--r--audio/out/ao_opensles.c1
-rw-r--r--audio/out/ao_oss.c2
-rw-r--r--audio/out/ao_wasapi.c4
-rw-r--r--audio/out/ao_wasapi_changenotify.c1
-rw-r--r--audio/out/push.c27
10 files changed, 136 insertions, 92 deletions
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index 9ffd4792ee..d09f5fc499 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -58,6 +58,8 @@ struct priv {
snd_pcm_uframes_t buffersize;
snd_pcm_uframes_t outburst;
+ snd_output_t *output;
+
char *cfg_device;
char *cfg_mixer_device;
char *cfg_mixer_name;
@@ -477,6 +479,22 @@ static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels
#endif /* else HAVE_CHMAP_API */
+static void dump_hw_params(struct ao *ao, int msglevel, const char *msg,
+ snd_pcm_hw_params_t *hw_params)
+{
+ struct priv *p = ao->priv;
+ int err;
+
+ err = snd_pcm_hw_params_dump(hw_params, p->output);
+ CHECK_ALSA_WARN("Dump hwparams error");
+
+ char *tmp = NULL;
+ size_t tmp_s = snd_output_buffer_string(p->output, &tmp);
+ if (tmp)
+ mp_msg(ao->log, msglevel, "%s---\n%.*s---\n", msg, (int)tmp_s, tmp);
+ snd_output_flush(p->output);
+}
+
static int map_iec958_srate(int srate)
{
switch (srate) {
@@ -520,7 +538,7 @@ static char *append_params(void *ta_parent, const char *device, const char *p)
abort();
}
-static int try_open_device(struct ao *ao, const char *device)
+static int try_open_device(struct ao *ao, const char *device, int mode)
{
struct priv *p = ao->priv;
int err;
@@ -534,7 +552,7 @@ static int try_open_device(struct ao *ao, const char *device)
map_iec958_srate(ao->samplerate));
const char *ac3_device = append_params(tmp, device, params);
MP_VERBOSE(ao, "opening device '%s' => '%s'\n", device, ac3_device);
- err = snd_pcm_open(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, 0);
+ err = snd_pcm_open(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, mode);
if (err < 0) {
// Some spdif-capable devices do not accept the AES0 parameter,
// and instead require the iec958 pseudo-device (they will play
@@ -547,13 +565,13 @@ static int try_open_device(struct ao *ao, const char *device)
MP_VERBOSE(ao, "got error %d; opening iec fallback device '%s'\n",
err, ac3_device);
err = snd_pcm_open
- (&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, 0);
+ (&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, mode);
}
}
talloc_free(tmp);
} else {
MP_VERBOSE(ao, "opening device '%s'\n", device);
- err = snd_pcm_open(&p->alsa, device, SND_PCM_STREAM_PLAYBACK, 0);
+ err = snd_pcm_open(&p->alsa, device, SND_PCM_STREAM_PLAYBACK, mode);
}
return err;
@@ -563,6 +581,10 @@ static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
+ if (p->output)
+ snd_output_close(p->output);
+ p->output = NULL;
+
if (p->alsa) {
int err;
@@ -574,20 +596,35 @@ static void uninit(struct ao *ao)
alsa_error: ;
}
-static int init_device(struct ao *ao)
+#define INIT_DEVICE_ERR_GENERIC -1
+#define INIT_DEVICE_ERR_HWPARAMS -2
+static int init_device(struct ao *ao, int mode)
{
struct priv *p = ao->priv;
+ int ret = INIT_DEVICE_ERR_GENERIC;
+ char *tmp;
+ size_t tmp_s;
int err;
+ err = snd_output_buffer_open(&p->output);
+ CHECK_ALSA_ERROR("Unable to create output buffer");
+
const char *device = "default";
if (ao->device)
device = ao->device;
if (p->cfg_device && p->cfg_device[0])
device = p->cfg_device;
- err = try_open_device(ao, device);
+ err = try_open_device(ao, device, mode);
CHECK_ALSA_ERROR("Playback open error");
+ err = snd_pcm_dump(p->alsa, p->output);
+ CHECK_ALSA_WARN("Dump PCM error");
+ tmp_s = snd_output_buffer_string(p->output, &tmp);
+ if (tmp)
+ MP_DBG(ao, "PCM setup:\n---\n%.*s---\n", (int)tmp_s, tmp);
+ snd_output_flush(p->output);
+
err = snd_pcm_nonblock(p->alsa, 0);
CHECK_ALSA_WARN("Unable to set blocking mode");
@@ -597,12 +634,15 @@ static int init_device(struct ao *ao)
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
CHECK_ALSA_ERROR("Unable to get initial parameters");
+ dump_hw_params(ao, MSGL_DEBUG, "Start HW params:\n", alsa_hwparams);
+
// Some ALSA drivers have broken delay reporting, so disable the ALSA
// resampling plugin by default.
if (!p->cfg_resample) {
err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0);
CHECK_ALSA_ERROR("Unable to disable resampling");
}
+ dump_hw_params(ao, MSGL_DEBUG, "HW params after rate:\n", alsa_hwparams);
snd_pcm_access_t access = af_fmt_is_planar(ao->format)
? SND_PCM_ACCESS_RW_NONINTERLEAVED
@@ -614,6 +654,7 @@ static int init_device(struct ao *ao)
err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
}
CHECK_ALSA_ERROR("Unable to set access type");
+ dump_hw_params(ao, MSGL_DEBUG, "HW params after access:\n", alsa_hwparams);
bool found_format = false;
int try_formats[AF_FORMAT_COUNT];
@@ -637,6 +678,7 @@ static int init_device(struct ao *ao)
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
CHECK_ALSA_ERROR("Unable to set format");
+ dump_hw_params(ao, MSGL_DEBUG, "HW params after format:\n", alsa_hwparams);
struct mp_chmap dev_chmap = ao->channels;
if (af_fmt_is_spdif(ao->format) || p->cfg_ignore_chmap) {
@@ -656,6 +698,7 @@ static int init_device(struct ao *ao)
err = snd_pcm_hw_params_set_channels_near
(p->alsa, alsa_hwparams, &num_channels);
CHECK_ALSA_ERROR("Unable to set channels");
+ dump_hw_params(ao, MSGL_DEBUG, "HW params after channels:\n", alsa_hwparams);
if (num_channels > MP_NUM_CHANNELS) {
MP_FATAL(ao, "Too many audio channels (%d).\n", num_channels);
@@ -665,6 +708,7 @@ static int init_device(struct ao *ao)
err = snd_pcm_hw_params_set_rate_near
(p->alsa, alsa_hwparams, &ao->samplerate, NULL);
CHECK_ALSA_ERROR("Unable to set samplerate-2");
+ dump_hw_params(ao, MSGL_DEBUG, "HW params after rate-2:\n", alsa_hwparams);
snd_pcm_hw_params_t *hwparams_backup;
snd_pcm_hw_params_alloca(&hwparams_backup);
@@ -682,9 +726,14 @@ static int init_device(struct ao *ao)
if (err < 0)
snd_pcm_hw_params_copy(alsa_hwparams, hwparams_backup);
+ dump_hw_params(ao, MSGL_V, "Going to set final HW params:\n", alsa_hwparams);
+
/* finally install hardware parameters */
err = snd_pcm_hw_params(p->alsa, alsa_hwparams);
+ ret = INIT_DEVICE_ERR_HWPARAMS;
CHECK_ALSA_ERROR("Unable to set hw-parameters");
+ ret = INIT_DEVICE_ERR_GENERIC;
+ dump_hw_params(ao, MSGL_DEBUG, "Final HW params:\n", alsa_hwparams);
if (set_chmap(ao, &dev_chmap, num_channels) < 0)
goto alsa_error;
@@ -740,7 +789,7 @@ static int init_device(struct ao *ao)
alsa_error:
uninit(ao);
- return -1;
+ return ret;
}
static int init(struct ao *ao)
@@ -751,7 +800,18 @@ static int init(struct ao *ao)
MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version());
- int r = init_device(ao);
+ int mode = 0;
+ int r = init_device(ao, mode);
+ if (r == INIT_DEVICE_ERR_HWPARAMS) {
+ // With some drivers, ALSA appears to be unable to set valid hwparams,
+ // but they work if at least SND_PCM_NO_AUTO_FORMAT is set. Also, it
+ // appears you can set this flag only on opening a device, thus there
+ // is the need to retry opening the device.
+ MP_WARN(ao, "Attempting to work around even more ALSA bugs...\n");
+ mode |= SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT |
+ SND_PCM_NO_AUTO_RESAMPLE;
+ r = init_device(ao, mode);
+ }
// Sometimes, ALSA will advertise certain chmaps, but it's not possible to
// set them. This can happen with dmix: as of alsa 1.0.29, dmix can do
@@ -773,7 +833,7 @@ static int init(struct ao *ao)
MP_VERBOSE(ao, "Working around braindead dmix multichannel behavior.\n");
uninit(ao);
ao->channels = without_na;
- r = init_device(ao);
+ r = init_device(ao, mode);
}
}
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index 471ab6d928..b0a5dc0da1 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -133,11 +133,6 @@ static bool reinit_device(struct ao *ao) {
OSStatus err = ca_select_device(ao, ao->device, &p->device);
CHECK_CA_ERROR("failed to select device");
- char *uid;
- err = CA_GET_STR(p->device, kAudioDevicePropertyDeviceUID, &uid);
- CHECK_CA_ERROR("failed to get device UID");
- ao->detected_device = talloc_steal(ao, uid);
-
return true;
coreaudio_error:
diff --git a/audio/out/ao_coreaudio_utils.c b/audio/out/ao_coreaudio_utils.c
index 6b5d8a7195..8f9690fc22 100644
--- a/audio/out/ao_coreaudio_utils.c
+++ b/audio/out/ao_coreaudio_utils.c
@@ -323,6 +323,9 @@ bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream)
for (int i = 0; i < n_formats; i++) {
AudioStreamBasicDescription asbd = formats[i].mFormat;
+
+ ca_print_asbd(ao, "- ", &asbd);
+
if (ca_formatid_is_compressed(asbd.mFormatID)) {
talloc_free(formats);
return true;
@@ -334,30 +337,6 @@ coreaudio_error:
return false;
}
-bool ca_device_supports_compressed(struct ao *ao, AudioDeviceID device)
-{
- AudioStreamID *streams = NULL;
- size_t n_streams;
-
- /* Retrieve all the output streams. */
- OSStatus err =
- CA_GET_ARY_O(device, kAudioDevicePropertyStreams, &streams, &n_streams);
-
- CHECK_CA_ERROR("could not get number of streams.");
-
- for (int i = 0; i < n_streams; i++) {
- if (ca_stream_supports_compressed(ao, streams[i])) {
- talloc_free(streams);
- return true;
- }
- }
-
- talloc_free(streams);
-
-coreaudio_error:
- return false;
-}
-
OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid)
{
*pid = getpid();
diff --git a/audio/out/ao_coreaudio_utils.h b/audio/out/ao_coreaudio_utils.h
index 780c56864b..5cc7db53be 100644
--- a/audio/out/ao_coreaudio_utils.h
+++ b/audio/out/ao_coreaudio_utils.h
@@ -65,7 +65,6 @@ bool ca_asbd_is_better(AudioStreamBasicDescription *req,
int64_t ca_frames_to_us(struct ao *ao, uint32_t frames);
int64_t ca_get_latency(const AudioTimeStamp *ts);
-bool ca_device_supports_compressed(struct ao *ao, AudioDeviceID device);
bool ca_stream_supports_compressed(struct ao *ao, AudioStreamID stream);
OSStatus ca_lock_device(AudioDeviceID device, pid_t *pid);
OSStatus ca_unlock_device(AudioDeviceID device, pid_t *pid);
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index bf1728682a..572874d27c 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -42,6 +42,7 @@ struct priv {
uint8_t *buffer;
size_t buffer_size;
AVStream *stream;
+ AVCodecContext *codec;
int pcmhack;
int aframesize;
int aframecount;
@@ -98,15 +99,14 @@ static int init(struct ao *ao)
pthread_mutex_lock(&ao->encode_lavc_ctx->lock);
- ac->stream = encode_lavc_alloc_stream(ao->encode_lavc_ctx,
- AVMEDIA_TYPE_AUDIO);
-
- if (!ac->stream) {
- MP_ERR(ao, "could not get a new audio stream\n");
- goto fail;
+ if (encode_lavc_alloc_stream(ao->encode_lavc_ctx,
+ AVMEDIA_TYPE_AUDIO,
+ &ac->stream, &ac->codec) < 0) {
+ MP_ERR(ao, "could not get a new audio stream\n");
+ goto fail;
}
- codec = encode_lavc_get_codec(ao->encode_lavc_ctx, ac->stream);
+ codec = ao->encode_lavc_ctx->ac;
int samplerate = af_select_best_samplerate(ao->samplerate,
codec->supported_samplerates);
@@ -118,40 +118,40 @@ static int init(struct ao *ao)
// Using codec->time_bvase is deprecated, but needed for older lavf.
ac->stream->time_base.num = 1;
ac->stream->time_base.den = ao->samplerate;
- ac->stream->codec->time_base.num = 1;
- ac->stream->codec->time_base.den = ao->samplerate;
+ ac->codec->time_base.num = 1;
+ ac->codec->time_base.den = ao->samplerate;
- ac->stream->codec->sample_rate = ao->samplerate;
+ ac->codec->sample_rate = ao->samplerate;
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_any(&sel);
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
goto fail;
mp_chmap_reorder_to_lavc(&ao->channels);
- ac->stream->codec->channels = ao->channels.num;
- ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels);
+ ac->codec->channels = ao->channels.num;
+ ac->codec->channel_layout = mp_chmap_to_lavc(&ao->channels);
- ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
+ ac->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
select_format(ao, codec);
ac->sample_size = af_fmt_to_bytes(ao->format);
- ac->stream->codec->sample_fmt = af_to_avformat(ao->format);
- ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
+ ac->codec->sample_fmt = af_to_avformat(ao->format);
+ ac->codec->bits_per_raw_sample = ac->sample_size * 8;
- if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
+ if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->codec) < 0)
goto fail;
ac->pcmhack = 0;
- if (ac->stream->codec->frame_size <= 1)
- ac->pcmhack = av_get_bits_per_sample(ac->stream->codec->codec_id) / 8;
+ if (ac->codec->frame_size <= 1)
+ ac->pcmhack = av_get_bits_per_sample(ac->codec->codec_id) / 8;
if (ac->pcmhack) {
ac->aframesize = 16384; // "enough"
ac->buffer_size =
ac->aframesize * ac->pcmhack * ao->channels.num * 2 + 200;
} else {
- ac->aframesize = ac->stream->codec->frame_size;
+ ac->aframesize = ac->codec->frame_size;
ac->buffer_size =
ac->aframesize * ac->sample_size * ao->channels.num * 2 + 200;
}
@@ -203,7 +203,7 @@ static void uninit(struct ao *ao)
double outpts = ac->expected_next_pts;
if (!ectx->options->rawts && ectx->options->copyts)
outpts += ectx->discontinuity_pts_offset;
- outpts += encode_lavc_getoffset(ectx, ac->stream);
+ outpts += encode_lavc_getoffset(ectx, ac->codec);
while (encode(ao, outpts, NULL) > 0) ;
}
@@ -252,25 +252,25 @@ static int encode(struct ao *ao, double apts, void **data)
if (ectx->options->rawts || ectx->options->copyts) {
// real audio pts
- frame->pts = floor(apts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
+ frame->pts = floor(apts * ac->codec->time_base.den / ac->codec->time_base.num + 0.5);
} else {
// audio playback time
- frame->pts = floor(realapts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
+ frame->pts = floor(realapts * ac->codec->time_base.den / ac->codec->time_base.num + 0.5);
}
- int64_t frame_pts = av_rescale_q(frame->pts, ac->stream->codec->time_base, ac->worst_time_base);
+ int64_t frame_pts = av_rescale_q(frame->pts, ac->codec->time_base, ac->worst_time_base);
if (ac->lastpts != AV_NOPTS_VALUE && frame_pts <= ac->lastpts) {
// this indicates broken video
// (video pts failing to increase fast enough to match audio)
MP_WARN(ao, "audio frame pts went backwards (%d <- %d), autofixed\n",
(int)frame->pts, (int)ac->lastpts);
frame_pts = ac->lastpts + 1;
- frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, ac->stream->codec->time_base);
+ frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, ac->codec->time_base);
}
ac->lastpts = frame_pts;
- frame->quality = ac->stream->codec->global_quality;
- status = avcodec_encode_audio2(ac->stream->codec, &packet, frame, &gotpacket);
+ frame->quality = ac->codec->global_quality;
+ status = avcodec_encode_audio2(ac->codec, &packet, frame, &gotpacket);
if (!status) {
if (ac->savepts == AV_NOPTS_VALUE)
@@ -281,7 +281,7 @@ static int encode(struct ao *ao, double apts, void **data)
}
else
{
- status = avcodec_encode_audio2(ac->stream->codec, &packet, NULL, &gotpacket);
+ status = avcodec_encode_audio2(ac->codec, &packet, NULL, &gotpacket);
}
if(status) {
@@ -295,7 +295,7 @@ static int encode(struct ao *ao, double apts, void **data)
MP_DBG(ao, "got pts %f (playback time: %f); out size: %d\n",
apts, realapts, packet.size);
- encode_lavc_write_stats(ao->encode_lavc_ctx, ac->stream);
+ encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec);
packet.stream_index = ac->stream->index;
@@ -307,20 +307,20 @@ static int encode(struct ao *ao, double apts, void **data)
}
if (packet.pts != AV_NOPTS_VALUE)
- packet.pts = av_rescale_q(packet.pts, ac->stream->codec->time_base,
+ packet.pts = av_rescale_q(packet.pts, ac->codec->time_base,
ac->stream->time_base);
if (packet.dts != AV_NOPTS_VALUE)
- packet.dts = av_rescale_q(packet.dts, ac->stream->codec->time_base,
+ packet.dts = av_rescale_q(packet.dts, ac->codec->time_base,
ac->stream->time_base);
if(packet.duration > 0)
- packet.duration = av_rescale_q(packet.duration, ac->stream->codec->time_base,
+ packet.duration = av_rescale_q(packet.duration, ac->codec->time_base,
ac->stream->time_base);
ac->savepts = AV_NOPTS_VALUE;
- if (encode_lavc_write_frame(ao->encode_lavc_ctx, &packet) < 0) {
+ if (encode_lavc_write_frame(ao->encode_lavc_ctx, ac->stream, &packet) < 0) {
MP_ERR(ao, "error writing at %f %f/%f\n",
realapts, (double) ac->stream->time_base.num,
(double) ac->stream->time_base.den);
@@ -377,22 +377,22 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
if (ac->worst_time_base.den == 0) {
- //if (ac->stream->codec->time_base.num / ac->stream->codec->time_base.den >= ac->stream->time_base.num / ac->stream->time_base.den)
- if (ac->stream->codec->time_base.num * (double) ac->stream->time_base.den >=
- ac->stream->time_base.num * (double) ac->stream->codec->time_base.den) {
+ //if (ac->codec->time_base.num / ac->codec->time_base.den >= ac->stream->time_base.num / ac->stream->time_base.den)
+ if (ac->codec->time_base.num * (double) ac->stream->time_base.den >=
+ ac->stream->time_base.num * (double) ac->codec->time_base.den) {
MP_VERBOSE(ao, "NOTE: using codec time base (%d/%d) for pts "
"adjustment; the stream base (%d/%d) is not worse.\n",
- (int)ac->stream->codec->time_base.num,
- (int)ac->stream->codec->time_base.den,
+ (int)ac->codec->time_base.num,
+ (int)ac->codec->time_base.den,
(int)ac->stream->time_base.num,
(int)ac->stream->time_base.den);
- ac->worst_time_base = ac->stream->codec->time_base;
+ ac->worst_time_base = ac->codec->time_base;
ac->worst_time_base_is_stream = 0;
} else {
MP_WARN(ao, "NOTE: not using codec time base (%d/%d) for pts "
"adjustment; the stream base (%d/%d) is worse.\n",
- (int)ac->stream->codec->time_base.num,
- (int)ac->stream->codec->time_base.den,
+ (int)ac->codec->time_base.num,
+ (int)ac->codec->time_base.den,
(int)ac->stream->time_base.num,
(int)ac->stream->time_base.den);
ac->worst_time_base = ac->stream->time_base;
@@ -437,7 +437,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
// Shift pts by the pts offset first.
- outpts += encode_lavc_getoffset(ectx, ac->stream);
+ outpts += encode_lavc_getoffset(ectx, ac->codec);
while (samples - bufpos >= ac->aframesize) {
void *start[MP_NUM_CHANNELS] = {0};
diff --git a/audio/out/ao_opensles.c b/audio/out/ao_opensles.c
index 0e80829557..2ce7b01552 100644
--- a/audio/out/ao_opensles.c
+++ b/audio/out/ao_opensles.c
@@ -46,7 +46,6 @@ struct priv {
static const int fmtmap[][2] = {
{ AF_FORMAT_U8, SL_PCMSAMPLEFORMAT_FIXED_8 },
{ AF_FORMAT_S16, SL_PCMSAMPLEFORMAT_FIXED_16 },
- { AF_FORMAT_S32, SL_PCMSAMPLEFORMAT_FIXED_32 },
{ 0 }
};
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c
index 393c9db780..3216d673e1 100644
--- a/audio/out/ao_oss.c
+++ b/audio/out/ao_oss.c
@@ -462,7 +462,7 @@ static int init(struct ao *ao)
p->buffersize = 0;
memset(data, 0, p->outburst);
while (p->buffersize < 0x40000 && device_writable(ao) > 0) {
- write(p->audio_fd, data, p->outburst);
+ (void)write(p->audio_fd, data, p->outburst);
p->buffersize += p->outburst;
}
free(data);
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index eecfded9e1..ae6bd3d9dc 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -270,7 +270,7 @@ static void uninit(struct ao *ao)
static int init(struct ao *ao)
{
MP_DBG(ao, "Init wasapi\n");
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ CoInitializeEx(NULL, COINIT_MULTITHREADED);
struct wasapi_state *state = ao->priv;
state->log = ao->log;
@@ -469,7 +469,7 @@ static int hotplug_init(struct ao *ao)
MP_DBG(ao, "Hotplug init\n");
struct wasapi_state *state = ao->priv;
state->log = ao->log;
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ CoInitializeEx(NULL, COINIT_MULTITHREADED);
HRESULT hr = wasapi_change_init(ao, true);
EXIT_ON_ERROR(hr);
diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c
index 661e957bcc..e3ca4e4936 100644
--- a/audio/out/ao_wasapi_changenotify.c
+++ b/audio/out/ao_wasapi_changenotify.c
@@ -17,6 +17,7 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <initguid.h>
#include <wchar.h>
#include "ao_wasapi.h"
diff --git a/audio/out/push.c b/audio/out/push.c
index 4fa2bc53d5..ac87c62a96 100644
--- a/audio/out/push.c
+++ b/audio/out/push.c
@@ -142,6 +142,7 @@ static void resume(struct ao *ao)
static void drain(struct ao *ao)
{
struct ao_push_state *p = ao->api_priv;
+ double maxbuffer = ao->buffer / (double)ao->samplerate + 1;
MP_VERBOSE(ao, "draining...\n");
@@ -151,14 +152,23 @@ static void drain(struct ao *ao)
p->final_chunk = true;
wakeup_playthread(ao);
- while (p->still_playing && mp_audio_buffer_samples(p->buffer) > 0)
- pthread_cond_wait(&p->wakeup, &p->lock);
+
+ // Wait until everything is done. Since the audio API (especially ALSA)
+ // can't be trusted to do this right, and we're hard-blocking here, apply
+ // an upper bound timeout.
+ struct timespec until = mp_rel_time_to_timespec(maxbuffer);
+ while (p->still_playing && mp_audio_buffer_samples(p->buffer) > 0) {
+ if (pthread_cond_timedwait(&p->wakeup, &p->lock, &until)) {
+ MP_WARN(ao, "Draining is taking too long, aborting.\n");
+ goto done;
+ }
+ }
if (ao->driver->drain) {
ao->driver->drain(ao);
} else {
double time = unlocked_get_delay(ao);
- mp_sleep_us(MPMIN(time, ao->buffer / (double)ao->samplerate + 1) * 1e6);
+ mp_sleep_us(MPMIN(time, maxbuffer) * 1e6);
}
done:
@@ -292,11 +302,12 @@ static void ao_play_data(struct ao *ao)
// If we just filled the AO completely (r == space), don't refill for a
// while. Prevents wakeup feedback with byte-granular AOs.
int needed = unlocked_get_space(ao);
- bool more = needed >= (r == space ? ao->device_buffer / 4 : 1) && !stuck;
+ bool more = needed >= (r == space ? ao->device_buffer / 4 : 1) && !stuck &&
+ !(flags & AOPLAY_FINAL_CHUNK);
if (more)
mp_input_wakeup(ao->input_ctx); // request more data
- MP_TRACE(ao, "in=%d flags=%d space=%d r=%d wa=%d needed=%d more=%d\n",
- max, flags, space, r, p->wait_on_ao, needed, more);
+ MP_TRACE(ao, "in=%d flags=%d space=%d r=%d wa/pl=%d/%d needed=%d more=%d\n",
+ max, flags, space, r, p->wait_on_ao, p->still_playing, needed, more);
}
static void *playthread(void *arg)
@@ -489,7 +500,7 @@ int ao_wait_poll(struct ao *ao, struct pollfd *fds, int num_fds,
// flush the wakeup pipe contents - might "drown" some wakeups, but
// that's ok for our use-case
char buf[100];
- read(p->wakeup_pipe[0], buf, sizeof(buf));
+ (void)read(p->wakeup_pipe[0], buf, sizeof(buf));
}
return (r >= 0 || r == -EINTR) ? wakeup : -1;
}
@@ -499,7 +510,7 @@ void ao_wakeup_poll(struct ao *ao)
assert(ao->api == &ao_api_push);
struct ao_push_state *p = ao->api_priv;
- write(p->wakeup_pipe[1], &(char){0}, 1);
+ (void)write(p->wakeup_pipe[1], &(char){0}, 1);
}
#endif