mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 18:05:37 +00:00
audioringbuffer: Add support for reordering of channels
This commit is contained in:
parent
b23ff1b515
commit
225238a913
2 changed files with 164 additions and 30 deletions
|
@ -539,11 +539,16 @@ gst_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
|
||||||
goto was_acquired;
|
goto was_acquired;
|
||||||
|
|
||||||
buf->acquired = TRUE;
|
buf->acquired = TRUE;
|
||||||
|
buf->need_reorder = FALSE;
|
||||||
|
|
||||||
rclass = GST_AUDIO_RING_BUFFER_GET_CLASS (buf);
|
rclass = GST_AUDIO_RING_BUFFER_GET_CLASS (buf);
|
||||||
if (G_LIKELY (rclass->acquire))
|
if (G_LIKELY (rclass->acquire))
|
||||||
res = rclass->acquire (buf, spec);
|
res = rclass->acquire (buf, spec);
|
||||||
|
|
||||||
|
/* Only reorder for raw audio */
|
||||||
|
buf->need_reorder = (buf->need_reorder
|
||||||
|
&& buf->spec.type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW);
|
||||||
|
|
||||||
if (G_UNLIKELY (!res))
|
if (G_UNLIKELY (!res))
|
||||||
goto acquire_failed;
|
goto acquire_failed;
|
||||||
|
|
||||||
|
@ -1276,13 +1281,34 @@ no_start:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FWD_SAMPLES(s,se,d,de) \
|
|
||||||
|
|
||||||
|
#define REORDER_SAMPLE(d, s, l) \
|
||||||
|
G_STMT_START { \
|
||||||
|
gint i; \
|
||||||
|
for (i = 0; i < channels; i++) { \
|
||||||
|
memcpy (d + reorder_map[i] * bps, s + i * bps, bps); \
|
||||||
|
} \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#define REORDER_SAMPLES(d, s, len) \
|
||||||
|
G_STMT_START { \
|
||||||
|
gint i, len_ = len / bpf; \
|
||||||
|
guint8 *d_ = d, *s_ = s; \
|
||||||
|
for (i = 0; i < len_; i++) { \
|
||||||
|
REORDER_SAMPLE(d_, s_, bpf); \
|
||||||
|
d_ += bpf; \
|
||||||
|
s_ += bpf; \
|
||||||
|
} \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#define FWD_SAMPLES(s,se,d,de,F) \
|
||||||
G_STMT_START { \
|
G_STMT_START { \
|
||||||
/* no rate conversion */ \
|
/* no rate conversion */ \
|
||||||
guint towrite = MIN (se + bpf - s, de - d); \
|
guint towrite = MIN (se + bpf - s, de - d); \
|
||||||
/* simple copy */ \
|
/* simple copy */ \
|
||||||
if (!skip) \
|
if (!skip) \
|
||||||
memcpy (d, s, towrite); \
|
F (d, s, towrite); \
|
||||||
in_samples -= towrite / bpf; \
|
in_samples -= towrite / bpf; \
|
||||||
out_samples -= towrite / bpf; \
|
out_samples -= towrite / bpf; \
|
||||||
s += towrite; \
|
s += towrite; \
|
||||||
|
@ -1290,12 +1316,12 @@ G_STMT_START { \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
/* in_samples >= out_samples, rate > 1.0 */
|
/* in_samples >= out_samples, rate > 1.0 */
|
||||||
#define FWD_UP_SAMPLES(s,se,d,de) \
|
#define FWD_UP_SAMPLES(s,se,d,de,F) \
|
||||||
G_STMT_START { \
|
G_STMT_START { \
|
||||||
guint8 *sb = s, *db = d; \
|
guint8 *sb = s, *db = d; \
|
||||||
while (s <= se && d < de) { \
|
while (s <= se && d < de) { \
|
||||||
if (!skip) \
|
if (!skip) \
|
||||||
memcpy (d, s, bpf); \
|
F (d, s, bpf); \
|
||||||
s += bpf; \
|
s += bpf; \
|
||||||
*accum += outr; \
|
*accum += outr; \
|
||||||
if ((*accum << 1) >= inr) { \
|
if ((*accum << 1) >= inr) { \
|
||||||
|
@ -1309,12 +1335,12 @@ G_STMT_START { \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
/* out_samples > in_samples, for rates smaller than 1.0 */
|
/* out_samples > in_samples, for rates smaller than 1.0 */
|
||||||
#define FWD_DOWN_SAMPLES(s,se,d,de) \
|
#define FWD_DOWN_SAMPLES(s,se,d,de,F) \
|
||||||
G_STMT_START { \
|
G_STMT_START { \
|
||||||
guint8 *sb = s, *db = d; \
|
guint8 *sb = s, *db = d; \
|
||||||
while (s <= se && d < de) { \
|
while (s <= se && d < de) { \
|
||||||
if (!skip) \
|
if (!skip) \
|
||||||
memcpy (d, s, bpf); \
|
F (d, s, bpf); \
|
||||||
d += bpf; \
|
d += bpf; \
|
||||||
*accum += inr; \
|
*accum += inr; \
|
||||||
if ((*accum << 1) >= outr) { \
|
if ((*accum << 1) >= outr) { \
|
||||||
|
@ -1327,12 +1353,12 @@ G_STMT_START { \
|
||||||
GST_DEBUG ("fwd_down end %d/%d",*accum,*toprocess); \
|
GST_DEBUG ("fwd_down end %d/%d",*accum,*toprocess); \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
#define REV_UP_SAMPLES(s,se,d,de) \
|
#define REV_UP_SAMPLES(s,se,d,de,F) \
|
||||||
G_STMT_START { \
|
G_STMT_START { \
|
||||||
guint8 *sb = se, *db = d; \
|
guint8 *sb = se, *db = d; \
|
||||||
while (s <= se && d < de) { \
|
while (s <= se && d < de) { \
|
||||||
if (!skip) \
|
if (!skip) \
|
||||||
memcpy (d, se, bpf); \
|
F (d, se, bpf); \
|
||||||
se -= bpf; \
|
se -= bpf; \
|
||||||
*accum += outr; \
|
*accum += outr; \
|
||||||
while (d < de && (*accum << 1) >= inr) { \
|
while (d < de && (*accum << 1) >= inr) { \
|
||||||
|
@ -1345,12 +1371,12 @@ G_STMT_START { \
|
||||||
GST_DEBUG ("rev_up end %d/%d",*accum,*toprocess); \
|
GST_DEBUG ("rev_up end %d/%d",*accum,*toprocess); \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
#define REV_DOWN_SAMPLES(s,se,d,de) \
|
#define REV_DOWN_SAMPLES(s,se,d,de,F) \
|
||||||
G_STMT_START { \
|
G_STMT_START { \
|
||||||
guint8 *sb = se, *db = d; \
|
guint8 *sb = se, *db = d; \
|
||||||
while (s <= se && d < de) { \
|
while (s <= se && d < de) { \
|
||||||
if (!skip) \
|
if (!skip) \
|
||||||
memcpy (d, se, bpf); \
|
F (d, se, bpf); \
|
||||||
d += bpf; \
|
d += bpf; \
|
||||||
*accum += inr; \
|
*accum += inr; \
|
||||||
while (s <= se && (*accum << 1) >= outr) { \
|
while (s <= se && (*accum << 1) >= outr) { \
|
||||||
|
@ -1368,20 +1394,25 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample,
|
||||||
guint8 * data, gint in_samples, gint out_samples, gint * accum)
|
guint8 * data, gint in_samples, gint out_samples, gint * accum)
|
||||||
{
|
{
|
||||||
gint segdone;
|
gint segdone;
|
||||||
gint segsize, segtotal, bpf, sps;
|
gint segsize, segtotal, channels, bps, bpf, sps;
|
||||||
guint8 *dest, *data_end;
|
guint8 *dest, *data_end;
|
||||||
gint writeseg, sampleoff;
|
gint writeseg, sampleoff;
|
||||||
gint *toprocess;
|
gint *toprocess;
|
||||||
gint inr, outr;
|
gint inr, outr;
|
||||||
gboolean reverse;
|
gboolean reverse;
|
||||||
|
gboolean need_reorder;
|
||||||
|
|
||||||
g_return_val_if_fail (buf->memory != NULL, -1);
|
g_return_val_if_fail (buf->memory != NULL, -1);
|
||||||
g_return_val_if_fail (data != NULL, -1);
|
g_return_val_if_fail (data != NULL, -1);
|
||||||
|
|
||||||
|
need_reorder = buf->need_reorder;
|
||||||
|
|
||||||
|
channels = buf->spec.info.channels;
|
||||||
dest = buf->memory;
|
dest = buf->memory;
|
||||||
segsize = buf->spec.segsize;
|
segsize = buf->spec.segsize;
|
||||||
segtotal = buf->spec.segtotal;
|
segtotal = buf->spec.segtotal;
|
||||||
bpf = buf->spec.info.bpf;
|
bpf = buf->spec.info.bpf;
|
||||||
|
bps = bpf / channels;
|
||||||
sps = buf->samples_per_seg;
|
sps = buf->samples_per_seg;
|
||||||
|
|
||||||
reverse = out_samples < 0;
|
reverse = out_samples < 0;
|
||||||
|
@ -1455,23 +1486,46 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample,
|
||||||
GST_DEBUG_OBJECT (buf, "write @%p seg %d, sps %d, off %d, avail %d",
|
GST_DEBUG_OBJECT (buf, "write @%p seg %d, sps %d, off %d, avail %d",
|
||||||
dest + ws * segsize, ws, sps, sampleoff, avail);
|
dest + ws * segsize, ws, sps, sampleoff, avail);
|
||||||
|
|
||||||
if (G_LIKELY (inr == outr && !reverse)) {
|
if (need_reorder) {
|
||||||
/* no rate conversion, simply copy samples */
|
gint *reorder_map = buf->channel_reorder_map;
|
||||||
FWD_SAMPLES (data, data_end, d, d_end);
|
|
||||||
} else if (!reverse) {
|
if (G_LIKELY (inr == outr && !reverse)) {
|
||||||
if (inr >= outr)
|
/* no rate conversion, simply copy samples */
|
||||||
/* forward speed up */
|
FWD_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLES);
|
||||||
FWD_UP_SAMPLES (data, data_end, d, d_end);
|
} else if (!reverse) {
|
||||||
else
|
if (inr >= outr)
|
||||||
/* forward slow down */
|
/* forward speed up */
|
||||||
FWD_DOWN_SAMPLES (data, data_end, d, d_end);
|
FWD_UP_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
|
||||||
|
else
|
||||||
|
/* forward slow down */
|
||||||
|
FWD_DOWN_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
|
||||||
|
} else {
|
||||||
|
if (inr >= outr)
|
||||||
|
/* reverse speed up */
|
||||||
|
REV_UP_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
|
||||||
|
else
|
||||||
|
/* reverse slow down */
|
||||||
|
REV_DOWN_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (inr >= outr)
|
if (G_LIKELY (inr == outr && !reverse)) {
|
||||||
/* reverse speed up */
|
/* no rate conversion, simply copy samples */
|
||||||
REV_UP_SAMPLES (data, data_end, d, d_end);
|
FWD_SAMPLES (data, data_end, d, d_end, memcpy);
|
||||||
else
|
} else if (!reverse) {
|
||||||
/* reverse slow down */
|
if (inr >= outr)
|
||||||
REV_DOWN_SAMPLES (data, data_end, d, d_end);
|
/* forward speed up */
|
||||||
|
FWD_UP_SAMPLES (data, data_end, d, d_end, memcpy);
|
||||||
|
else
|
||||||
|
/* forward slow down */
|
||||||
|
FWD_DOWN_SAMPLES (data, data_end, d, d_end, memcpy);
|
||||||
|
} else {
|
||||||
|
if (inr >= outr)
|
||||||
|
/* reverse speed up */
|
||||||
|
REV_UP_SAMPLES (data, data_end, d, d_end, memcpy);
|
||||||
|
else
|
||||||
|
/* reverse slow down */
|
||||||
|
REV_DOWN_SAMPLES (data, data_end, d, d_end, memcpy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for the next iteration we write to the next segment at the beginning. */
|
/* for the next iteration we write to the next segment at the beginning. */
|
||||||
|
@ -1572,18 +1626,22 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
|
||||||
guint8 * data, guint len)
|
guint8 * data, guint len)
|
||||||
{
|
{
|
||||||
gint segdone;
|
gint segdone;
|
||||||
gint segsize, segtotal, bpf, sps;
|
gint segsize, segtotal, channels, bps, bpf, sps;
|
||||||
guint8 *dest;
|
guint8 *dest;
|
||||||
guint to_read;
|
guint to_read;
|
||||||
|
gboolean need_reorder;
|
||||||
|
|
||||||
g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), -1);
|
g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), -1);
|
||||||
g_return_val_if_fail (buf->memory != NULL, -1);
|
g_return_val_if_fail (buf->memory != NULL, -1);
|
||||||
g_return_val_if_fail (data != NULL, -1);
|
g_return_val_if_fail (data != NULL, -1);
|
||||||
|
|
||||||
|
need_reorder = buf->need_reorder;
|
||||||
dest = buf->memory;
|
dest = buf->memory;
|
||||||
segsize = buf->spec.segsize;
|
segsize = buf->spec.segsize;
|
||||||
segtotal = buf->spec.segtotal;
|
segtotal = buf->spec.segtotal;
|
||||||
|
channels = buf->spec.info.channels;
|
||||||
bpf = buf->spec.info.bpf;
|
bpf = buf->spec.info.bpf;
|
||||||
|
bps = bpf / channels;
|
||||||
sps = buf->samples_per_seg;
|
sps = buf->samples_per_seg;
|
||||||
|
|
||||||
to_read = len;
|
to_read = len;
|
||||||
|
@ -1639,8 +1697,22 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
|
||||||
GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, sampleslen %d",
|
GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, sampleslen %d",
|
||||||
dest + readseg * segsize, readseg, sampleoff, sampleslen);
|
dest + readseg * segsize, readseg, sampleoff, sampleslen);
|
||||||
|
|
||||||
memcpy (data, dest + (readseg * segsize) + (sampleoff * bpf),
|
if (need_reorder) {
|
||||||
(sampleslen * bpf));
|
guint8 *ptr = dest + (readseg * segsize) + (sampleoff * bpf);
|
||||||
|
gint i, j;
|
||||||
|
gint *reorder_map = buf->channel_reorder_map;
|
||||||
|
|
||||||
|
/* Reorder from device order to GStreamer order */
|
||||||
|
for (i = 0; i < sampleslen; i++) {
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
memcpy (data + reorder_map[j] * bps, ptr + j * bps, bps);
|
||||||
|
}
|
||||||
|
ptr += bpf;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy (data, dest + (readseg * segsize) + (sampleoff * bpf),
|
||||||
|
(sampleslen * bpf));
|
||||||
|
}
|
||||||
|
|
||||||
next:
|
next:
|
||||||
to_read -= sampleslen;
|
to_read -= sampleslen;
|
||||||
|
@ -1796,3 +1868,57 @@ gst_audio_ring_buffer_may_start (GstAudioRingBuffer * buf, gboolean allowed)
|
||||||
GST_LOG_OBJECT (buf, "may start: %d", allowed);
|
GST_LOG_OBJECT (buf, "may start: %d", allowed);
|
||||||
g_atomic_int_set (&buf->may_start, allowed);
|
g_atomic_int_set (&buf->may_start, allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_audio_ring_buffer_set_channel_positions:
|
||||||
|
* @buf: the #GstAudioRingBuffer
|
||||||
|
* @position: the device channel positions
|
||||||
|
*
|
||||||
|
* Tell the ringbuffer about the device's channel positions. This must
|
||||||
|
* be called in when the ringbuffer is acquired.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_audio_ring_buffer_set_channel_positions (GstAudioRingBuffer * buf,
|
||||||
|
const GstAudioChannelPosition * position)
|
||||||
|
{
|
||||||
|
const GstAudioChannelPosition *to;
|
||||||
|
gint channels;
|
||||||
|
gint i, j;
|
||||||
|
|
||||||
|
g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf));
|
||||||
|
g_return_if_fail (buf->acquired);
|
||||||
|
|
||||||
|
channels = buf->spec.info.channels;
|
||||||
|
to = buf->spec.info.position;
|
||||||
|
|
||||||
|
buf->need_reorder = FALSE;
|
||||||
|
if (memcmp (position, to, channels * sizeof (to[0])) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Build reorder map and check compatibility */
|
||||||
|
for (i = 0; i < channels; i++) {
|
||||||
|
g_return_if_fail (position[i] == GST_AUDIO_CHANNEL_POSITION_NONE
|
||||||
|
|| to[i] == GST_AUDIO_CHANNEL_POSITION_NONE);
|
||||||
|
g_return_if_fail (position[i] == GST_AUDIO_CHANNEL_POSITION_INVALID
|
||||||
|
|| to[i] == GST_AUDIO_CHANNEL_POSITION_INVALID);
|
||||||
|
g_return_if_fail (position[i] == GST_AUDIO_CHANNEL_POSITION_MONO
|
||||||
|
|| to[i] == GST_AUDIO_CHANNEL_POSITION_MONO);
|
||||||
|
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
if (position[i] == to[j]) {
|
||||||
|
buf->channel_reorder_map[i] = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not all channels present in both */
|
||||||
|
g_return_if_fail (j == channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < channels; i++) {
|
||||||
|
if (buf->channel_reorder_map[i] != i) {
|
||||||
|
buf->need_reorder = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -123,6 +123,7 @@ struct _GstAudioRingBufferSpec
|
||||||
GstAudioRingBufferFormatType type;
|
GstAudioRingBufferFormatType type;
|
||||||
GstAudioInfo info;
|
GstAudioInfo info;
|
||||||
|
|
||||||
|
|
||||||
guint64 latency_time; /* the required/actual latency time, this is the
|
guint64 latency_time; /* the required/actual latency time, this is the
|
||||||
* actual the size of one segment and the
|
* actual the size of one segment and the
|
||||||
* minimum possible latency we can achieve. */
|
* minimum possible latency we can achieve. */
|
||||||
|
@ -189,6 +190,10 @@ struct _GstAudioRingBuffer {
|
||||||
GstAudioRingBufferCallback callback;
|
GstAudioRingBufferCallback callback;
|
||||||
gpointer cb_data;
|
gpointer cb_data;
|
||||||
|
|
||||||
|
gboolean need_reorder;
|
||||||
|
/* gst[channel_reorder_map[i]] = device[i] */
|
||||||
|
gint channel_reorder_map[64];
|
||||||
|
|
||||||
gboolean flushing;
|
gboolean flushing;
|
||||||
/* ATOMIC */
|
/* ATOMIC */
|
||||||
gint may_start;
|
gint may_start;
|
||||||
|
@ -273,6 +278,9 @@ gboolean gst_audio_ring_buffer_release (GstAudioRingBuffer *buf);
|
||||||
|
|
||||||
gboolean gst_audio_ring_buffer_is_acquired (GstAudioRingBuffer *buf);
|
gboolean gst_audio_ring_buffer_is_acquired (GstAudioRingBuffer *buf);
|
||||||
|
|
||||||
|
/* set the device channel positions */
|
||||||
|
void gst_audio_ring_buffer_set_channel_positions (GstAudioRingBuffer *buf, const GstAudioChannelPosition *position);
|
||||||
|
|
||||||
/* activating */
|
/* activating */
|
||||||
gboolean gst_audio_ring_buffer_activate (GstAudioRingBuffer *buf, gboolean active);
|
gboolean gst_audio_ring_buffer_activate (GstAudioRingBuffer *buf, gboolean active);
|
||||||
gboolean gst_audio_ring_buffer_is_active (GstAudioRingBuffer *buf);
|
gboolean gst_audio_ring_buffer_is_active (GstAudioRingBuffer *buf);
|
||||||
|
|
Loading…
Reference in a new issue