mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
gst/audioconvert/: Cleanups, librarify a bit, optimize, better negotiation and more.
Original commit message from CVS: * gst/audioconvert/Makefile.am: * gst/audioconvert/audioconvert.c: (if), (float), (audio_convert_get_func_index), (check_default), (audio_convert_clean_fmt), (audio_convert_prepare_context), (audio_convert_clean_context), (audio_convert_get_sizes), (get_temp_buffer), (audio_convert_convert): * gst/audioconvert/audioconvert.h: * gst/audioconvert/gstaudioconvert.c: (gst_audio_convert_class_init), (gst_audio_convert_init), (gst_audio_convert_dispose), (gst_audio_convert_parse_caps), (gst_audio_convert_get_unit_size), (gst_audio_convert_transform_caps), (gst_audio_convert_fixate_caps), (gst_audio_convert_set_caps), (gst_audio_convert_transform_ip), (gst_audio_convert_transform): * gst/audioconvert/gstaudioconvert.h: * gst/audioconvert/gstchannelmix.c: (gst_channel_mix_unset_matrix), (gst_channel_mix_fill_identical), (gst_channel_mix_fill_compatible), (gst_channel_mix_detect_pos), (gst_channel_mix_fill_one_other), (gst_channel_mix_fill_others), (gst_channel_mix_fill_normalize), (gst_channel_mix_fill_matrix), (gst_channel_mix_setup_matrix), (gst_channel_mix_passthrough), (gst_channel_mix_mix): * gst/audioconvert/gstchannelmix.h: Cleanups, librarify a bit, optimize, better negotiation and more.
This commit is contained in:
parent
ee2bc937be
commit
ceb84de916
8 changed files with 842 additions and 603 deletions
27
ChangeLog
27
ChangeLog
|
@ -1,3 +1,30 @@
|
|||
2005-08-26 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* gst/audioconvert/Makefile.am:
|
||||
* gst/audioconvert/audioconvert.c: (if), (float),
|
||||
(audio_convert_get_func_index), (check_default),
|
||||
(audio_convert_clean_fmt), (audio_convert_prepare_context),
|
||||
(audio_convert_clean_context), (audio_convert_get_sizes),
|
||||
(get_temp_buffer), (audio_convert_convert):
|
||||
* gst/audioconvert/audioconvert.h:
|
||||
* gst/audioconvert/gstaudioconvert.c:
|
||||
(gst_audio_convert_class_init), (gst_audio_convert_init),
|
||||
(gst_audio_convert_dispose), (gst_audio_convert_parse_caps),
|
||||
(gst_audio_convert_get_unit_size),
|
||||
(gst_audio_convert_transform_caps),
|
||||
(gst_audio_convert_fixate_caps), (gst_audio_convert_set_caps),
|
||||
(gst_audio_convert_transform_ip), (gst_audio_convert_transform):
|
||||
* gst/audioconvert/gstaudioconvert.h:
|
||||
* gst/audioconvert/gstchannelmix.c: (gst_channel_mix_unset_matrix),
|
||||
(gst_channel_mix_fill_identical),
|
||||
(gst_channel_mix_fill_compatible), (gst_channel_mix_detect_pos),
|
||||
(gst_channel_mix_fill_one_other), (gst_channel_mix_fill_others),
|
||||
(gst_channel_mix_fill_normalize), (gst_channel_mix_fill_matrix),
|
||||
(gst_channel_mix_setup_matrix), (gst_channel_mix_passthrough),
|
||||
(gst_channel_mix_mix):
|
||||
* gst/audioconvert/gstchannelmix.h:
|
||||
Cleanups, librarify a bit, optimize, better negotiation and more.
|
||||
|
||||
2005-08-26 Jan Schmidt <thaytan@mad.scientist.com>
|
||||
|
||||
* ext/ogg/gstoggdemux.c: (ogg_find_peek):
|
||||
|
|
|
@ -2,6 +2,7 @@ plugin_LTLIBRARIES = libgstaudioconvert.la
|
|||
|
||||
libgstaudioconvert_la_SOURCES = \
|
||||
gstaudioconvert.c \
|
||||
audioconvert.c \
|
||||
gstchannelmix.c \
|
||||
bufferframesconvert.c \
|
||||
plugin.c
|
||||
|
@ -13,6 +14,8 @@ libgstaudioconvert_la_LIBADD = \
|
|||
$(GST_LIBS)
|
||||
|
||||
noinst_HEADERS = \
|
||||
gstaudioconvert.h \
|
||||
audioconvert.h \
|
||||
gstchannelmix.h \
|
||||
plugin.h
|
||||
|
||||
|
|
393
gst/audioconvert/audioconvert.c
Normal file
393
gst/audioconvert/audioconvert.c
Normal file
|
@ -0,0 +1,393 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2005 Wim Taymans <wim at fluendo dot com>
|
||||
*
|
||||
* audioconvert.c: Convert audio to different audio formats automatically
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gstchannelmix.h"
|
||||
#include "audioconvert.h"
|
||||
|
||||
/* int to float conversion: int2float(i) = 1 / (2^31-1) * i */
|
||||
#define INT2FLOAT(i) (4.6566128752457969e-10 * ((gfloat)i))
|
||||
|
||||
#define UNPACK_CODE(type, corr, E_FUNC) \
|
||||
type* p = (type *) src; \
|
||||
gint64 tmp; \
|
||||
for (;count; count--) { \
|
||||
tmp = ((gint64) E_FUNC (*p) - corr) * scale; \
|
||||
*dst++ = CLAMP (tmp, -((gint64) 1 << 32), (gint64) 0x7FFFFFFF); \
|
||||
p++; \
|
||||
}
|
||||
|
||||
#define MAKE_UNPACK_FUNC_NAME(name) \
|
||||
audio_convert_unpack_##name
|
||||
|
||||
/* unsigned case */
|
||||
#define MAKE_UNPACK_FUNC_U(name, type, E_FUNC) \
|
||||
static void \
|
||||
MAKE_UNPACK_FUNC_NAME (name) (gpointer src, gint32 *dst, \
|
||||
gint64 scale, gint count) \
|
||||
{ \
|
||||
UNPACK_CODE(type, (1 << (sizeof (type) * 8 - 1)), E_FUNC); \
|
||||
}
|
||||
|
||||
/* signed case */
|
||||
#define MAKE_UNPACK_FUNC_S(name, type, E_FUNC) \
|
||||
static void \
|
||||
MAKE_UNPACK_FUNC_NAME (name) (gpointer src, gint32 *dst, \
|
||||
gint64 scale, gint count) \
|
||||
{ \
|
||||
UNPACK_CODE(type, 0, E_FUNC); \
|
||||
}
|
||||
|
||||
MAKE_UNPACK_FUNC_U (u8, guint8, /* nothing */ )
|
||||
MAKE_UNPACK_FUNC_S (s8, gint8, /* nothing */ )
|
||||
MAKE_UNPACK_FUNC_U (u16_le, guint16, GUINT16_FROM_LE)
|
||||
MAKE_UNPACK_FUNC_S (s16_le, gint16, GINT16_FROM_LE)
|
||||
MAKE_UNPACK_FUNC_U (u16_be, guint16, GUINT16_FROM_BE)
|
||||
MAKE_UNPACK_FUNC_S (s16_be, gint16, GINT16_FROM_BE)
|
||||
MAKE_UNPACK_FUNC_U (u32_le, guint32, GUINT32_FROM_LE)
|
||||
MAKE_UNPACK_FUNC_S (s32_le, gint32, GINT32_FROM_LE)
|
||||
MAKE_UNPACK_FUNC_U (u32_be, guint32, GUINT32_FROM_BE)
|
||||
MAKE_UNPACK_FUNC_S (s32_be, gint32, GINT32_FROM_BE)
|
||||
|
||||
/* FIXME 24 bits */
|
||||
#if 0
|
||||
gint64 cur = 0;
|
||||
|
||||
/* FIXME */
|
||||
|
||||
/* Read 24-bits LE/BE into signed 64 host-endian */
|
||||
if (this->sinkcaps.endianness == G_LITTLE_ENDIAN)
|
||||
{
|
||||
cur = src[0] | (src[1] << 8) | (src[2] << 16);
|
||||
} else {
|
||||
cur = src[2] | (src[1] << 8) | (src[0] << 16);
|
||||
}
|
||||
|
||||
/* Sign extend */
|
||||
if ((this->sinkcaps.sign)
|
||||
&& (cur & (1 << (this->sinkcaps.depth - 1))))
|
||||
cur |= ((gint64) (-1)) ^ ((1 << this->sinkcaps.depth) - 1);
|
||||
|
||||
src -= 3;
|
||||
#endif
|
||||
|
||||
static void
|
||||
MAKE_UNPACK_FUNC_NAME (float) (gpointer src, gint32 * dst,
|
||||
gint64 scale, gint count)
|
||||
{
|
||||
gfloat *p = (gfloat *) src;
|
||||
gfloat temp;
|
||||
|
||||
for (; count; count--) {
|
||||
temp = *p++ * 2147483647.0f + .5;
|
||||
*dst++ = (gint32) CLAMP (temp, -2147483648ll, 2147483647ll);
|
||||
}
|
||||
}
|
||||
|
||||
#define PACK_CODE(type, corr, E_FUNC) \
|
||||
type* p = (type *) dst; \
|
||||
gint32 scale = (32 - depth); \
|
||||
for (;count; count--) { \
|
||||
*p = E_FUNC ((type)((*src) >> scale) + corr); \
|
||||
p++; src++; \
|
||||
}
|
||||
|
||||
#define MAKE_PACK_FUNC_NAME(name) \
|
||||
audio_convert_pack_##name
|
||||
|
||||
#define MAKE_PACK_FUNC_U(name, type, E_FUNC) \
|
||||
static void \
|
||||
MAKE_PACK_FUNC_NAME (name) (gint32 *src, gpointer dst, \
|
||||
gint depth, gint count) \
|
||||
{ \
|
||||
PACK_CODE (type, (1 << (depth - 1)), E_FUNC); \
|
||||
}
|
||||
|
||||
#define MAKE_PACK_FUNC_S(name, type, E_FUNC) \
|
||||
static void \
|
||||
MAKE_PACK_FUNC_NAME (name) (gint32 *src, gpointer dst, \
|
||||
gint depth, gint count) \
|
||||
{ \
|
||||
PACK_CODE (type, 0, E_FUNC); \
|
||||
}
|
||||
|
||||
MAKE_PACK_FUNC_U (u8, guint8, /* nothing */ );
|
||||
MAKE_PACK_FUNC_S (s8, gint8, /* nothing */ );
|
||||
MAKE_PACK_FUNC_U (u16_le, guint16, GUINT16_TO_LE);
|
||||
MAKE_PACK_FUNC_S (s16_le, gint16, GINT16_TO_LE);
|
||||
MAKE_PACK_FUNC_U (u16_be, guint16, GUINT16_TO_BE);
|
||||
MAKE_PACK_FUNC_S (s16_be, gint16, GINT16_TO_BE);
|
||||
MAKE_PACK_FUNC_U (u32_le, guint32, GUINT32_TO_LE);
|
||||
MAKE_PACK_FUNC_S (s32_le, gint32, GINT32_TO_LE);
|
||||
MAKE_PACK_FUNC_U (u32_be, guint32, GUINT32_TO_BE);
|
||||
MAKE_PACK_FUNC_S (s32_be, gint32, GINT32_TO_BE);
|
||||
|
||||
static void
|
||||
MAKE_PACK_FUNC_NAME (float) (gint32 * src, gpointer dst, gint depth, gint count)
|
||||
{
|
||||
gfloat *p = (gfloat *) dst;
|
||||
|
||||
for (; count; count--) {
|
||||
*p++ = INT2FLOAT (*src++);
|
||||
}
|
||||
}
|
||||
|
||||
static AudioConvertUnpack unpack_funcs[] = {
|
||||
MAKE_UNPACK_FUNC_NAME (u8),
|
||||
MAKE_UNPACK_FUNC_NAME (s8),
|
||||
MAKE_UNPACK_FUNC_NAME (u8),
|
||||
MAKE_UNPACK_FUNC_NAME (s8),
|
||||
MAKE_UNPACK_FUNC_NAME (u16_le),
|
||||
MAKE_UNPACK_FUNC_NAME (s16_le),
|
||||
MAKE_UNPACK_FUNC_NAME (u16_be),
|
||||
MAKE_UNPACK_FUNC_NAME (s16_be),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
MAKE_UNPACK_FUNC_NAME (u32_le),
|
||||
MAKE_UNPACK_FUNC_NAME (s32_le),
|
||||
MAKE_UNPACK_FUNC_NAME (u32_be),
|
||||
MAKE_UNPACK_FUNC_NAME (s32_be),
|
||||
MAKE_UNPACK_FUNC_NAME (float),
|
||||
};
|
||||
|
||||
static AudioConvertPack pack_funcs[] = {
|
||||
MAKE_PACK_FUNC_NAME (u8),
|
||||
MAKE_PACK_FUNC_NAME (s8),
|
||||
MAKE_PACK_FUNC_NAME (u8),
|
||||
MAKE_PACK_FUNC_NAME (s8),
|
||||
MAKE_PACK_FUNC_NAME (u16_le),
|
||||
MAKE_PACK_FUNC_NAME (s16_le),
|
||||
MAKE_PACK_FUNC_NAME (u16_be),
|
||||
MAKE_PACK_FUNC_NAME (s16_be),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
MAKE_PACK_FUNC_NAME (u32_le),
|
||||
MAKE_PACK_FUNC_NAME (s32_le),
|
||||
MAKE_PACK_FUNC_NAME (u32_be),
|
||||
MAKE_PACK_FUNC_NAME (s32_be),
|
||||
MAKE_PACK_FUNC_NAME (float),
|
||||
};
|
||||
|
||||
static gint
|
||||
audio_convert_get_func_index (AudioConvertFmt * fmt)
|
||||
{
|
||||
gint index = 0;
|
||||
|
||||
if (fmt->is_int) {
|
||||
index += (fmt->width / 8 - 1) * 4;
|
||||
index += fmt->endianness == G_LITTLE_ENDIAN ? 0 : 2;
|
||||
index += fmt->sign ? 1 : 0;
|
||||
} else {
|
||||
index = 16;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_default (AudioConvertFmt * fmt)
|
||||
{
|
||||
return (fmt->width == 32 && fmt->depth == 32 &&
|
||||
fmt->endianness == G_BYTE_ORDER && fmt->sign == TRUE);
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_clean_fmt (AudioConvertFmt * fmt)
|
||||
{
|
||||
g_return_val_if_fail (fmt != NULL, FALSE);
|
||||
|
||||
g_free (fmt->pos);
|
||||
fmt->pos = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
audio_convert_prepare_context (AudioConvertCtx * ctx, AudioConvertFmt * in,
|
||||
AudioConvertFmt * out)
|
||||
{
|
||||
gint idx;
|
||||
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
g_return_val_if_fail (in != NULL, FALSE);
|
||||
g_return_val_if_fail (out != NULL, FALSE);
|
||||
|
||||
/* first clean the existing context */
|
||||
audio_convert_clean_context (ctx);
|
||||
|
||||
ctx->in = *in;
|
||||
ctx->out = *out;
|
||||
|
||||
gst_channel_mix_setup_matrix (ctx);
|
||||
|
||||
idx = audio_convert_get_func_index (in);
|
||||
if (!(ctx->unpack = unpack_funcs[idx]))
|
||||
goto not_supported;
|
||||
|
||||
idx = audio_convert_get_func_index (out);
|
||||
if (!(ctx->pack = pack_funcs[idx]))
|
||||
goto not_supported;
|
||||
|
||||
/* check if input is in default format */
|
||||
ctx->in_default = check_default (in);
|
||||
/* check if channel mixer is passthrough */
|
||||
ctx->mix_passthrough = gst_channel_mix_passthrough (ctx);
|
||||
/* check if output is in default format */
|
||||
ctx->out_default = check_default (out);
|
||||
|
||||
ctx->scale = ((gint64) 1 << (32 - in->depth));
|
||||
ctx->depth = out->depth;
|
||||
|
||||
return TRUE;
|
||||
|
||||
not_supported:
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_clean_context (AudioConvertCtx * ctx)
|
||||
{
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
|
||||
audio_convert_clean_fmt (&ctx->in);
|
||||
audio_convert_clean_fmt (&ctx->out);
|
||||
gst_channel_mix_unset_matrix (ctx);
|
||||
|
||||
g_free (ctx->tmpbuf);
|
||||
ctx->tmpbuf = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples, gint * srcsize,
|
||||
gint * dstsize)
|
||||
{
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
|
||||
if (srcsize)
|
||||
*srcsize = samples * ctx->in.unit_size;
|
||||
if (dstsize)
|
||||
*dstsize = samples * ctx->out.unit_size;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
get_temp_buffer (AudioConvertCtx * ctx, gpointer src, gint srcsize,
|
||||
gboolean writable, gint tmpsize)
|
||||
{
|
||||
gpointer result;
|
||||
|
||||
if (srcsize >= tmpsize && writable) {
|
||||
result = src;
|
||||
} else {
|
||||
if (ctx->tmpbufsize < tmpsize) {
|
||||
ctx->tmpbuf = g_realloc (ctx->tmpbuf, tmpsize);
|
||||
ctx->tmpbufsize = tmpsize;
|
||||
}
|
||||
result = ctx->tmpbuf;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
|
||||
gpointer dst, gint samples, gboolean src_writable)
|
||||
{
|
||||
gint insize;
|
||||
gboolean final;
|
||||
gpointer buf;
|
||||
gint bufsize;
|
||||
gboolean bufwritable;
|
||||
gpointer tmpdst;
|
||||
gint tmpsize;
|
||||
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
g_return_val_if_fail (src != NULL, FALSE);
|
||||
g_return_val_if_fail (dst != NULL, FALSE);
|
||||
g_return_val_if_fail (samples >= 0, FALSE);
|
||||
|
||||
if (samples == 0)
|
||||
return TRUE;
|
||||
|
||||
insize = ctx->in.unit_size * samples;
|
||||
tmpsize = insize * 32 / ctx->in.width;
|
||||
|
||||
/* this is our source data, we start with the input src data. */
|
||||
buf = src;
|
||||
bufsize = insize;
|
||||
bufwritable = src_writable;
|
||||
|
||||
if (!ctx->in_default) {
|
||||
/* check if final conversion */
|
||||
final = (ctx->out_default && ctx->mix_passthrough);
|
||||
|
||||
if (final)
|
||||
tmpdst = dst;
|
||||
else
|
||||
tmpdst = get_temp_buffer (ctx, buf, bufsize, bufwritable, tmpsize);
|
||||
|
||||
ctx->unpack (buf, tmpdst, ctx->scale, samples * ctx->in.channels);
|
||||
|
||||
if (!final) {
|
||||
/* new source data, it is writable */
|
||||
buf = tmpdst;
|
||||
bufsize = tmpsize;
|
||||
bufwritable = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx->mix_passthrough) {
|
||||
/* see if we need an intermediate step */
|
||||
final = ctx->out_default;
|
||||
|
||||
if (final)
|
||||
tmpdst = dst;
|
||||
else
|
||||
tmpdst = get_temp_buffer (ctx, buf, bufsize, bufwritable, tmpsize);
|
||||
|
||||
/* convert */
|
||||
gst_channel_mix_mix (ctx, buf, tmpdst, samples);
|
||||
|
||||
if (!final) {
|
||||
buf = tmpdst;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx->out_default) {
|
||||
/* output always to dst buffer */
|
||||
ctx->pack (buf, dst, ctx->depth, samples * ctx->out.channels);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
89
gst/audioconvert/audioconvert.h
Normal file
89
gst/audioconvert/audioconvert.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
*
|
||||
* audioconvert.h: audio format conversion library
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_CONVERT_H__
|
||||
#define __AUDIO_CONVERT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/multichannel.h>
|
||||
|
||||
typedef struct _AudioConvertCtx AudioConvertCtx;
|
||||
typedef struct _AudioConvertFmt AudioConvertFmt;
|
||||
|
||||
struct _AudioConvertFmt
|
||||
{
|
||||
/* general caps */
|
||||
gboolean is_int;
|
||||
gint endianness;
|
||||
gint width;
|
||||
gint rate;
|
||||
gint channels;
|
||||
GstAudioChannelPosition *pos;
|
||||
|
||||
/* int audio caps */
|
||||
gboolean sign;
|
||||
gint depth;
|
||||
|
||||
/* float audio caps */
|
||||
gint buffer_frames;
|
||||
|
||||
gint unit_size;
|
||||
};
|
||||
|
||||
typedef void (*AudioConvertUnpack) (gpointer src, gint32 *dst, gint64 scale, gint count);
|
||||
typedef void (*AudioConvertPack) (gint32 *src, gpointer dst, gint depth, gint count);
|
||||
|
||||
struct _AudioConvertCtx
|
||||
{
|
||||
AudioConvertFmt in;
|
||||
AudioConvertFmt out;
|
||||
|
||||
AudioConvertUnpack unpack;
|
||||
AudioConvertPack pack;
|
||||
|
||||
/* channel conversion matrix, m[in_channels][out_channels].
|
||||
* If identity matrix, passthrough applies. */
|
||||
gfloat **matrix;
|
||||
|
||||
gboolean in_default;
|
||||
gboolean mix_passthrough;
|
||||
gboolean out_default;
|
||||
|
||||
gpointer tmpbuf;
|
||||
gint tmpbufsize;
|
||||
|
||||
gint64 scale;
|
||||
gint depth;
|
||||
};
|
||||
|
||||
gboolean audio_convert_clean_fmt (AudioConvertFmt *fmt);
|
||||
|
||||
gboolean audio_convert_prepare_context (AudioConvertCtx *ctx, AudioConvertFmt *in,
|
||||
AudioConvertFmt *out);
|
||||
gboolean audio_convert_get_sizes (AudioConvertCtx *ctx, gint samples, gint *srcsize,
|
||||
gint *dstsize);
|
||||
|
||||
gboolean audio_convert_clean_context (AudioConvertCtx *ctx);
|
||||
|
||||
gboolean audio_convert_convert (AudioConvertCtx *ctx, gpointer src,
|
||||
gpointer dst, gint samples, gboolean src_writable);
|
||||
|
||||
#endif /* __AUDIO_CONVERT_H__ */
|
|
@ -1,6 +1,7 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
|
||||
* Copyright (C) 2005 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
* Copyright (C) 2005 Wim Taymans <wim at fluendo dot com>
|
||||
*
|
||||
* gstaudioconvert.c: Convert audio to different audio formats automatically
|
||||
*
|
||||
|
@ -36,10 +37,9 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include <gst/audio/multichannel.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gstaudioconvert.h"
|
||||
#include "gstchannelmix.h"
|
||||
#include "plugin.h"
|
||||
|
||||
|
@ -65,28 +65,18 @@ static void gst_audio_convert_init (GstAudioConvert * audio_convert);
|
|||
static void gst_audio_convert_dispose (GObject * obj);
|
||||
|
||||
/* gstreamer functions */
|
||||
static GstBuffer *gst_audio_convert_buffer_to_default_format (GstAudioConvert *
|
||||
this, GstBuffer * buf);
|
||||
static GstBuffer *gst_audio_convert_buffer_from_default_format (GstAudioConvert
|
||||
* this, GstBuffer * buf);
|
||||
|
||||
static GstBuffer *gst_audio_convert_channels (GstAudioConvert * this,
|
||||
GstBuffer * buf);
|
||||
|
||||
static gboolean gst_audio_convert_parse_caps (const GstCaps * gst_caps,
|
||||
GstAudioConvertCaps * caps);
|
||||
|
||||
gboolean audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps,
|
||||
guint * size);
|
||||
GstCaps *audio_convert_transform_caps (GstBaseTransform * base,
|
||||
static gboolean gst_audio_convert_get_unit_size (GstBaseTransform * base,
|
||||
GstCaps * caps, guint * size);
|
||||
static GstCaps *gst_audio_convert_transform_caps (GstBaseTransform * base,
|
||||
GstPadDirection direction, GstCaps * caps);
|
||||
void audio_convert_fixate_caps (GstBaseTransform * base,
|
||||
static void gst_audio_convert_fixate_caps (GstBaseTransform * base,
|
||||
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
|
||||
gboolean audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps,
|
||||
GstCaps * outcaps);
|
||||
static GstFlowReturn
|
||||
audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
|
||||
GstBuffer * outbuf);
|
||||
static gboolean gst_audio_convert_set_caps (GstBaseTransform * base,
|
||||
GstCaps * incaps, GstCaps * outcaps);
|
||||
static GstFlowReturn gst_audio_convert_transform (GstBaseTransform * base,
|
||||
GstBuffer * inbuf, GstBuffer * outbuf);
|
||||
static GstFlowReturn gst_audio_convert_transform_ip (GstBaseTransform * base,
|
||||
GstBuffer * buf);
|
||||
|
||||
/* AudioConvert signals and args */
|
||||
enum
|
||||
|
@ -191,25 +181,22 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass)
|
|||
supported_positions[i] = i;
|
||||
|
||||
GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
|
||||
GST_DEBUG_FUNCPTR (audio_convert_get_unit_size);
|
||||
GST_DEBUG_FUNCPTR (gst_audio_convert_get_unit_size);
|
||||
GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
|
||||
GST_DEBUG_FUNCPTR (audio_convert_transform_caps);
|
||||
GST_DEBUG_FUNCPTR (gst_audio_convert_transform_caps);
|
||||
GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps =
|
||||
GST_DEBUG_FUNCPTR (audio_convert_fixate_caps);
|
||||
GST_DEBUG_FUNCPTR (gst_audio_convert_fixate_caps);
|
||||
GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
|
||||
GST_DEBUG_FUNCPTR (audio_convert_set_caps);
|
||||
GST_DEBUG_FUNCPTR (gst_audio_convert_set_caps);
|
||||
GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
|
||||
GST_DEBUG_FUNCPTR (gst_audio_convert_transform_ip);
|
||||
GST_BASE_TRANSFORM_CLASS (klass)->transform =
|
||||
GST_DEBUG_FUNCPTR (audio_convert_transform);
|
||||
GST_DEBUG_FUNCPTR (gst_audio_convert_transform);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_convert_init (GstAudioConvert * this)
|
||||
{
|
||||
/* clear important variables */
|
||||
this->convert_internal = NULL;
|
||||
this->sinkcaps.pos = NULL;
|
||||
this->srccaps.pos = NULL;
|
||||
this->matrix = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -217,42 +204,105 @@ gst_audio_convert_dispose (GObject * obj)
|
|||
{
|
||||
GstAudioConvert *this = GST_AUDIO_CONVERT (obj);
|
||||
|
||||
if (this->sinkcaps.pos) {
|
||||
g_free (this->sinkcaps.pos);
|
||||
this->sinkcaps.pos = NULL;
|
||||
}
|
||||
|
||||
if (this->srccaps.pos) {
|
||||
g_free (this->srccaps.pos);
|
||||
this->srccaps.pos = NULL;
|
||||
}
|
||||
|
||||
gst_audio_convert_unset_matrix (this);
|
||||
audio_convert_clean_context (&this->ctx);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
||||
}
|
||||
|
||||
/*** GSTREAMER FUNCTIONS ******************************************************/
|
||||
|
||||
/* convert the given GstCaps to our format */
|
||||
static gboolean
|
||||
gst_audio_convert_parse_caps (const GstCaps * caps, AudioConvertFmt * fmt)
|
||||
{
|
||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
GST_DEBUG ("parse caps %p and %" GST_PTR_FORMAT, caps, caps);
|
||||
|
||||
g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
|
||||
g_return_val_if_fail (fmt != NULL, FALSE);
|
||||
|
||||
/* cleanup old */
|
||||
audio_convert_clean_fmt (fmt);
|
||||
|
||||
fmt->endianness = G_BYTE_ORDER;
|
||||
fmt->is_int =
|
||||
(strcmp (gst_structure_get_name (structure), "audio/x-raw-int") == 0);
|
||||
|
||||
/* parse common fields */
|
||||
if (!gst_structure_get_int (structure, "channels", &fmt->channels))
|
||||
goto no_values;
|
||||
if (!(fmt->pos = gst_audio_get_channel_positions (structure)))
|
||||
goto no_values;
|
||||
if (!gst_structure_get_int (structure, "width", &fmt->width))
|
||||
goto no_values;
|
||||
if (!gst_structure_get_int (structure, "rate", &fmt->rate))
|
||||
goto no_values;
|
||||
|
||||
if (fmt->is_int) {
|
||||
/* int specific fields */
|
||||
if (!gst_structure_get_boolean (structure, "signed", &fmt->sign))
|
||||
goto no_values;
|
||||
if (!gst_structure_get_int (structure, "depth", &fmt->depth))
|
||||
goto no_values;
|
||||
|
||||
/* width != 8 can have an endianness field */
|
||||
if (fmt->width != 8) {
|
||||
if (!gst_structure_get_int (structure, "endianness", &fmt->endianness))
|
||||
goto no_values;
|
||||
}
|
||||
/* depth cannot be bigger than the width */
|
||||
if (fmt->depth > fmt->width)
|
||||
goto not_allowed;
|
||||
} else {
|
||||
/* float specific fields */
|
||||
if (!gst_structure_get_int (structure, "buffer-frames",
|
||||
&fmt->buffer_frames))
|
||||
goto no_values;
|
||||
}
|
||||
|
||||
fmt->unit_size = (fmt->width * fmt->channels) / 8;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
no_values:
|
||||
{
|
||||
GST_DEBUG ("could not get some values from structure");
|
||||
audio_convert_clean_fmt (fmt);
|
||||
return FALSE;
|
||||
}
|
||||
not_allowed:
|
||||
{
|
||||
GST_DEBUG ("width > depth, not allowed - make us advertise correct fmt");
|
||||
audio_convert_clean_fmt (fmt);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* BaseTransform vmethods */
|
||||
gboolean
|
||||
audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps,
|
||||
static gboolean
|
||||
gst_audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps,
|
||||
guint * size)
|
||||
{
|
||||
GstAudioConvertCaps ac_caps;
|
||||
AudioConvertFmt ac_caps = { 0 };
|
||||
|
||||
g_return_val_if_fail (size, FALSE);
|
||||
|
||||
memset (&ac_caps, 0, sizeof (ac_caps));
|
||||
if (!gst_audio_convert_parse_caps (caps, &ac_caps))
|
||||
goto parse_error;
|
||||
|
||||
if (!gst_audio_convert_parse_caps (caps, &ac_caps)) {
|
||||
g_free (ac_caps.pos);
|
||||
|
||||
*size = ac_caps.unit_size;
|
||||
|
||||
return TRUE;
|
||||
|
||||
parse_error:
|
||||
{
|
||||
g_free (ac_caps.pos);
|
||||
return FALSE;
|
||||
}
|
||||
g_free (ac_caps.pos);
|
||||
|
||||
*size = ac_caps.width * ac_caps.channels / 8;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* audioconvert can convert anything except sample rate; so return template
|
||||
|
@ -260,25 +310,27 @@ audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps,
|
|||
/* FIXME:
|
||||
* it would be smart here to return the caps with the same width as the first
|
||||
*/
|
||||
GstCaps *
|
||||
audio_convert_transform_caps (GstBaseTransform * base,
|
||||
static GstCaps *
|
||||
gst_audio_convert_transform_caps (GstBaseTransform * base,
|
||||
GstPadDirection direction, GstCaps * caps)
|
||||
{
|
||||
int i;
|
||||
const GValue *rate;
|
||||
GstCaps *ret;
|
||||
GstStructure *structure;
|
||||
|
||||
g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL);
|
||||
|
||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
GstCaps *ret = gst_static_caps_get (&gst_audio_convert_static_caps);
|
||||
ret = gst_static_caps_get (&gst_audio_convert_static_caps);
|
||||
|
||||
ret = gst_caps_make_writable (ret);
|
||||
|
||||
rate = gst_structure_get_value (structure, "rate");
|
||||
if (!rate) {
|
||||
/* if rate not set, we return the template */
|
||||
if (!(rate = gst_structure_get_value (structure, "rate")))
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* else, write rate in the template caps */
|
||||
ret = gst_caps_make_writable (ret);
|
||||
|
||||
for (i = 0; i < gst_caps_get_size (ret); ++i) {
|
||||
structure = gst_caps_get_structure (ret, i);
|
||||
|
@ -290,12 +342,12 @@ audio_convert_transform_caps (GstBaseTransform * base,
|
|||
/* try to keep as many of the structure members the same by fixating the
|
||||
* possible ranges; this way we convert the least amount of things as possible
|
||||
*/
|
||||
void
|
||||
audio_convert_fixate_caps (GstBaseTransform * base,
|
||||
static void
|
||||
gst_audio_convert_fixate_caps (GstBaseTransform * base,
|
||||
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
|
||||
{
|
||||
GstStructure *ins, *outs;
|
||||
gint rate, endianness, depth;
|
||||
gint rate, endianness, depth, width, channels;
|
||||
gboolean signedness;
|
||||
|
||||
g_return_if_fail (gst_caps_is_fixed (caps));
|
||||
|
@ -306,6 +358,11 @@ audio_convert_fixate_caps (GstBaseTransform * base,
|
|||
ins = gst_caps_get_structure (caps, 0);
|
||||
outs = gst_caps_get_structure (othercaps, 0);
|
||||
|
||||
if (gst_structure_get_int (ins, "channels", &channels)) {
|
||||
if (gst_structure_has_field (outs, "channels")) {
|
||||
gst_caps_structure_fixate_field_nearest_int (outs, "channels", channels);
|
||||
}
|
||||
}
|
||||
if (gst_structure_get_int (ins, "rate", &rate)) {
|
||||
if (gst_structure_has_field (outs, "rate")) {
|
||||
gst_caps_structure_fixate_field_nearest_int (outs, "rate", rate);
|
||||
|
@ -317,11 +374,25 @@ audio_convert_fixate_caps (GstBaseTransform * base,
|
|||
endianness);
|
||||
}
|
||||
}
|
||||
if (gst_structure_get_int (ins, "width", &width)) {
|
||||
if (gst_structure_has_field (outs, "width")) {
|
||||
gst_caps_structure_fixate_field_nearest_int (outs, "width", width);
|
||||
}
|
||||
} else {
|
||||
/* this is not allowed */
|
||||
}
|
||||
|
||||
if (gst_structure_get_int (ins, "depth", &depth)) {
|
||||
if (gst_structure_has_field (outs, "depth")) {
|
||||
gst_caps_structure_fixate_field_nearest_int (outs, "depth", depth);
|
||||
}
|
||||
} else {
|
||||
/* set depth as width */
|
||||
if (gst_structure_has_field (outs, "depth")) {
|
||||
gst_caps_structure_fixate_field_nearest_int (outs, "depth", width);
|
||||
}
|
||||
}
|
||||
|
||||
if (gst_structure_get_boolean (ins, "signed", &signedness)) {
|
||||
if (gst_structure_has_field (outs, "signed")) {
|
||||
gst_caps_structure_fixate_field_boolean (outs, "signed", signedness);
|
||||
|
@ -331,424 +402,82 @@ audio_convert_fixate_caps (GstBaseTransform * base,
|
|||
GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps,
|
||||
static gboolean
|
||||
gst_audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps,
|
||||
GstCaps * outcaps)
|
||||
{
|
||||
GstAudioConvertCaps in_ac_caps = { 0 };
|
||||
GstAudioConvertCaps out_ac_caps = { 0 };
|
||||
AudioConvertFmt in_ac_caps = { 0 };
|
||||
AudioConvertFmt out_ac_caps = { 0 };
|
||||
GstAudioConvert *this = GST_AUDIO_CONVERT (base);
|
||||
|
||||
GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
|
||||
GST_PTR_FORMAT, incaps, outcaps);
|
||||
|
||||
in_ac_caps.pos = NULL;
|
||||
if (!gst_audio_convert_parse_caps (incaps, &in_ac_caps))
|
||||
return FALSE;
|
||||
|
||||
out_ac_caps.pos = NULL;
|
||||
if (!gst_audio_convert_parse_caps (outcaps, &out_ac_caps))
|
||||
return FALSE;
|
||||
|
||||
this->sinkcaps = in_ac_caps;
|
||||
this->srccaps = out_ac_caps;
|
||||
|
||||
GST_DEBUG ("setting up matrix");
|
||||
gst_audio_convert_setup_matrix (this);
|
||||
GST_DEBUG ("set up matrix, %p", this->matrix);
|
||||
if (!audio_convert_prepare_context (&this->ctx, &in_ac_caps, &out_ac_caps))
|
||||
goto no_converter;
|
||||
|
||||
return TRUE;
|
||||
|
||||
no_converter:
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
|
||||
GstBuffer * outbuf)
|
||||
gst_audio_convert_transform_ip (GstBaseTransform * base, GstBuffer * buf)
|
||||
{
|
||||
GstAudioConvert *this = GST_AUDIO_CONVERT (base);
|
||||
GstBuffer *buf;
|
||||
|
||||
/*
|
||||
* Theory of operation:
|
||||
* - convert the format (endianness, signedness, width, depth) to
|
||||
* (G_BYTE_ORDER, TRUE, 32, 32)
|
||||
* - convert rate and channels
|
||||
* - convert back to output format
|
||||
*/
|
||||
|
||||
/* FIXME: optimize for copying */
|
||||
buf = gst_buffer_copy (inbuf);
|
||||
buf = gst_audio_convert_buffer_to_default_format (this, buf);
|
||||
buf = gst_audio_convert_channels (this, buf);
|
||||
buf = gst_audio_convert_buffer_from_default_format (this, buf);
|
||||
memcpy (GST_BUFFER_DATA (outbuf), GST_BUFFER_DATA (buf),
|
||||
GST_BUFFER_SIZE (outbuf));
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
/* nothing to do here */
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
/* convert the given GstCaps to our ghetto format */
|
||||
static gboolean
|
||||
gst_audio_convert_parse_caps (const GstCaps * gst_caps,
|
||||
GstAudioConvertCaps * caps)
|
||||
static GstFlowReturn
|
||||
gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
|
||||
GstBuffer * outbuf)
|
||||
{
|
||||
GstStructure *structure = gst_caps_get_structure (gst_caps, 0);
|
||||
GstAudioConvert *this = GST_AUDIO_CONVERT (base);
|
||||
gboolean res;
|
||||
gint insize, outsize;
|
||||
gint samples;
|
||||
gpointer src, dst;
|
||||
|
||||
GST_DEBUG ("parse caps %p and %" GST_PTR_FORMAT, gst_caps, gst_caps);
|
||||
/* get amount of samples to convert. */
|
||||
samples = GST_BUFFER_SIZE (inbuf) / this->ctx.in.unit_size;
|
||||
|
||||
g_return_val_if_fail (gst_caps_is_fixed (gst_caps), FALSE);
|
||||
g_return_val_if_fail (caps != NULL, FALSE);
|
||||
/* get in/output sizes, to see if the buffers we got are of correct
|
||||
* sizes */
|
||||
if (!(res = audio_convert_get_sizes (&this->ctx, samples, &insize, &outsize)))
|
||||
goto error;
|
||||
|
||||
/* cleanup old */
|
||||
if (caps->pos) {
|
||||
g_free (caps->pos);
|
||||
caps->pos = NULL;
|
||||
/* check in and outsize */
|
||||
if (GST_BUFFER_SIZE (inbuf) < insize)
|
||||
goto wrong_size;
|
||||
if (GST_BUFFER_SIZE (outbuf) < outsize)
|
||||
goto wrong_size;
|
||||
|
||||
/* get src and dst data */
|
||||
src = GST_BUFFER_DATA (inbuf);
|
||||
dst = GST_BUFFER_DATA (outbuf);
|
||||
|
||||
/* and convert the samples */
|
||||
if (!(res = audio_convert_convert (&this->ctx, src, dst,
|
||||
samples, gst_buffer_is_writable (inbuf))))
|
||||
goto error;
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
/* ERRORS */
|
||||
error:
|
||||
{
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
caps->endianness = G_BYTE_ORDER;
|
||||
caps->is_int =
|
||||
(strcmp (gst_structure_get_name (structure), "audio/x-raw-int") == 0);
|
||||
if (!gst_structure_get_int (structure, "channels", &caps->channels)
|
||||
|| !(caps->pos = gst_audio_get_channel_positions (structure))
|
||||
|| !gst_structure_get_int (structure, "width", &caps->width)
|
||||
|| !gst_structure_get_int (structure, "rate", &caps->rate)
|
||||
|| (caps->is_int
|
||||
&& (!gst_structure_get_boolean (structure, "signed", &caps->sign)
|
||||
|| !gst_structure_get_int (structure, "depth", &caps->depth)
|
||||
|| (caps->width != 8
|
||||
&& !gst_structure_get_int (structure, "endianness",
|
||||
&caps->endianness)))) || (!caps->is_int
|
||||
&& !gst_structure_get_int (structure, "buffer-frames",
|
||||
&caps->buffer_frames))) {
|
||||
GST_DEBUG ("could not get some values from structure");
|
||||
g_free (caps->pos);
|
||||
caps->pos = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
if (caps->is_int && caps->depth > caps->width) {
|
||||
GST_DEBUG ("width > depth, not allowed - make us advertise correct caps");
|
||||
g_free (caps->pos);
|
||||
caps->pos = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* return a writable buffer of size which ideally is the same as before
|
||||
- You must unref the new buffer
|
||||
- The size of the old buffer is undefined after this operation */
|
||||
static GstBuffer *
|
||||
gst_audio_convert_get_buffer (GstBuffer * buf, guint size)
|
||||
{
|
||||
GstBuffer *ret;
|
||||
|
||||
g_assert (GST_IS_BUFFER (buf));
|
||||
|
||||
GST_LOG
|
||||
("new buffer of size %u requested. Current is: data: %p - size: %u",
|
||||
size, buf->data, buf->size);
|
||||
if (buf->size >= size && gst_buffer_is_writable (buf)) {
|
||||
gst_buffer_ref (buf);
|
||||
buf->size = size;
|
||||
GST_LOG
|
||||
("returning same buffer with adjusted values. data: %p - size: %u",
|
||||
buf->data, buf->size);
|
||||
return buf;
|
||||
} else {
|
||||
ret = gst_buffer_new_and_alloc (size);
|
||||
g_assert (ret);
|
||||
gst_buffer_stamp (ret, buf);
|
||||
GST_LOG ("returning new buffer. data: %p - size: %u", ret->data, ret->size);
|
||||
return ret;
|
||||
wrong_size:
|
||||
{
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static inline guint8
|
||||
GUINT8_IDENTITY (guint8 x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
static inline guint8
|
||||
GINT8_IDENTITY (gint8 x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
#define CONVERT_TO(to, from, type, sign, endianness, LE_FUNC, BE_FUNC) \
|
||||
G_STMT_START { \
|
||||
type value; \
|
||||
memcpy (&value, from, sizeof (type)); \
|
||||
from -= sizeof (type); \
|
||||
value = (endianness == G_LITTLE_ENDIAN) ? \
|
||||
LE_FUNC (value) : BE_FUNC (value); \
|
||||
if (sign) { \
|
||||
to = value; \
|
||||
} else { \
|
||||
to = (gint64) value - (1 << (sizeof (type) * 8 - 1)); \
|
||||
} \
|
||||
} G_STMT_END;
|
||||
|
||||
static GstBuffer *
|
||||
gst_audio_convert_buffer_to_default_format (GstAudioConvert * this,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
GstBaseTransform *base = GST_BASE_TRANSFORM (this);
|
||||
GstBuffer *ret;
|
||||
gint i, count;
|
||||
gint64 cur = 0;
|
||||
gint32 write;
|
||||
gint32 *dest;
|
||||
guint8 *src;
|
||||
|
||||
GST_LOG_OBJECT (base, "converting buffer of size %d to default format",
|
||||
GST_BUFFER_SIZE (buf));
|
||||
if (this->sinkcaps.is_int) {
|
||||
if (this->sinkcaps.width == 32 && this->sinkcaps.depth == 32 &&
|
||||
this->sinkcaps.endianness == G_BYTE_ORDER
|
||||
&& this->sinkcaps.sign == TRUE)
|
||||
return buf;
|
||||
|
||||
ret =
|
||||
gst_audio_convert_get_buffer (buf,
|
||||
buf->size * 32 / this->sinkcaps.width);
|
||||
gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad));
|
||||
|
||||
count = ret->size / 4;
|
||||
src = buf->data + (count - 1) * (this->sinkcaps.width / 8);
|
||||
dest = (gint32 *) ret->data;
|
||||
for (i = count - 1; i >= 0; i--) {
|
||||
switch (this->sinkcaps.width) {
|
||||
case 8:
|
||||
if (this->sinkcaps.sign) {
|
||||
CONVERT_TO (cur, src, gint8, this->sinkcaps.sign,
|
||||
this->sinkcaps.endianness, GINT8_IDENTITY, GINT8_IDENTITY);
|
||||
} else {
|
||||
CONVERT_TO (cur, src, guint8, this->sinkcaps.sign,
|
||||
this->sinkcaps.endianness, GUINT8_IDENTITY, GUINT8_IDENTITY);
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
if (this->sinkcaps.sign) {
|
||||
CONVERT_TO (cur, src, gint16, this->sinkcaps.sign,
|
||||
this->sinkcaps.endianness, GINT16_FROM_LE, GINT16_FROM_BE);
|
||||
} else {
|
||||
CONVERT_TO (cur, src, guint16, this->sinkcaps.sign,
|
||||
this->sinkcaps.endianness, GUINT16_FROM_LE, GUINT16_FROM_BE);
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
{
|
||||
/* Read 24-bits LE/BE into signed 64 host-endian */
|
||||
if (this->sinkcaps.endianness == G_LITTLE_ENDIAN) {
|
||||
cur = src[0] | (src[1] << 8) | (src[2] << 16);
|
||||
} else {
|
||||
cur = src[2] | (src[1] << 8) | (src[0] << 16);
|
||||
}
|
||||
|
||||
/* Sign extend */
|
||||
if ((this->sinkcaps.sign)
|
||||
&& (cur & (1 << (this->sinkcaps.depth - 1))))
|
||||
cur |= ((gint64) (-1)) ^ ((1 << this->sinkcaps.depth) - 1);
|
||||
|
||||
src -= 3;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
if (this->sinkcaps.sign) {
|
||||
CONVERT_TO (cur, src, gint32, this->sinkcaps.sign,
|
||||
this->sinkcaps.endianness, GINT32_FROM_LE, GINT32_FROM_BE);
|
||||
} else {
|
||||
CONVERT_TO (cur, src, guint32, this->sinkcaps.sign,
|
||||
this->sinkcaps.endianness, GUINT32_FROM_LE, GUINT32_FROM_BE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
cur = cur * ((gint64) 1 << (32 - this->sinkcaps.depth));
|
||||
cur = CLAMP (cur, -((gint64) 1 << 32), (gint64) 0x7FFFFFFF);
|
||||
write = cur;
|
||||
memcpy (&dest[i], &write, 4);
|
||||
}
|
||||
} else {
|
||||
/* float2int */
|
||||
gfloat *in;
|
||||
gint32 *out;
|
||||
float temp;
|
||||
|
||||
/* should just give the same buffer, unless it's not writable -- float is
|
||||
* already 32 bits */
|
||||
ret = gst_audio_convert_get_buffer (buf, buf->size);
|
||||
gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad));
|
||||
|
||||
in = (gfloat *) GST_BUFFER_DATA (buf);
|
||||
out = (gint32 *) GST_BUFFER_DATA (ret);
|
||||
for (i = buf->size / sizeof (float); i > 0; i--) {
|
||||
temp = *in * 2147483647.0f + .5;
|
||||
*out = (gint32) CLAMP ((gint64) temp, -2147483648ll, 2147483647ll);
|
||||
out++;
|
||||
in++;
|
||||
}
|
||||
}
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define POPULATE(out, format, be_func, le_func) G_STMT_START { \
|
||||
format val; \
|
||||
format* p = (format *) out; \
|
||||
int_value >>= (32 - this->srccaps.depth); \
|
||||
if (this->srccaps.sign) { \
|
||||
val = (format) int_value; \
|
||||
} else { \
|
||||
val = (format) int_value + (1 << (this->srccaps.depth - 1)); \
|
||||
} \
|
||||
switch (this->srccaps.endianness) { \
|
||||
case G_LITTLE_ENDIAN: \
|
||||
val = le_func (val); \
|
||||
break; \
|
||||
case G_BIG_ENDIAN: \
|
||||
val = be_func (val); \
|
||||
break; \
|
||||
default: \
|
||||
g_assert_not_reached (); \
|
||||
}; \
|
||||
*p = val; \
|
||||
p ++; \
|
||||
out = (guint8 *) p; \
|
||||
}G_STMT_END
|
||||
|
||||
static GstBuffer *
|
||||
gst_audio_convert_buffer_from_default_format (GstAudioConvert * this,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
GstBaseTransform *base;
|
||||
GstBuffer *ret;
|
||||
guint count, i;
|
||||
gint32 *src;
|
||||
|
||||
base = GST_BASE_TRANSFORM (this);
|
||||
|
||||
GST_LOG_OBJECT (base, "converting buffer of size %d from default format",
|
||||
GST_BUFFER_SIZE (buf));
|
||||
|
||||
if (this->srccaps.is_int && this->srccaps.width == 32
|
||||
&& this->srccaps.depth == 32 && this->srccaps.endianness == G_BYTE_ORDER
|
||||
&& this->srccaps.sign == TRUE)
|
||||
return buf;
|
||||
|
||||
if (this->srccaps.is_int) {
|
||||
guint8 *dest;
|
||||
|
||||
count = buf->size / 4; /* size is undefined after gst_audio_convert_get_buffer! */
|
||||
ret =
|
||||
gst_audio_convert_get_buffer (buf,
|
||||
buf->size * this->srccaps.width / 32);
|
||||
gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad));
|
||||
|
||||
dest = ret->data;
|
||||
src = (gint32 *) buf->data;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
gint32 int_value = *src;
|
||||
|
||||
src++;
|
||||
switch (this->srccaps.width) {
|
||||
case 8:
|
||||
if (this->srccaps.sign) {
|
||||
POPULATE (dest, gint8, GINT8_IDENTITY, GINT8_IDENTITY);
|
||||
} else {
|
||||
POPULATE (dest, guint8, GUINT8_IDENTITY, GUINT8_IDENTITY);
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
if (this->srccaps.sign) {
|
||||
POPULATE (dest, gint16, GINT16_TO_BE, GINT16_TO_LE);
|
||||
} else {
|
||||
POPULATE (dest, guint16, GUINT16_TO_BE, GUINT16_TO_LE);
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
{
|
||||
guint8 tmp[4];
|
||||
guint8 *tmpp = tmp;
|
||||
|
||||
/* Write out big endian array */
|
||||
if (this->srccaps.sign) {
|
||||
POPULATE (tmpp, gint32, GINT32_TO_BE, GINT32_TO_BE);
|
||||
} else {
|
||||
POPULATE (tmpp, guint32, GUINT32_TO_BE, GUINT32_TO_BE);
|
||||
}
|
||||
|
||||
if (this->srccaps.endianness == G_LITTLE_ENDIAN) {
|
||||
dest[2] = tmp[1];
|
||||
dest[1] = tmp[2];
|
||||
dest[0] = tmp[3];
|
||||
} else {
|
||||
memcpy (dest, tmp + 1, 3);
|
||||
}
|
||||
dest += 3;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
if (this->srccaps.sign) {
|
||||
POPULATE (dest, gint32, GINT32_TO_BE, GINT32_TO_LE);
|
||||
} else {
|
||||
POPULATE (dest, guint32, GUINT32_TO_BE, GUINT32_TO_LE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gfloat *dest;
|
||||
|
||||
count = buf->size / 4; /* size is undefined after gst_audio_convert_get_buffer! */
|
||||
ret =
|
||||
gst_audio_convert_get_buffer (buf,
|
||||
buf->size * this->srccaps.width / 32);
|
||||
gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad));
|
||||
|
||||
dest = (gfloat *) ret->data;
|
||||
src = (gint32 *) buf->data;
|
||||
for (i = 0; i < count; i++) {
|
||||
*dest = INT2FLOAT (*src);
|
||||
dest++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_audio_convert_channels (GstAudioConvert * this, GstBuffer * buf)
|
||||
{
|
||||
GstBaseTransform *base = GST_BASE_TRANSFORM (this);
|
||||
GstBuffer *ret;
|
||||
gint units; /* one unit is one sample of audio for each channel, combined */
|
||||
|
||||
g_assert (this->matrix != NULL);
|
||||
|
||||
GST_LOG_OBJECT (base, "converting buffer of size %d for different channels",
|
||||
GST_BUFFER_SIZE (buf));
|
||||
|
||||
/* check for passthrough */
|
||||
if (gst_audio_convert_passthrough (this))
|
||||
return buf;
|
||||
|
||||
/* convert */
|
||||
GST_LOG_OBJECT (base, "%d sinkpad channels, %d srcpad channels",
|
||||
this->sinkcaps.channels, this->srccaps.channels);
|
||||
units = GST_BUFFER_SIZE (buf) / 4 / this->sinkcaps.channels;
|
||||
ret = gst_audio_convert_get_buffer (buf, units * 4 * this->srccaps.channels);
|
||||
gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad));
|
||||
gst_audio_convert_mix (this, (gint32 *) GST_BUFFER_DATA (buf),
|
||||
(gint32 *) GST_BUFFER_DATA (ret), units);
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
52
gst/audioconvert/gstaudioconvert.h
Normal file
52
gst/audioconvert/gstaudioconvert.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2005 Wim Taymans <wim at fluendo dot com>
|
||||
*
|
||||
* gstaudioconvert.h: Convert audio to different audio formats automatically
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_AUDIO_CONVERT_H__
|
||||
#define __GST_AUDIO_CONVERT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include <gst/audio/multichannel.h>
|
||||
|
||||
#include "audioconvert.h"
|
||||
|
||||
#define GST_TYPE_AUDIO_CONVERT (gst_audio_convert_get_type())
|
||||
#define GST_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_CONVERT,GstAudioConvert))
|
||||
#define GST_AUDIO_CONVERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_CONVERT,GstAudioConvert))
|
||||
#define GST_IS_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_CONVERT))
|
||||
#define GST_IS_AUDIO_CONVERT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_CONVERT))
|
||||
|
||||
typedef struct _GstAudioConvert GstAudioConvert;
|
||||
typedef struct _GstAudioConvertClass GstAudioConvertClass;
|
||||
|
||||
struct _GstAudioConvert
|
||||
{
|
||||
GstBaseTransform element;
|
||||
|
||||
AudioConvertCtx ctx;
|
||||
};
|
||||
|
||||
struct _GstAudioConvertClass
|
||||
{
|
||||
GstBaseTransformClass parent_class;
|
||||
};
|
||||
|
||||
#endif /* __GST_AUDIO_CONVERT_H__ */
|
|
@ -43,7 +43,7 @@
|
|||
*/
|
||||
|
||||
void
|
||||
gst_audio_convert_unset_matrix (GstAudioConvert * this)
|
||||
gst_channel_mix_unset_matrix (AudioConvertCtx * this)
|
||||
{
|
||||
gint i;
|
||||
|
||||
|
@ -52,7 +52,7 @@ gst_audio_convert_unset_matrix (GstAudioConvert * this)
|
|||
return;
|
||||
|
||||
/* free */
|
||||
for (i = 0; i < this->sinkcaps.channels; i++)
|
||||
for (i = 0; i < this->in.channels; i++)
|
||||
g_free (this->matrix[i]);
|
||||
g_free (this->matrix);
|
||||
|
||||
|
@ -66,17 +66,17 @@ gst_audio_convert_unset_matrix (GstAudioConvert * this)
|
|||
*/
|
||||
|
||||
static void
|
||||
gst_audio_convert_fill_identical (GstAudioConvert * this)
|
||||
gst_channel_mix_fill_identical (AudioConvertCtx * this)
|
||||
{
|
||||
gint ci, co;
|
||||
|
||||
/* Apart from the compatible channel assignments, we can also have
|
||||
* same channel assignments. This is much simpler, we simply copy
|
||||
* the value from source to dest! */
|
||||
for (co = 0; co < this->srccaps.channels; co++) {
|
||||
for (co = 0; co < this->out.channels; co++) {
|
||||
/* find a channel in input with same position */
|
||||
for (ci = 0; ci < this->sinkcaps.channels; ci++) {
|
||||
if (this->sinkcaps.pos[ci] == this->srccaps.pos[co]) {
|
||||
for (ci = 0; ci < this->in.channels; ci++) {
|
||||
if (this->in.pos[ci] == this->out.pos[co]) {
|
||||
this->matrix[ci][co] = 1.0;
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ gst_audio_convert_fill_identical (GstAudioConvert * this)
|
|||
*/
|
||||
|
||||
static void
|
||||
gst_audio_convert_fill_compatible (GstAudioConvert * this)
|
||||
gst_channel_mix_fill_compatible (AudioConvertCtx * this)
|
||||
{
|
||||
/* Conversions from one-channel to compatible two-channel configs */
|
||||
struct
|
||||
|
@ -128,14 +128,14 @@ gst_audio_convert_fill_compatible (GstAudioConvert * this)
|
|||
gint pos1_0 = -1, pos1_1 = -1, pos2_0 = -1, n;
|
||||
|
||||
/* Try to go from the given 2 channels to the given 1 channel */
|
||||
for (n = 0; n < this->sinkcaps.channels; n++) {
|
||||
if (this->sinkcaps.pos[n] == conv[c].pos1[0])
|
||||
for (n = 0; n < this->in.channels; n++) {
|
||||
if (this->in.pos[n] == conv[c].pos1[0])
|
||||
pos1_0 = n;
|
||||
else if (this->sinkcaps.pos[n] == conv[c].pos1[1])
|
||||
else if (this->in.pos[n] == conv[c].pos1[1])
|
||||
pos1_1 = n;
|
||||
}
|
||||
for (n = 0; n < this->srccaps.channels; n++) {
|
||||
if (this->srccaps.pos[n] == conv[c].pos2[0])
|
||||
for (n = 0; n < this->out.channels; n++) {
|
||||
if (this->out.pos[n] == conv[c].pos2[0])
|
||||
pos2_0 = n;
|
||||
}
|
||||
|
||||
|
@ -149,14 +149,14 @@ gst_audio_convert_fill_compatible (GstAudioConvert * this)
|
|||
pos1_1 = -1;
|
||||
pos2_0 = -1;
|
||||
|
||||
for (n = 0; n < this->srccaps.channels; n++) {
|
||||
if (this->srccaps.pos[n] == conv[c].pos1[0])
|
||||
for (n = 0; n < this->out.channels; n++) {
|
||||
if (this->out.pos[n] == conv[c].pos1[0])
|
||||
pos1_0 = n;
|
||||
else if (this->srccaps.pos[n] == conv[c].pos1[1])
|
||||
else if (this->out.pos[n] == conv[c].pos1[1])
|
||||
pos1_1 = n;
|
||||
}
|
||||
for (n = 0; n < this->sinkcaps.channels; n++) {
|
||||
if (this->sinkcaps.pos[n] == conv[c].pos2[0])
|
||||
for (n = 0; n < this->in.channels; n++) {
|
||||
if (this->in.pos[n] == conv[c].pos2[0])
|
||||
pos2_0 = n;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ gst_audio_convert_fill_compatible (GstAudioConvert * this)
|
|||
*/
|
||||
|
||||
static void
|
||||
gst_audio_convert_detect_pos (GstAudioConvertCaps * caps,
|
||||
gst_channel_mix_detect_pos (AudioConvertFmt * caps,
|
||||
gint * f, gboolean * has_f,
|
||||
gint * c, gboolean * has_c, gint * r, gboolean * has_r,
|
||||
gint * s, gboolean * has_s, gint * b, gboolean * has_b)
|
||||
|
@ -232,12 +232,12 @@ gst_audio_convert_detect_pos (GstAudioConvertCaps * caps,
|
|||
}
|
||||
|
||||
static void
|
||||
gst_audio_convert_fill_one_other (gfloat ** matrix,
|
||||
GstAudioConvertCaps * from_caps, gint * from_idx,
|
||||
gst_channel_mix_fill_one_other (gfloat ** matrix,
|
||||
AudioConvertFmt * from_caps, gint * from_idx,
|
||||
GstAudioChannelPosition from_pos_l,
|
||||
GstAudioChannelPosition from_pos_r,
|
||||
GstAudioChannelPosition from_pos_c,
|
||||
GstAudioConvertCaps * to_caps, gint * to_idx,
|
||||
AudioConvertFmt * to_caps, gint * to_idx,
|
||||
GstAudioChannelPosition to_pos_l,
|
||||
GstAudioChannelPosition to_pos_r,
|
||||
GstAudioChannelPosition to_pos_c, gfloat ratio)
|
||||
|
@ -285,7 +285,7 @@ gst_audio_convert_fill_one_other (gfloat ** matrix,
|
|||
#define RATIO_CENTER_BASS (1.0 / sqrt (2.0))
|
||||
|
||||
static void
|
||||
gst_audio_convert_fill_others (GstAudioConvert * this)
|
||||
gst_channel_mix_fill_others (AudioConvertCtx * this)
|
||||
{
|
||||
gboolean in_has_front = FALSE, out_has_front = FALSE,
|
||||
in_has_center = FALSE, out_has_center = FALSE,
|
||||
|
@ -305,33 +305,33 @@ gst_audio_convert_fill_others (GstAudioConvert * this)
|
|||
|
||||
/* First see where (if at all) the various channels from/to
|
||||
* which we want to convert are located in our matrix/array. */
|
||||
gst_audio_convert_detect_pos (&this->sinkcaps,
|
||||
gst_channel_mix_detect_pos (&this->in,
|
||||
in_f, &in_has_front,
|
||||
in_c, &in_has_center, in_r, &in_has_rear,
|
||||
in_s, &in_has_side, in_b, &in_has_bass);
|
||||
gst_audio_convert_detect_pos (&this->srccaps,
|
||||
gst_channel_mix_detect_pos (&this->out,
|
||||
out_f, &out_has_front,
|
||||
out_c, &out_has_center, out_r, &out_has_rear,
|
||||
out_s, &out_has_side, out_b, &out_has_bass);
|
||||
|
||||
/* center/front */
|
||||
if (!in_has_center && in_has_front && out_has_center) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_f,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_f,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
|
||||
&this->srccaps, out_c,
|
||||
&this->out, out_c,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_FRONT_CENTER);
|
||||
} else if (in_has_center && !out_has_center && out_has_front) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_c,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_c,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
&this->srccaps, out_f,
|
||||
&this->out, out_f,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_CENTER);
|
||||
|
@ -339,22 +339,22 @@ gst_audio_convert_fill_others (GstAudioConvert * this)
|
|||
|
||||
/* rear/front */
|
||||
if (!in_has_rear && in_has_front && out_has_rear) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_f,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_f,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
|
||||
&this->srccaps, out_r,
|
||||
&this->out, out_r,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_FRONT_REAR);
|
||||
} else if (in_has_center && !out_has_center && out_has_front) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_r,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_r,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
|
||||
&this->srccaps, out_f,
|
||||
&this->out, out_f,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_REAR);
|
||||
|
@ -363,68 +363,68 @@ gst_audio_convert_fill_others (GstAudioConvert * this)
|
|||
/* bass/any */
|
||||
if (in_has_bass && !out_has_bass) {
|
||||
if (out_has_front) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_b,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_b,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE,
|
||||
&this->srccaps, out_f,
|
||||
&this->out, out_f,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_BASS);
|
||||
}
|
||||
if (out_has_center) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_b,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_b,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE,
|
||||
&this->srccaps, out_c,
|
||||
&this->out, out_c,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_CENTER_BASS);
|
||||
}
|
||||
if (out_has_rear) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_b,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_b,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE,
|
||||
&this->srccaps, out_r,
|
||||
&this->out, out_r,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_REAR_BASS);
|
||||
}
|
||||
} else if (!in_has_bass && out_has_bass) {
|
||||
if (in_has_front) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_f,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_f,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
|
||||
&this->srccaps, out_b,
|
||||
&this->out, out_b,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_FRONT_BASS);
|
||||
}
|
||||
if (in_has_center) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_c,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_c,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
|
||||
&this->srccaps, out_b,
|
||||
&this->out, out_b,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_CENTER_BASS);
|
||||
}
|
||||
if (in_has_rear) {
|
||||
gst_audio_convert_fill_one_other (this->matrix,
|
||||
&this->sinkcaps, in_r,
|
||||
gst_channel_mix_fill_one_other (this->matrix,
|
||||
&this->in, in_r,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
|
||||
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
|
||||
&this->srccaps, out_b,
|
||||
&this->out, out_b,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_INVALID,
|
||||
GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_REAR_BASS);
|
||||
|
@ -439,15 +439,15 @@ gst_audio_convert_fill_others (GstAudioConvert * this)
|
|||
*/
|
||||
|
||||
static void
|
||||
gst_audio_convert_fill_normalize (GstAudioConvert * this)
|
||||
gst_channel_mix_fill_normalize (AudioConvertCtx * this)
|
||||
{
|
||||
gfloat sum, top = 0;
|
||||
gint i, j;
|
||||
|
||||
for (j = 0; j < this->srccaps.channels; j++) {
|
||||
for (j = 0; j < this->out.channels; j++) {
|
||||
/* calculate sum */
|
||||
sum = 0.0;
|
||||
for (i = 0; i < this->sinkcaps.channels; i++) {
|
||||
for (i = 0; i < this->in.channels; i++) {
|
||||
sum += fabs (this->matrix[i][j]);
|
||||
}
|
||||
if (sum > top) {
|
||||
|
@ -456,8 +456,8 @@ gst_audio_convert_fill_normalize (GstAudioConvert * this)
|
|||
}
|
||||
|
||||
/* normalize to this */
|
||||
for (j = 0; j < this->srccaps.channels; j++) {
|
||||
for (i = 0; i < this->sinkcaps.channels; i++) {
|
||||
for (j = 0; j < this->out.channels; j++) {
|
||||
for (i = 0; i < this->in.channels; i++) {
|
||||
this->matrix[i][j] /= top;
|
||||
}
|
||||
}
|
||||
|
@ -468,45 +468,45 @@ gst_audio_convert_fill_normalize (GstAudioConvert * this)
|
|||
*/
|
||||
|
||||
static void
|
||||
gst_audio_convert_fill_matrix (GstAudioConvert * this)
|
||||
gst_channel_mix_fill_matrix (AudioConvertCtx * this)
|
||||
{
|
||||
gst_audio_convert_fill_identical (this);
|
||||
gst_audio_convert_fill_compatible (this);
|
||||
gst_audio_convert_fill_others (this);
|
||||
gst_audio_convert_fill_normalize (this);
|
||||
gst_channel_mix_fill_identical (this);
|
||||
gst_channel_mix_fill_compatible (this);
|
||||
gst_channel_mix_fill_others (this);
|
||||
gst_channel_mix_fill_normalize (this);
|
||||
}
|
||||
|
||||
/* only call after this->srccaps and this->sinkcaps are filled in */
|
||||
/* only call after this->out and this->in are filled in */
|
||||
void
|
||||
gst_audio_convert_setup_matrix (GstAudioConvert * this)
|
||||
gst_channel_mix_setup_matrix (AudioConvertCtx * this)
|
||||
{
|
||||
gint i, j;
|
||||
GString *s;
|
||||
|
||||
/* don't lose memory */
|
||||
gst_audio_convert_unset_matrix (this);
|
||||
gst_channel_mix_unset_matrix (this);
|
||||
|
||||
/* allocate */
|
||||
this->matrix = g_new0 (gfloat *, this->sinkcaps.channels);
|
||||
for (i = 0; i < this->sinkcaps.channels; i++) {
|
||||
this->matrix[i] = g_new (gfloat, this->srccaps.channels);
|
||||
for (j = 0; j < this->srccaps.channels; j++)
|
||||
this->matrix = g_new0 (gfloat *, this->in.channels);
|
||||
for (i = 0; i < this->in.channels; i++) {
|
||||
this->matrix[i] = g_new (gfloat, this->out.channels);
|
||||
for (j = 0; j < this->out.channels; j++)
|
||||
this->matrix[i][j] = 0.;
|
||||
}
|
||||
|
||||
/* setup the matrix' internal values */
|
||||
gst_audio_convert_fill_matrix (this);
|
||||
gst_channel_mix_fill_matrix (this);
|
||||
|
||||
/* debug */
|
||||
s = g_string_new ("Matrix for");
|
||||
g_string_append_printf (s, " %d -> %d: ",
|
||||
this->sinkcaps.channels, this->srccaps.channels);
|
||||
this->in.channels, this->out.channels);
|
||||
g_string_append (s, "{");
|
||||
for (i = 0; i < this->sinkcaps.channels; i++) {
|
||||
for (i = 0; i < this->in.channels; i++) {
|
||||
if (i != 0)
|
||||
g_string_append (s, ",");
|
||||
g_string_append (s, " {");
|
||||
for (j = 0; j < this->srccaps.channels; j++) {
|
||||
for (j = 0; j < this->out.channels; j++) {
|
||||
if (j != 0)
|
||||
g_string_append (s, ",");
|
||||
g_string_append_printf (s, " %f", this->matrix[i][j]);
|
||||
|
@ -519,16 +519,16 @@ gst_audio_convert_setup_matrix (GstAudioConvert * this)
|
|||
}
|
||||
|
||||
gboolean
|
||||
gst_audio_convert_passthrough (GstAudioConvert * this)
|
||||
gst_channel_mix_passthrough (AudioConvertCtx * this)
|
||||
{
|
||||
gint i;
|
||||
|
||||
/* only NxN matrices can be identities */
|
||||
if (this->sinkcaps.channels != this->srccaps.channels)
|
||||
if (this->in.channels != this->out.channels)
|
||||
return FALSE;
|
||||
|
||||
/* this assumes a normalized matrix */
|
||||
for (i = 0; i < this->sinkcaps.channels; i++)
|
||||
for (i = 0; i < this->in.channels; i++)
|
||||
if (this->matrix[i][i] != 1.)
|
||||
return FALSE;
|
||||
|
||||
|
@ -538,23 +538,22 @@ gst_audio_convert_passthrough (GstAudioConvert * this)
|
|||
/* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data
|
||||
* you might need later on! */
|
||||
void
|
||||
gst_audio_convert_mix (GstAudioConvert * this,
|
||||
gst_channel_mix_mix (AudioConvertCtx * this,
|
||||
gint32 * in_data, gint32 * out_data, gint samples)
|
||||
{
|
||||
gint in, out, n;
|
||||
gint64 res;
|
||||
gint32 tmp[this->srccaps.channels];
|
||||
gboolean backwards = this->srccaps.channels > this->sinkcaps.channels;
|
||||
gint32 tmp[this->out.channels];
|
||||
gboolean backwards = this->out.channels > this->in.channels;
|
||||
|
||||
/* FIXME: use liboil here? */
|
||||
for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0;
|
||||
backwards ? n-- : n++) {
|
||||
for (out = 0; out < this->srccaps.channels; out++) {
|
||||
for (out = 0; out < this->out.channels; out++) {
|
||||
/* convert */
|
||||
res = 0;
|
||||
for (in = 0; in < this->sinkcaps.channels; in++) {
|
||||
res += in_data[n * this->sinkcaps.channels + in] *
|
||||
this->matrix[in][out];
|
||||
for (in = 0; in < this->in.channels; in++) {
|
||||
res += in_data[n * this->in.channels + in] * this->matrix[in][out];
|
||||
}
|
||||
|
||||
/* clip (shouldn't we use doubles instead as intermediate format?) */
|
||||
|
@ -564,7 +563,7 @@ gst_audio_convert_mix (GstAudioConvert * this,
|
|||
res = G_MAXINT32;
|
||||
tmp[out] = res;
|
||||
}
|
||||
memcpy (&out_data[n * this->srccaps.channels], tmp,
|
||||
sizeof (gint32) * this->srccaps.channels);
|
||||
memcpy (&out_data[n * this->out.channels], tmp,
|
||||
sizeof (gint32) * this->out.channels);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,83 +23,30 @@
|
|||
#define __GST_CHANNEL_MIX_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include <gst/audio/multichannel.h>
|
||||
|
||||
#define GST_TYPE_AUDIO_CONVERT (gst_audio_convert_get_type())
|
||||
#define GST_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_CONVERT,GstAudioConvert))
|
||||
#define GST_AUDIO_CONVERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_CONVERT,GstAudioConvert))
|
||||
#define GST_IS_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_CONVERT))
|
||||
#define GST_IS_AUDIO_CONVERT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_CONVERT))
|
||||
#include "audioconvert.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (audio_convert_debug);
|
||||
#define GST_CAT_DEFAULT (audio_convert_debug)
|
||||
|
||||
typedef struct _GstAudioConvert GstAudioConvert;
|
||||
typedef struct _GstAudioConvertCaps GstAudioConvertCaps;
|
||||
typedef struct _GstAudioConvertClass GstAudioConvertClass;
|
||||
|
||||
/* this struct is a handy way of passing around all the caps info ... */
|
||||
struct _GstAudioConvertCaps
|
||||
{
|
||||
/* general caps */
|
||||
gboolean is_int;
|
||||
gint endianness;
|
||||
gint width;
|
||||
gint rate;
|
||||
gint channels;
|
||||
GstAudioChannelPosition *pos;
|
||||
|
||||
/* int audio caps */
|
||||
gboolean sign;
|
||||
gint depth;
|
||||
|
||||
/* float audio caps */
|
||||
gint buffer_frames;
|
||||
};
|
||||
|
||||
struct _GstAudioConvert
|
||||
{
|
||||
GstBaseTransform element;
|
||||
|
||||
GstAudioConvertCaps srccaps;
|
||||
GstAudioConvertCaps sinkcaps;
|
||||
|
||||
GstCaps *src_prefered;
|
||||
GstCaps *sink_prefered;
|
||||
|
||||
/* channel conversion matrix, m[in_channels][out_channels].
|
||||
* If identity matrix, passthrough applies. */
|
||||
gfloat **matrix;
|
||||
|
||||
/* conversion functions */
|
||||
GstBuffer *(*convert_internal) (GstAudioConvert * this, GstBuffer * buf);
|
||||
};
|
||||
|
||||
struct _GstAudioConvertClass
|
||||
{
|
||||
GstBaseTransformClass parent_class;
|
||||
};
|
||||
|
||||
/*
|
||||
* Delete channel mixer matrix.
|
||||
*/
|
||||
void gst_audio_convert_unset_matrix (GstAudioConvert * this);
|
||||
void gst_channel_mix_unset_matrix (AudioConvertCtx * this);
|
||||
|
||||
/*
|
||||
* Setup channel mixer matrix.
|
||||
*/
|
||||
void gst_audio_convert_setup_matrix (GstAudioConvert * this);
|
||||
void gst_channel_mix_setup_matrix (AudioConvertCtx * this);
|
||||
|
||||
/*
|
||||
* Checks for passthrough (= identity matrix).
|
||||
*/
|
||||
gboolean gst_audio_convert_passthrough (GstAudioConvert * this);
|
||||
gboolean gst_channel_mix_passthrough (AudioConvertCtx * this);
|
||||
|
||||
/*
|
||||
* Do actual mixing.
|
||||
*/
|
||||
void gst_audio_convert_mix (GstAudioConvert * this,
|
||||
void gst_channel_mix_mix (AudioConvertCtx * this,
|
||||
gint32 * in_data,
|
||||
gint32 * out_data,
|
||||
gint samples);
|
||||
|
|
Loading…
Reference in a new issue