From f0edb5fb7063e41d3d0c51bb8ec4b514da2e3c1e Mon Sep 17 00:00:00 2001 From: Alexander Schrab Date: Wed, 24 Apr 2013 13:56:56 +0200 Subject: [PATCH] mulawenc: change to gstaudioencoder base, added bitrate tags --- gst/law/mulaw-encode.c | 306 +++++++++++--------------------- gst/law/mulaw-encode.h | 18 +- tests/check/Makefile.am | 1 + tests/check/elements/mulawenc.c | 180 +++++++++++++++++++ 4 files changed, 294 insertions(+), 211 deletions(-) create mode 100644 tests/check/elements/mulawenc.c diff --git a/gst/law/mulaw-encode.c b/gst/law/mulaw-encode.c index 59606144f2..8450fef4d6 100644 --- a/gst/law/mulaw-encode.c +++ b/gst/law/mulaw-encode.c @@ -46,138 +46,148 @@ enum ARG_0 }; -static gboolean gst_mulawenc_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static GstFlowReturn gst_mulawenc_chain (GstPad * pad, GstObject * parent, +static gboolean gst_mulawenc_start (GstAudioEncoder * audioenc); +static gboolean gst_mulawenc_set_format (GstAudioEncoder * enc, + GstAudioInfo * info); +static GstFlowReturn gst_mulawenc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer); +static void gst_mulawenc_set_tags (GstMuLawEnc * mulawenc); + #define gst_mulawenc_parent_class parent_class -G_DEFINE_TYPE (GstMuLawEnc, gst_mulawenc, GST_TYPE_ELEMENT); +G_DEFINE_TYPE (GstMuLawEnc, gst_mulawenc, GST_TYPE_AUDIO_ENCODER); /*static guint gst_stereo_signals[LAST_SIGNAL] = { 0 }; */ -static GstCaps * -mulawenc_getcaps (GstPad * pad, GstCaps * filter) +static gboolean +gst_mulawenc_start (GstAudioEncoder * audioenc) { - GstMuLawEnc *mulawenc; - GstPad *otherpad; - GstCaps *othercaps, *result; - GstCaps *templ; - const gchar *name; - gint i; + GstMuLawEnc *mulawenc = GST_MULAWENC (audioenc); - mulawenc = GST_MULAWENC (GST_PAD_PARENT (pad)); + mulawenc->channels = 0; + mulawenc->rate = 0; - /* figure out the name of the caps we are going to return */ - if (pad == mulawenc->srcpad) { - name = "audio/x-mulaw"; - otherpad = mulawenc->sinkpad; - } else { - name = "audio/x-raw"; - otherpad = mulawenc->srcpad; - } - /* get caps from the peer, this can return NULL when there is no peer */ - othercaps = gst_pad_peer_query_caps (otherpad, NULL); - - /* get the template caps to make sure we return something acceptable */ - templ = gst_pad_get_pad_template_caps (pad); - - if (othercaps) { - /* there was a peer */ - othercaps = gst_caps_make_writable (othercaps); - - /* go through the caps and remove the fields we don't want */ - for (i = 0; i < gst_caps_get_size (othercaps); i++) { - GstStructure *structure; - - structure = gst_caps_get_structure (othercaps, i); - - /* adjust the name */ - gst_structure_set_name (structure, name); - - if (pad == mulawenc->srcpad) { - /* remove the fields we don't want */ - gst_structure_remove_fields (structure, "format", NULL); - } else { - /* add fixed fields */ - gst_structure_set (structure, "format", G_TYPE_STRING, - GST_AUDIO_NE (S16), NULL); - } - } - /* filter against the allowed caps of the pad to return our result */ - result = gst_caps_intersect (othercaps, templ); - gst_caps_unref (othercaps); - gst_caps_unref (templ); - } else { - /* there was no peer, return the template caps */ - result = templ; - } - if (filter && result) { - GstCaps *temp; - - temp = gst_caps_intersect (result, filter); - gst_caps_unref (result); - result = temp; - } - - return result; + return TRUE; } -static gboolean -gst_mulawenc_query (GstPad * pad, GstObject * parent, GstQuery * query) + +static void +gst_mulawenc_set_tags (GstMuLawEnc * mulawenc) { - gboolean res; + GstTagList *taglist; + guint bitrate; - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CAPS: - { - GstCaps *filter, *caps; + /* bitrate of mulaw is 8 bits/sample * sample rate * number of channels */ + bitrate = 8 * mulawenc->rate * mulawenc->channels; - gst_query_parse_caps (query, &filter); - caps = mulawenc_getcaps (pad, filter); - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); + taglist = gst_tag_list_new_empty (); + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_MAXIMUM_BITRATE, bitrate, NULL); + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_MINIMUM_BITRATE, bitrate, NULL); + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_BITRATE, bitrate, NULL); - res = TRUE; - break; - } - default: - res = gst_pad_query_default (pad, parent, query); - break; - } - return res; + gst_audio_encoder_merge_tags (GST_AUDIO_ENCODER (mulawenc), + taglist, GST_TAG_MERGE_REPLACE); + + gst_tag_list_unref (taglist); } static gboolean -mulawenc_setcaps (GstMuLawEnc * mulawenc, GstCaps * caps) +gst_mulawenc_set_format (GstAudioEncoder * audioenc, GstAudioInfo * info) { - GstStructure *structure; GstCaps *base_caps; + GstStructure *structure; + GstMuLawEnc *mulawenc = GST_MULAWENC (audioenc); - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "channels", &mulawenc->channels); - gst_structure_get_int (structure, "rate", &mulawenc->rate); + mulawenc->rate = info->rate; + mulawenc->channels = info->channels; - base_caps = gst_pad_get_pad_template_caps (mulawenc->srcpad); + base_caps = + gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (audioenc)); + g_assert (base_caps); base_caps = gst_caps_make_writable (base_caps); + g_assert (base_caps); structure = gst_caps_get_structure (base_caps, 0); + g_assert (structure); gst_structure_set (structure, "rate", G_TYPE_INT, mulawenc->rate, NULL); gst_structure_set (structure, "channels", G_TYPE_INT, mulawenc->channels, NULL); - gst_pad_set_caps (mulawenc->srcpad, base_caps); + gst_mulawenc_set_tags (mulawenc); - gst_caps_unref (base_caps); - - return TRUE; + return gst_audio_encoder_set_output_format (audioenc, base_caps); } +static GstFlowReturn +gst_mulawenc_handle_frame (GstAudioEncoder * audioenc, GstBuffer * buffer) +{ + GstMuLawEnc *mulawenc; + GstMapInfo inmap, outmap; + gint16 *linear_data; + gsize linear_size; + guint8 *mulaw_data; + guint mulaw_size; + GstBuffer *outbuf; + GstFlowReturn ret; + + if (!buffer) { + ret = GST_FLOW_OK; + goto done; + } + + mulawenc = GST_MULAWENC (audioenc); + + if (!mulawenc->rate || !mulawenc->channels) + goto not_negotiated; + + gst_buffer_map (buffer, &inmap, GST_MAP_READ); + linear_data = (gint16 *) inmap.data; + linear_size = inmap.size; + + mulaw_size = linear_size / 2; + + outbuf = gst_audio_encoder_allocate_output_buffer (audioenc, mulaw_size); + + g_assert (outbuf); + + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + mulaw_data = outmap.data; + + mulaw_encode (linear_data, mulaw_data, mulaw_size); + + gst_buffer_unmap (outbuf, &outmap); + gst_buffer_unmap (buffer, &inmap); + + ret = gst_audio_encoder_finish_frame (audioenc, outbuf, -1); + +done: + + return ret; + +not_negotiated: + { + GST_DEBUG_OBJECT (mulawenc, "no format negotiated"); + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } +} + + + static void gst_mulawenc_class_init (GstMuLawEncClass * klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstAudioEncoderClass *audio_encoder_class = GST_AUDIO_ENCODER_CLASS (klass); + + audio_encoder_class->start = GST_DEBUG_FUNCPTR (gst_mulawenc_start); + audio_encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_mulawenc_set_format); + audio_encoder_class->handle_frame = + GST_DEBUG_FUNCPTR (gst_mulawenc_handle_frame); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&mulaw_enc_src_factory)); @@ -193,111 +203,5 @@ gst_mulawenc_class_init (GstMuLawEncClass * klass) static void gst_mulawenc_init (GstMuLawEnc * mulawenc) { - mulawenc->sinkpad = - gst_pad_new_from_static_template (&mulaw_enc_sink_factory, "sink"); - gst_pad_set_query_function (mulawenc->sinkpad, gst_mulawenc_query); - gst_pad_set_event_function (mulawenc->sinkpad, gst_mulawenc_event); - gst_pad_set_chain_function (mulawenc->sinkpad, gst_mulawenc_chain); - gst_element_add_pad (GST_ELEMENT (mulawenc), mulawenc->sinkpad); - mulawenc->srcpad = - gst_pad_new_from_static_template (&mulaw_enc_src_factory, "src"); - gst_pad_set_query_function (mulawenc->srcpad, gst_mulawenc_query); - gst_element_add_pad (GST_ELEMENT (mulawenc), mulawenc->srcpad); - - /* init rest */ - mulawenc->channels = 0; - mulawenc->rate = 0; -} - -static gboolean -gst_mulawenc_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstMuLawEnc *mulawenc; - gboolean res; - - mulawenc = GST_MULAWENC (parent); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - mulawenc_setcaps (mulawenc, caps); - gst_event_unref (event); - - res = TRUE; - break; - } - default: - res = gst_pad_event_default (pad, parent, event); - break; - } - return res; -} - -static GstFlowReturn -gst_mulawenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) -{ - GstMuLawEnc *mulawenc; - GstMapInfo inmap, outmap; - gint16 *linear_data; - gsize linear_size; - guint8 *mulaw_data; - guint mulaw_size; - GstBuffer *outbuf; - GstFlowReturn ret; - GstClockTime timestamp, duration; - - mulawenc = GST_MULAWENC (parent); - - if (!mulawenc->rate || !mulawenc->channels) - goto not_negotiated; - - gst_buffer_map (buffer, &inmap, GST_MAP_READ); - linear_data = (gint16 *) inmap.data; - linear_size = inmap.size; - - mulaw_size = linear_size / 2; - - timestamp = GST_BUFFER_TIMESTAMP (buffer); - duration = GST_BUFFER_DURATION (buffer); - - outbuf = gst_buffer_new_allocate (NULL, mulaw_size, NULL); - - if (duration == -1) { - duration = gst_util_uint64_scale_int (mulaw_size, - GST_SECOND, mulawenc->rate * mulawenc->channels); - } - - gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); - mulaw_data = outmap.data; - - /* copy discont flag */ - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - - GST_BUFFER_TIMESTAMP (outbuf) = timestamp; - GST_BUFFER_DURATION (outbuf) = duration; - - mulaw_encode (linear_data, mulaw_data, mulaw_size); - - gst_buffer_unmap (outbuf, &outmap); - gst_buffer_unmap (buffer, &inmap); - gst_buffer_unref (buffer); - - ret = gst_pad_push (mulawenc->srcpad, outbuf); - -done: - - return ret; - -not_negotiated: - { - GST_DEBUG_OBJECT (mulawenc, "no format negotiated"); - ret = GST_FLOW_NOT_NEGOTIATED; - gst_buffer_unref (buffer); - goto done; - } } diff --git a/gst/law/mulaw-encode.h b/gst/law/mulaw-encode.h index 15904f1528..c5ade39c1e 100644 --- a/gst/law/mulaw-encode.h +++ b/gst/law/mulaw-encode.h @@ -22,9 +22,9 @@ #define __GST_MULAWENCODE_H__ #include +#include G_BEGIN_DECLS - #define GST_TYPE_MULAWENC \ (gst_mulawenc_get_type()) #define GST_MULAWENC(obj) \ @@ -35,25 +35,23 @@ G_BEGIN_DECLS (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MULAWENC)) #define GST_IS_MULAWENC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MULAWENC)) - typedef struct _GstMuLawEnc GstMuLawEnc; typedef struct _GstMuLawEncClass GstMuLawEncClass; -struct _GstMuLawEnc { - GstElement element; - - GstPad *sinkpad,*srcpad; +struct _GstMuLawEnc +{ + GstAudioEncoder element; gint channels; gint rate; }; -struct _GstMuLawEncClass { - GstElementClass parent_class; +struct _GstMuLawEncClass +{ + GstAudioEncoderClass parent_class; }; -GType gst_mulawenc_get_type(void); +GType gst_mulawenc_get_type (void); G_END_DECLS - #endif /* __GST_STEREO_H__ */ diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 3f4d6b5eff..8f1e811043 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -134,6 +134,7 @@ check_PROGRAMS = \ elements/matroskamux \ elements/matroskaparse \ elements/mpegaudioparse \ + elements/mulawenc \ elements/multifile \ elements/qtmux \ elements/rganalysis \ diff --git a/tests/check/elements/mulawenc.c b/tests/check/elements/mulawenc.c new file mode 100644 index 0000000000..a0a45644b1 --- /dev/null +++ b/tests/check/elements/mulawenc.c @@ -0,0 +1,180 @@ +/* GStreamer MulawEnc unit tests + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +GList *buffers = NULL; + +GstPad *mysrcpad, *mysinkpad; +GstElement *mulawenc = NULL; + + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-mulaw," "rate = (int) 8000," "channels = (int) 1") + ); + + +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw," + "format = (string) S16LE, " + "rate = (int) 8000, " + "channels = (int) 1, " "layout = (string)interleaved") + ); + +static void +mulawenc_setup (void) +{ + GstCaps *src_caps; + + src_caps = gst_caps_from_string ("audio/x-raw," + "format = (string) S16LE, " + "rate = (int) 8000, " + "channels = (int) 1, " "layout = (string)interleaved"); + + GST_DEBUG ("%s", __FUNCTION__); + + mulawenc = gst_check_setup_element ("mulawenc"); + + mysrcpad = gst_check_setup_src_pad (mulawenc, &srctemplate); + mysinkpad = gst_check_setup_sink_pad (mulawenc, &sinktemplate); + + gst_pad_set_active (mysrcpad, TRUE); + gst_pad_set_active (mysinkpad, TRUE); + + gst_pad_set_caps (mysrcpad, src_caps); + gst_caps_unref (src_caps); +} + +static void +buffer_unref (void *buffer, void *user_data) +{ + gst_buffer_unref (GST_BUFFER (buffer)); +} + +static void +mulawenc_teardown (void) +{ + /* free encoded buffers */ + g_list_foreach (buffers, buffer_unref, NULL); + g_list_free (buffers); + buffers = NULL; + + gst_pad_set_active (mysrcpad, FALSE); + gst_pad_set_active (mysinkpad, FALSE); + gst_object_unref (mysrcpad); + gst_object_unref (mysinkpad); + gst_check_teardown_src_pad (mulawenc); + gst_check_teardown_sink_pad (mulawenc); + gst_check_teardown_element (mulawenc); + mulawenc = NULL; +} + +static gboolean +check_for_maximum_bitrate (GstPad * pad, GstEvent ** eventp, gpointer user_data) +{ + gboolean *found_maximum_bitrate = (gboolean *) user_data; + GstEvent *event = *eventp; + + if (event->type == GST_EVENT_TAG) { + GstTagList *taglist = NULL; + guint value = 0; + gst_event_parse_tag (event, &taglist); + + fail_unless (taglist != NULL); + + fail_unless (gst_tag_list_get_uint (taglist, GST_TAG_MAXIMUM_BITRATE, + &value)); + + /* bitrate needs to be exactly sample rate * channels * 8 */ + fail_unless (value == 8000 * 1 * 8); + + *found_maximum_bitrate = TRUE; + } + + return TRUE; +} + +GST_START_TEST (test_one_buffer) +{ + GstBuffer *buffer; + int buf_size = 4096; + unsigned char *dp; + + fail_unless (gst_element_set_state (mulawenc, GST_STATE_PLAYING) == + GST_STATE_CHANGE_SUCCESS, "could not change state to playing"); + + buffer = gst_buffer_new (); + dp = g_malloc (buf_size); + gst_buffer_append_memory (buffer, + gst_memory_new_wrapped (0, dp, buf_size, 0, buf_size, dp, g_free)); + ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1); + + fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); + + fail_unless (g_list_length (buffers) == 1); + fail_unless (gst_buffer_get_size (GST_BUFFER (g_list_first (buffers)->data))); +} + +GST_END_TEST; + +GST_START_TEST (test_tags) +{ + GstBuffer *buffer; + int buf_size = 4096; + unsigned char *dp; + gboolean found_maximum_bitrate = FALSE; + + fail_unless (gst_element_set_state (mulawenc, GST_STATE_PLAYING) == + GST_STATE_CHANGE_SUCCESS, "could not change state to playing"); + + buffer = gst_buffer_new (); + dp = g_malloc (buf_size); + gst_buffer_append_memory (buffer, + gst_memory_new_wrapped (0, dp, buf_size, 0, buf_size, dp, g_free)); + ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1); + + fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); + gst_pad_sticky_events_foreach (mysinkpad, check_for_maximum_bitrate, + &found_maximum_bitrate); + fail_unless (found_maximum_bitrate); +} + +GST_END_TEST; + +static Suite * +mulawenc_suite (void) +{ + Suite *s = suite_create ("mulawenc"); + TCase *tc_chain = tcase_create ("mulawenc"); + + tcase_add_checked_fixture (tc_chain, mulawenc_setup, mulawenc_teardown); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_one_buffer); + tcase_add_test (tc_chain, test_tags); + return s; +} + +GST_CHECK_MAIN (mulawenc)