mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 01:45:33 +00:00
- Implement queries and convert functions for vorbisenc + lots of cleanups/improvements
Original commit message from CVS: - Implement queries and convert functions for vorbisenc + lots of cleanups/improvements - catch negotiation errors in vorbisfile
This commit is contained in:
parent
2809d5864a
commit
168b9de977
3 changed files with 253 additions and 7 deletions
|
@ -58,6 +58,24 @@ enum
|
|||
ARG_LAST_MESSAGE,
|
||||
};
|
||||
|
||||
static const GstFormat*
|
||||
gst_vorbisenc_get_formats (GstPad *pad)
|
||||
{
|
||||
static const GstFormat src_formats[] = {
|
||||
GST_FORMAT_BYTES,
|
||||
GST_FORMAT_TIME,
|
||||
0
|
||||
};
|
||||
static const GstFormat sink_formats[] = {
|
||||
GST_FORMAT_BYTES,
|
||||
GST_FORMAT_UNITS,
|
||||
GST_FORMAT_TIME,
|
||||
0
|
||||
};
|
||||
|
||||
return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
|
||||
}
|
||||
|
||||
#define MAX_BITRATE_DEFAULT -1
|
||||
#define BITRATE_DEFAULT -1
|
||||
#define MIN_BITRATE_DEFAULT -1
|
||||
|
@ -169,6 +187,213 @@ gst_vorbisenc_sinkconnect (GstPad * pad, GstCaps * caps)
|
|||
return GST_PAD_CONNECT_REFUSED;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_vorbisenc_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value,
|
||||
GstFormat *dest_format, gint64 *dest_value)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
VorbisEnc *vorbisenc;
|
||||
gint64 avg;
|
||||
|
||||
vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
|
||||
|
||||
if (vorbisenc->samples_in == 0 ||
|
||||
vorbisenc->bytes_out == 0 ||
|
||||
vorbisenc->frequency == 0)
|
||||
return FALSE;
|
||||
|
||||
avg = (vorbisenc->bytes_out * vorbisenc->frequency)/
|
||||
(vorbisenc->samples_in);
|
||||
|
||||
switch (src_format) {
|
||||
case GST_FORMAT_BYTES:
|
||||
switch (*dest_format) {
|
||||
case GST_FORMAT_DEFAULT:
|
||||
*dest_format = GST_FORMAT_TIME;
|
||||
case GST_FORMAT_TIME:
|
||||
*dest_value = src_value * GST_SECOND / avg;
|
||||
break;
|
||||
default:
|
||||
res = FALSE;
|
||||
}
|
||||
break;
|
||||
case GST_FORMAT_TIME:
|
||||
switch (*dest_format) {
|
||||
case GST_FORMAT_DEFAULT:
|
||||
*dest_format = GST_FORMAT_BYTES;
|
||||
case GST_FORMAT_BYTES:
|
||||
*dest_value = src_value * avg / GST_SECOND;
|
||||
break;
|
||||
default:
|
||||
res = FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
res = FALSE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_vorbisenc_convert_sink (GstPad *pad, GstFormat src_format, gint64 src_value,
|
||||
GstFormat *dest_format, gint64 *dest_value)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
guint scale = 1;
|
||||
gint bytes_per_sample;
|
||||
VorbisEnc *vorbisenc;
|
||||
|
||||
vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
|
||||
|
||||
bytes_per_sample = vorbisenc->channels * 2;
|
||||
|
||||
switch (src_format) {
|
||||
case GST_FORMAT_BYTES:
|
||||
switch (*dest_format) {
|
||||
case GST_FORMAT_UNITS:
|
||||
if (bytes_per_sample == 0)
|
||||
return FALSE;
|
||||
*dest_value = src_value / bytes_per_sample;
|
||||
break;
|
||||
case GST_FORMAT_DEFAULT:
|
||||
*dest_format = GST_FORMAT_TIME;
|
||||
case GST_FORMAT_TIME:
|
||||
{
|
||||
gint byterate = bytes_per_sample * vorbisenc->frequency;
|
||||
|
||||
if (byterate == 0)
|
||||
return FALSE;
|
||||
*dest_value = src_value * GST_SECOND / byterate;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = FALSE;
|
||||
}
|
||||
break;
|
||||
case GST_FORMAT_UNITS:
|
||||
switch (*dest_format) {
|
||||
case GST_FORMAT_BYTES:
|
||||
*dest_value = src_value * bytes_per_sample;
|
||||
break;
|
||||
case GST_FORMAT_DEFAULT:
|
||||
*dest_format = GST_FORMAT_TIME;
|
||||
case GST_FORMAT_TIME:
|
||||
if (vorbisenc->frequency == 0)
|
||||
return FALSE;
|
||||
*dest_value = src_value * GST_SECOND / vorbisenc->frequency;
|
||||
break;
|
||||
default:
|
||||
res = FALSE;
|
||||
}
|
||||
break;
|
||||
case GST_FORMAT_TIME:
|
||||
switch (*dest_format) {
|
||||
case GST_FORMAT_DEFAULT:
|
||||
*dest_format = GST_FORMAT_BYTES;
|
||||
case GST_FORMAT_BYTES:
|
||||
scale = bytes_per_sample;
|
||||
/* fallthrough */
|
||||
case GST_FORMAT_UNITS:
|
||||
*dest_value = src_value * scale * vorbisenc->frequency / GST_SECOND;
|
||||
break;
|
||||
default:
|
||||
res = FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
res = FALSE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static const GstPadQueryType*
|
||||
gst_vorbisenc_get_query_types (GstPad *pad)
|
||||
{
|
||||
static const GstPadQueryType gst_vorbisenc_src_query_types[] = {
|
||||
GST_PAD_QUERY_TOTAL,
|
||||
GST_PAD_QUERY_POSITION,
|
||||
0
|
||||
};
|
||||
return gst_vorbisenc_src_query_types;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_vorbisenc_src_query (GstPad *pad, GstPadQueryType type,
|
||||
GstFormat *format, gint64 *value)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
VorbisEnc *vorbisenc;
|
||||
|
||||
vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
|
||||
|
||||
switch (type) {
|
||||
case GST_PAD_QUERY_TOTAL:
|
||||
{
|
||||
switch (*format) {
|
||||
case GST_FORMAT_DEFAULT:
|
||||
*format = GST_FORMAT_TIME;
|
||||
/* fallthrough */
|
||||
case GST_FORMAT_BYTES:
|
||||
case GST_FORMAT_TIME:
|
||||
{
|
||||
gint64 peer_value;
|
||||
const GstFormat *peer_formats;
|
||||
|
||||
res = FALSE;
|
||||
|
||||
peer_formats = gst_pad_get_formats (GST_PAD_PEER (vorbisenc->sinkpad));
|
||||
|
||||
while (peer_formats && *peer_formats && !res) {
|
||||
|
||||
GstFormat peer_format = *peer_formats;
|
||||
|
||||
/* do the probe */
|
||||
if (gst_pad_query (GST_PAD_PEER (vorbisenc->sinkpad), GST_PAD_QUERY_TOTAL,
|
||||
&peer_format, &peer_value))
|
||||
{
|
||||
GstFormat conv_format;
|
||||
/* convert to TIME */
|
||||
conv_format = GST_FORMAT_TIME;
|
||||
res = gst_pad_convert (vorbisenc->sinkpad,
|
||||
peer_format, peer_value,
|
||||
&conv_format, value);
|
||||
/* and to final format */
|
||||
res &= gst_pad_convert (pad,
|
||||
GST_FORMAT_TIME, *value,
|
||||
format, value);
|
||||
}
|
||||
peer_formats++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_PAD_QUERY_POSITION:
|
||||
switch (*format) {
|
||||
case GST_FORMAT_DEFAULT:
|
||||
*format = GST_FORMAT_TIME;
|
||||
/* fall through */
|
||||
default:
|
||||
{
|
||||
/* we only know about our samples, convert to requested format */
|
||||
res = gst_pad_convert (pad,
|
||||
GST_FORMAT_BYTES, vorbisenc->bytes_out,
|
||||
format, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_vorbisenc_init (VorbisEnc * vorbisenc)
|
||||
{
|
||||
|
@ -176,8 +401,14 @@ gst_vorbisenc_init (VorbisEnc * vorbisenc)
|
|||
gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->sinkpad);
|
||||
gst_pad_set_chain_function (vorbisenc->sinkpad, gst_vorbisenc_chain);
|
||||
gst_pad_set_connect_function (vorbisenc->sinkpad, gst_vorbisenc_sinkconnect);
|
||||
gst_pad_set_convert_function (vorbisenc->sinkpad, GST_DEBUG_FUNCPTR (gst_vorbisenc_convert_sink));
|
||||
gst_pad_set_formats_function (vorbisenc->sinkpad, GST_DEBUG_FUNCPTR (gst_vorbisenc_get_formats));
|
||||
|
||||
vorbisenc->srcpad = gst_pad_new_from_template (gst_vorbisenc_src_template, "src");
|
||||
gst_pad_set_query_function (vorbisenc->srcpad, GST_DEBUG_FUNCPTR (gst_vorbisenc_src_query));
|
||||
gst_pad_set_query_type_function (vorbisenc->srcpad, GST_DEBUG_FUNCPTR (gst_vorbisenc_get_query_types));
|
||||
gst_pad_set_convert_function (vorbisenc->srcpad, GST_DEBUG_FUNCPTR (gst_vorbisenc_convert_src));
|
||||
gst_pad_set_formats_function (vorbisenc->srcpad, GST_DEBUG_FUNCPTR (gst_vorbisenc_get_formats));
|
||||
gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->srcpad);
|
||||
|
||||
vorbisenc->channels = -1;
|
||||
|
@ -345,6 +576,7 @@ gst_vorbisenc_setup (VorbisEnc *vorbisenc)
|
|||
struct ovectl_ratemanage_arg ai;
|
||||
vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_GET, &ai);
|
||||
|
||||
/* the bitrates are in kHz */
|
||||
ai.bitrate_hard_min = vorbisenc->min_bitrate / 1000;
|
||||
ai.bitrate_hard_max = vorbisenc->max_bitrate / 1000;
|
||||
ai.management_active = 1;
|
||||
|
@ -455,27 +687,29 @@ gst_vorbisenc_chain (GstPad * pad, GstBuffer * buf)
|
|||
float **buffer;
|
||||
|
||||
if (!vorbisenc->setup) {
|
||||
gst_element_error (GST_ELEMENT (vorbisenc), "encoder not initialized (input is not audio?)");
|
||||
gst_buffer_unref (buf);
|
||||
gst_element_error (GST_ELEMENT (vorbisenc), "encoder not initialized (input is not audio?)");
|
||||
return;
|
||||
}
|
||||
|
||||
/* data to encode */
|
||||
data = (gint16 *) GST_BUFFER_DATA (buf);
|
||||
size = GST_BUFFER_SIZE (buf) >> 1;
|
||||
size = GST_BUFFER_SIZE (buf) / (vorbisenc->channels * 2);
|
||||
|
||||
/* expose the buffer to submit data */
|
||||
buffer = vorbis_analysis_buffer (&vorbisenc->vd, size / vorbisenc->channels);
|
||||
buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
|
||||
|
||||
/* uninterleave samples */
|
||||
for (i = 0; i < size / vorbisenc->channels; i++) {
|
||||
for (i = 0; i < size; i++) {
|
||||
for (j = 0; j < vorbisenc->channels; j++) {
|
||||
buffer[j][i] = data[i * vorbisenc->channels + j] / 32768.f;
|
||||
}
|
||||
}
|
||||
|
||||
/* tell the library how much we actually submitted */
|
||||
vorbis_analysis_wrote (&vorbisenc->vd, size / vorbisenc->channels);
|
||||
vorbis_analysis_wrote (&vorbisenc->vd, size);
|
||||
|
||||
vorbisenc->samples_in += size;
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
@ -514,6 +748,8 @@ gst_vorbisenc_chain (GstPad * pad, GstBuffer * buf)
|
|||
GST_DEBUG (0, "vorbisenc: encoded buffer of %d bytes",
|
||||
GST_BUFFER_SIZE (outbuf));
|
||||
|
||||
vorbisenc->bytes_out += GST_BUFFER_SIZE (outbuf);
|
||||
|
||||
gst_pad_push (vorbisenc->srcpad, outbuf);
|
||||
|
||||
/* this could be set above, but for illustrative purposes, I do
|
||||
|
@ -572,6 +808,7 @@ gst_vorbisenc_get_property (GObject * object, guint prop_id, GValue * value, GPa
|
|||
g_value_set_string (value, vorbisenc->last_message);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -636,6 +873,7 @@ gst_vorbisenc_set_property (GObject * object, guint prop_id, const GValue * valu
|
|||
vorbisenc->managed = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,9 @@ struct _VorbisEnc {
|
|||
gint channels;
|
||||
gint frequency;
|
||||
|
||||
guint64 samples_in;
|
||||
guint64 bytes_out;
|
||||
|
||||
GstCaps *metadata;
|
||||
|
||||
gboolean setup;
|
||||
|
|
|
@ -451,7 +451,7 @@ gst_vorbisfile_loop (GstElement *element)
|
|||
if (!GST_PAD_CAPS (vorbisfile->srcpad)) {
|
||||
vorbis_info *vi = ov_info (&vorbisfile->vf, -1);
|
||||
|
||||
gst_pad_try_set_caps (vorbisfile->srcpad,
|
||||
if (gst_pad_try_set_caps (vorbisfile->srcpad,
|
||||
GST_CAPS_NEW ("vorbisdec_src",
|
||||
"audio/raw",
|
||||
"format", GST_PROPS_STRING ("int"),
|
||||
|
@ -462,7 +462,12 @@ gst_vorbisfile_loop (GstElement *element)
|
|||
"depth", GST_PROPS_INT (16),
|
||||
"rate", GST_PROPS_INT (vi->rate),
|
||||
"channels", GST_PROPS_INT (vi->channels)
|
||||
));
|
||||
)) <= 0)
|
||||
{
|
||||
gst_buffer_unref (outbuf);
|
||||
gst_element_error (GST_ELEMENT (vorbisfile), "could not negotiate format");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vorbisfile->may_eos = TRUE;
|
||||
|
|
Loading…
Reference in a new issue