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:
Wim Taymans 2005-08-26 15:43:56 +00:00
parent ee2bc937be
commit ceb84de916
8 changed files with 842 additions and 603 deletions

View file

@ -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):

View file

@ -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

View 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;
}

View 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__ */

View file

@ -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;
}

View 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__ */

View file

@ -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);
}
}

View file

@ -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);