audioconvert: implement support for converting between interleaved and non-interleaved layouts

https://bugzilla.gnome.org/show_bug.cgi?id=705986
This commit is contained in:
George Kiagiadakis 2018-02-01 18:08:51 +02:00
parent 060ecd16cd
commit 0eda002cfd

View file

@ -138,6 +138,8 @@ static gboolean gst_audio_convert_transform_meta (GstBaseTransform * trans,
GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf);
static GstFlowReturn gst_audio_convert_submit_input_buffer (GstBaseTransform *
base, gboolean is_discont, GstBuffer * input);
static GstFlowReturn gst_audio_convert_prepare_output_buffer (GstBaseTransform *
base, GstBuffer * inbuf, GstBuffer ** outbuf);
static void gst_audio_convert_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_audio_convert_get_property (GObject * object, guint prop_id,
@ -169,7 +171,7 @@ G_DEFINE_TYPE_WITH_CODE (GstAudioConvert, gst_audio_convert,
#define STATIC_CAPS \
GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) \
", layout = (string) interleaved")
", layout = (string) { interleaved, non-interleaved }")
static GstStaticPadTemplate gst_audio_convert_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
@ -243,6 +245,8 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass)
GST_DEBUG_FUNCPTR (gst_audio_convert_transform_meta);
basetransform_class->submit_input_buffer =
GST_DEBUG_FUNCPTR (gst_audio_convert_submit_input_buffer);
basetransform_class->prepare_output_buffer =
GST_DEBUG_FUNCPTR (gst_audio_convert_prepare_output_buffer);
basetransform_class->passthrough_on_same_caps = TRUE;
basetransform_class->transform_ip_on_passthrough = FALSE;
@ -307,6 +311,14 @@ remove_format_from_structure (GstCapsFeatures * features,
return TRUE;
}
static gboolean
remove_layout_from_structure (GstCapsFeatures * features,
GstStructure * structure, gpointer user_data G_GNUC_UNUSED)
{
gst_structure_remove_field (structure, "layout");
return TRUE;
}
static gboolean
remove_channels_from_structure (GstCapsFeatures * features, GstStructure * s,
gpointer user_data)
@ -352,6 +364,7 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans,
tmp = gst_caps_copy (caps);
gst_caps_map_in_place (tmp, remove_format_from_structure, NULL);
gst_caps_map_in_place (tmp, remove_layout_from_structure, NULL);
gst_caps_map_in_place (tmp, remove_channels_from_structure, btrans);
/* We can infer the required input / output channels based on the
@ -792,47 +805,28 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
{
GstFlowReturn ret;
GstAudioConvert *this = GST_AUDIO_CONVERT (base);
GstMapInfo srcmap = { NULL, }, dstmap;
gint insize, outsize;
GstAudioBuffer srcabuf, dstabuf;
gboolean inbuf_writable;
GstAudioConverterFlags flags;
gsize samples;
/* get amount of samples to convert. */
samples = gst_buffer_get_size (inbuf) / this->in_info.bpf;
/* get in/output sizes, to see if the buffers we got are of correct
* sizes */
insize = samples * this->in_info.bpf;
outsize = samples * this->out_info.bpf;
if (insize == 0 || outsize == 0)
/* https://bugzilla.gnome.org/show_bug.cgi?id=396835 */
if (gst_buffer_get_size (inbuf) == 0)
return GST_FLOW_OK;
gst_buffer_resize (outbuf, 0, outsize);
/* get src and dst data */
if (inbuf != outbuf) {
inbuf_writable = gst_buffer_is_writable (inbuf)
&& gst_buffer_n_memory (inbuf) == 1
&& gst_memory_is_writable (gst_buffer_peek_memory (inbuf, 0));
if (!gst_buffer_map (inbuf, &srcmap,
if (!gst_audio_buffer_map (&srcabuf, &this->in_info, inbuf,
inbuf_writable ? GST_MAP_READWRITE : GST_MAP_READ))
goto inmap_error;
} else {
inbuf_writable = TRUE;
}
if (!gst_buffer_map (outbuf, &dstmap, GST_MAP_WRITE))
goto outmap_error;
/* check in and outsize */
if (inbuf != outbuf) {
if (srcmap.size < insize)
goto wrong_size;
}
if (dstmap.size < outsize)
goto wrong_size;
if (!gst_audio_buffer_map (&dstabuf, &this->out_info, outbuf, GST_MAP_WRITE))
goto outmap_error;
/* and convert the samples */
flags = 0;
@ -840,36 +834,28 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
flags |= GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
gpointer in[1] = { srcmap.data };
gpointer out[1] = { dstmap.data };
if (!gst_audio_converter_samples (this->convert, flags,
inbuf != outbuf ? in : out, samples, out, samples))
inbuf != outbuf ? srcabuf.planes : dstabuf.planes,
dstabuf.n_samples, dstabuf.planes, dstabuf.n_samples))
goto convert_error;
} else {
/* Create silence buffer */
gst_audio_format_fill_silence (this->out_info.finfo, dstmap.data, outsize);
gint i;
for (i = 0; i < dstabuf.n_planes; i++) {
gst_audio_format_fill_silence (this->out_info.finfo, dstabuf.planes[i],
GST_AUDIO_BUFFER_PLANE_SIZE (&dstabuf));
}
}
ret = GST_FLOW_OK;
done:
gst_buffer_unmap (outbuf, &dstmap);
gst_audio_buffer_unmap (&dstabuf);
if (inbuf != outbuf)
gst_buffer_unmap (inbuf, &srcmap);
gst_audio_buffer_unmap (&srcabuf);
return ret;
/* ERRORS */
wrong_size:
{
GST_ELEMENT_ERROR (this, STREAM, FORMAT,
(NULL),
("input/output buffers are of wrong size in: %" G_GSIZE_FORMAT " < %d"
" or out: %" G_GSIZE_FORMAT " < %d",
srcmap.size, insize, dstmap.size, outsize));
ret = GST_FLOW_ERROR;
goto done;
}
convert_error:
{
GST_ELEMENT_ERROR (this, STREAM, FORMAT,
@ -888,7 +874,7 @@ outmap_error:
GST_ELEMENT_ERROR (this, STREAM, FORMAT,
(NULL), ("failed to map output buffer"));
if (inbuf != outbuf)
gst_buffer_unmap (inbuf, &srcmap);
gst_audio_buffer_unmap (&srcabuf);
return GST_FLOW_ERROR;
}
}
@ -935,6 +921,43 @@ gst_audio_convert_submit_input_buffer (GstBaseTransform * base,
is_discont, input);
}
static GstFlowReturn
gst_audio_convert_prepare_output_buffer (GstBaseTransform * base,
GstBuffer * inbuf, GstBuffer ** outbuf)
{
GstAudioConvert *this = GST_AUDIO_CONVERT (base);
GstAudioMeta *meta;
GstFlowReturn ret;
ret = GST_BASE_TRANSFORM_CLASS (parent_class)->prepare_output_buffer (base,
inbuf, outbuf);
if (ret != GST_FLOW_OK)
return ret;
meta = gst_buffer_get_audio_meta (inbuf);
if (inbuf != *outbuf) {
gsize samples = meta ?
meta->samples : (gst_buffer_get_size (inbuf) / this->in_info.bpf);
/* ensure that the output buffer is not bigger than what we need */
gst_buffer_resize (*outbuf, 0, samples * this->out_info.bpf);
/* add the audio meta on the output buffer if it's planar */
if (this->out_info.layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
gst_buffer_add_audio_meta (*outbuf, &this->out_info, samples, NULL);
}
} else {
/* if the input buffer came with a GstAudioMeta,
* update it to reflect the properties of the output format */
if (meta)
meta->info = this->out_info;
}
return ret;
}
static void
gst_audio_convert_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)