summaryrefslogtreecommitdiffstats
path: root/audio/out/pull.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-07-07 17:35:09 +0200
committerwm4 <wm4@nowhere>2017-07-07 17:54:05 +0200
commit90dd2298713d3414bad39b6e8648490cd9f52603 (patch)
treee1da2c0b5d04a2e2fd6bce752131dd3089e603f9 /audio/out/pull.c
parent7c1db05cbb386522017942a173cc9552637a660e (diff)
downloadmpv-90dd2298713d3414bad39b6e8648490cd9f52603.tar.bz2
mpv-90dd2298713d3414bad39b6e8648490cd9f52603.tar.xz
audio/out: add helper code to do 24 bit conversion in AO
I plan to remove the S24 sample formats in mpv. It seems like we should still support this _somehow_ in AOs though. So the idea is to convert the data to more obscure representations (that would not be useful for filtering etc. anyway) within the AO. This commit adds helper to enable this. ao_convert_fmt is meant to provide mechanisms for this, rather than a generic audio format description (as the latter leads only to overly generic misery). The conversion also supports only cases which we think will be needed at all. The main advantage of this approach is that we get S24 out of sight, and that we could support other crazy formats (like S20). The main disadvantage is that usually S32 will be selected (if both S32 and S24 are available), and there's no user control to force S24. That doesn't really matter though, and at worst makes testing harder or will lead to unpleasant arguments with audiophiles (they'd be wrong anyway). ao_convert_fmt.pad_lsb is ignored, although if we ever find a case in which playing S32 with data in the LSBs breaks when playing it as padded 24 bit format. (For example, WAVEFORMATEXTENSIBLE recommends setting the unused bits to 0 if wValidBitsPerSample implies LSB padding.)
Diffstat (limited to 'audio/out/pull.c')
-rw-r--r--audio/out/pull.c45
1 files changed, 45 insertions, 0 deletions
diff --git a/audio/out/pull.c b/audio/out/pull.c
index 6da3825965..fc8dc893c3 100644
--- a/audio/out/pull.c
+++ b/audio/out/pull.c
@@ -64,6 +64,8 @@ struct ao_pull_state {
// Device delay of the last written sample, in realtime.
atomic_llong end_time_us;
+
+ char *convert_buffer;
};
static void set_state(struct ao *ao, int new_state)
@@ -180,6 +182,45 @@ end:
return bytes / ao->sstride;
}
+// Same as ao_read_data(), but read pre-converted data according to *fmt.
+// fmt->src_fmt and fmt->channels must be the same as the AO parameters.
+int ao_read_data_converted(struct ao *ao, struct ao_convert_fmt *fmt,
+ void **data, int samples, int64_t out_time_us)
+{
+ assert(ao->api == &ao_api_pull);
+
+ struct ao_pull_state *p = ao->api_priv;
+ void *ndata[MP_NUM_CHANNELS];
+
+ if (!ao_need_conversion(fmt))
+ return ao_read_data(ao, data, samples, out_time_us);
+
+ assert(ao->format == fmt->src_fmt);
+ assert(ao->channels.num == fmt->channels);
+
+ bool planar = af_fmt_is_planar(fmt->src_fmt);
+ int planes = planar ? fmt->channels : 1;
+ int plane_size = af_fmt_to_bytes(fmt->src_fmt) * samples *
+ (planar ? 1: fmt->channels);
+
+ int needed = plane_size * planes * fmt->channels * samples;
+ if (needed > talloc_get_size(p->convert_buffer) || !p->convert_buffer) {
+ talloc_free(p->convert_buffer);
+ p->convert_buffer = talloc_size(NULL, needed);
+ }
+
+ for (int n = 0; n < planes; n++)
+ ndata[n] = p->convert_buffer + n * plane_size;
+
+ int res = ao_read_data(ao, ndata, samples, out_time_us);
+
+ ao_convert_inplace(fmt, ndata, samples);
+ for (int n = 0; n < planes; n++)
+ memcpy(data[n], ndata[n], plane_size);
+
+ return res;
+}
+
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
if (ao->driver->control)
@@ -256,7 +297,11 @@ static void drain(struct ao *ao)
static void uninit(struct ao *ao)
{
+ struct ao_pull_state *p = ao->api_priv;
+
ao->driver->uninit(ao);
+
+ talloc_free(p->convert_buffer);
}
static int init(struct ao *ao)