| /* GStreamer FAAC (Free AAC Encoder) plugin |
| * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> |
| * Copyright (C) 2009 Mark Nauwelaerts <mnauw@users.sourceforge.net> |
| * |
| * 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. |
| */ |
| |
| /** |
| * SECTION:element-faac |
| * @see_also: faad |
| * |
| * faac encodes raw audio to AAC (MPEG-4 part 3) streams. |
| * |
| * <refsect2> |
| * <title>Example launch line</title> |
| * |[ |
| * gst-launch-1.0 audiotestsrc wave=sine num-buffers=100 ! audioconvert ! faac ! matroskamux ! filesink location=sine.mkv |
| * ]| Encode a sine beep as aac and write to matroska container. |
| * </refsect2> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <gst/audio/audio.h> |
| #include <gst/pbutils/codec-utils.h> |
| |
| #include "gstfaac.h" |
| |
| #define SAMPLE_RATES " 8000, " \ |
| "11025, " \ |
| "12000, " \ |
| "16000, " \ |
| "22050, " \ |
| "24000, " \ |
| "32000, " \ |
| "44100, " \ |
| "48000, " \ |
| "64000, " \ |
| "88200, " \ |
| "96000" |
| |
| /* these don't seem to work? */ |
| #if 0 |
| "audio/x-raw-int, " |
| "endianness = (int) BYTE_ORDER, " |
| "signed = (boolean) true, " |
| "width = (int) 32, " |
| "depth = (int) { 24, 32 }, " |
| "rate = (int) [ 8000, 96000], " |
| "channels = (int) [ 1, 6]; " |
| "audio/x-raw-float, " |
| "endianness = (int) BYTE_ORDER, " |
| "width = (int) 32, " |
| "rate = (int) [ 8000, 96000], " "channels = (int) [ 1, 6]" |
| #endif |
| #define SRC_CAPS \ |
| "audio/mpeg, " \ |
| "mpegversion = (int) 4, " \ |
| "channels = (int) [ 1, 6 ], " \ |
| "rate = (int) {" SAMPLE_RATES "}, " \ |
| "stream-format = (string) { adts, raw }, " \ |
| "base-profile = (string) { main, lc, ssr, ltp }, " \ |
| "framed = (boolean) true; " \ |
| "audio/mpeg, " \ |
| "mpegversion = (int) 2, " \ |
| "channels = (int) [ 1, 6 ], " \ |
| "rate = (int) {" SAMPLE_RATES "}, " \ |
| "stream-format = (string) { adts, raw }, " \ |
| "profile = (string) { main, lc }," \ |
| "framed = (boolean) true; " |
| static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (SRC_CAPS)); |
| |
| enum |
| { |
| PROP_0, |
| PROP_QUALITY, |
| PROP_BITRATE, |
| PROP_RATE_CONTROL, |
| PROP_PROFILE, |
| PROP_TNS, |
| PROP_MIDSIDE, |
| PROP_SHORTCTL |
| }; |
| |
| enum |
| { |
| VBR = 1, |
| ABR |
| }; |
| |
| static void gst_faac_set_property (GObject * object, |
| guint prop_id, const GValue * value, GParamSpec * pspec); |
| static void gst_faac_get_property (GObject * object, |
| guint prop_id, GValue * value, GParamSpec * pspec); |
| |
| static GstCaps *gst_faac_enc_generate_sink_caps (void); |
| static gboolean gst_faac_configure_source_pad (GstFaac * faac, |
| GstAudioInfo * info); |
| |
| static gboolean gst_faac_stop (GstAudioEncoder * enc); |
| static gboolean gst_faac_set_format (GstAudioEncoder * enc, |
| GstAudioInfo * info); |
| static GstFlowReturn gst_faac_handle_frame (GstAudioEncoder * enc, |
| GstBuffer * in_buf); |
| |
| GST_DEBUG_CATEGORY_STATIC (faac_debug); |
| #define GST_CAT_DEFAULT faac_debug |
| |
| #define FAAC_DEFAULT_QUALITY 100 |
| #define FAAC_DEFAULT_BITRATE 128 * 1000 |
| #define FAAC_DEFAULT_RATE_CONTROL VBR |
| #define FAAC_DEFAULT_TNS FALSE |
| #define FAAC_DEFAULT_MIDSIDE TRUE |
| #define FAAC_DEFAULT_SHORTCTL SHORTCTL_NORMAL |
| |
| #define gst_faac_parent_class parent_class |
| G_DEFINE_TYPE (GstFaac, gst_faac, GST_TYPE_AUDIO_ENCODER); |
| |
| #define GST_TYPE_FAAC_RATE_CONTROL (gst_faac_brtype_get_type ()) |
| static GType |
| gst_faac_brtype_get_type (void) |
| { |
| static GType gst_faac_brtype_type = 0; |
| |
| if (!gst_faac_brtype_type) { |
| static const GEnumValue gst_faac_brtype[] = { |
| {VBR, "VBR", "VBR encoding"}, |
| {ABR, "ABR", "ABR encoding"}, |
| {0, NULL, NULL}, |
| }; |
| |
| gst_faac_brtype_type = g_enum_register_static ("GstFaacBrtype", |
| gst_faac_brtype); |
| } |
| |
| return gst_faac_brtype_type; |
| } |
| |
| #define GST_TYPE_FAAC_SHORTCTL (gst_faac_shortctl_get_type ()) |
| static GType |
| gst_faac_shortctl_get_type (void) |
| { |
| static GType gst_faac_shortctl_type = 0; |
| |
| if (!gst_faac_shortctl_type) { |
| static const GEnumValue gst_faac_shortctl[] = { |
| {SHORTCTL_NORMAL, "SHORTCTL_NORMAL", "Normal block type"}, |
| {SHORTCTL_NOSHORT, "SHORTCTL_NOSHORT", "No short blocks"}, |
| {SHORTCTL_NOLONG, "SHORTCTL_NOLONG", "No long blocks"}, |
| {0, NULL, NULL}, |
| }; |
| |
| gst_faac_shortctl_type = g_enum_register_static ("GstFaacShortCtl", |
| gst_faac_shortctl); |
| } |
| |
| return gst_faac_shortctl_type; |
| } |
| |
| static void |
| gst_faac_class_init (GstFaacClass * klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); |
| GstAudioEncoderClass *base_class = GST_AUDIO_ENCODER_CLASS (klass); |
| GstCaps *sink_caps; |
| GstPadTemplate *sink_templ; |
| |
| gobject_class->set_property = gst_faac_set_property; |
| gobject_class->get_property = gst_faac_get_property; |
| |
| GST_DEBUG_CATEGORY_INIT (faac_debug, "faac", 0, "AAC encoding"); |
| |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&src_template)); |
| |
| sink_caps = gst_faac_enc_generate_sink_caps (); |
| sink_templ = gst_pad_template_new ("sink", |
| GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps); |
| gst_element_class_add_pad_template (gstelement_class, sink_templ); |
| gst_caps_unref (sink_caps); |
| |
| gst_element_class_set_static_metadata (gstelement_class, "AAC audio encoder", |
| "Codec/Encoder/Audio", |
| "Free MPEG-2/4 AAC encoder", |
| "Ronald Bultje <rbultje@ronald.bitfreak.net>"); |
| |
| base_class->stop = GST_DEBUG_FUNCPTR (gst_faac_stop); |
| base_class->set_format = GST_DEBUG_FUNCPTR (gst_faac_set_format); |
| base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_faac_handle_frame); |
| |
| /* properties */ |
| g_object_class_install_property (gobject_class, PROP_QUALITY, |
| g_param_spec_int ("quality", "Quality (%)", |
| "Variable bitrate (VBR) quantizer quality in %", 1, 1000, |
| FAAC_DEFAULT_QUALITY, |
| G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_BITRATE, |
| g_param_spec_int ("bitrate", "Bitrate (bps)", |
| "Average Bitrate (ABR) in bits/sec", 8 * 1000, 320 * 1000, |
| FAAC_DEFAULT_BITRATE, |
| G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_RATE_CONTROL, |
| g_param_spec_enum ("rate-control", "Rate Control (ABR/VBR)", |
| "Encoding bitrate type (VBR/ABR)", GST_TYPE_FAAC_RATE_CONTROL, |
| FAAC_DEFAULT_RATE_CONTROL, |
| G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_TNS, |
| g_param_spec_boolean ("tns", "TNS", "Use temporal noise shaping", |
| FAAC_DEFAULT_TNS, |
| G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_MIDSIDE, |
| g_param_spec_boolean ("midside", "Midside", "Allow mid/side encoding", |
| FAAC_DEFAULT_MIDSIDE, |
| G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_SHORTCTL, |
| g_param_spec_enum ("shortctl", "Block type", |
| "Block type encorcing", |
| GST_TYPE_FAAC_SHORTCTL, FAAC_DEFAULT_SHORTCTL, |
| G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| } |
| |
| static void |
| gst_faac_init (GstFaac * faac) |
| { |
| GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (faac)); |
| } |
| |
| static void |
| gst_faac_close_encoder (GstFaac * faac) |
| { |
| if (faac->handle) |
| faacEncClose (faac->handle); |
| faac->handle = NULL; |
| } |
| |
| static gboolean |
| gst_faac_stop (GstAudioEncoder * enc) |
| { |
| GstFaac *faac = GST_FAAC (enc); |
| |
| GST_DEBUG_OBJECT (faac, "stop"); |
| gst_faac_close_encoder (faac); |
| return TRUE; |
| } |
| |
| static const GstAudioChannelPosition aac_channel_positions[][8] = { |
| {GST_AUDIO_CHANNEL_POSITION_MONO}, |
| {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, |
| { |
| GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, |
| }, |
| { |
| GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, |
| GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, |
| { |
| GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, |
| GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, |
| GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, |
| { |
| GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
| GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, |
| GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, |
| GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, |
| GST_AUDIO_CHANNEL_POSITION_LFE1} |
| }; |
| |
| static GstCaps * |
| gst_faac_enc_generate_sink_caps (void) |
| { |
| GstCaps *caps = gst_caps_new_empty (); |
| GstStructure *s, *t; |
| gint i, c; |
| static const int rates[] = { |
| 8000, 11025, 12000, 16000, 22050, 24000, |
| 32000, 44100, 48000, 64000, 88200, 96000 |
| }; |
| GValue rates_arr = { 0, }; |
| GValue tmp_v = { 0, }; |
| |
| g_value_init (&rates_arr, GST_TYPE_LIST); |
| g_value_init (&tmp_v, G_TYPE_INT); |
| for (i = 0; i < G_N_ELEMENTS (rates); i++) { |
| g_value_set_int (&tmp_v, rates[i]); |
| gst_value_list_append_value (&rates_arr, &tmp_v); |
| } |
| g_value_unset (&tmp_v); |
| |
| s = gst_structure_new ("audio/x-raw", |
| "format", G_TYPE_STRING, GST_AUDIO_NE (S16), |
| "layout", G_TYPE_STRING, "interleaved", NULL); |
| gst_structure_set_value (s, "rate", &rates_arr); |
| |
| t = gst_structure_copy (s); |
| gst_structure_set (t, "channels", G_TYPE_INT, 1, NULL); |
| gst_caps_append_structure (caps, t); |
| |
| for (i = 2; i <= 6; i++) { |
| guint64 channel_mask = 0; |
| t = gst_structure_copy (s); |
| |
| gst_structure_set (t, "channels", G_TYPE_INT, i, NULL); |
| for (c = 0; c < i; c++) |
| channel_mask |= G_GUINT64_CONSTANT (1) << aac_channel_positions[i - 1][c]; |
| |
| gst_structure_set (t, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); |
| gst_caps_append_structure (caps, t); |
| } |
| gst_structure_free (s); |
| g_value_unset (&rates_arr); |
| |
| GST_DEBUG ("Generated sinkcaps: %" GST_PTR_FORMAT, caps); |
| return caps; |
| } |
| |
| static void |
| gst_faac_set_tags (GstFaac * faac) |
| { |
| GstTagList *taglist; |
| |
| /* create a taglist and add a bitrate tag to it */ |
| taglist = gst_tag_list_new_empty (); |
| gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, |
| GST_TAG_BITRATE, faac->bitrate, NULL); |
| |
| gst_audio_encoder_merge_tags (GST_AUDIO_ENCODER (faac), taglist, |
| GST_TAG_MERGE_REPLACE); |
| |
| gst_tag_list_unref (taglist); |
| } |
| |
| static gboolean |
| gst_faac_set_format (GstAudioEncoder * enc, GstAudioInfo * info) |
| { |
| GstFaac *faac = GST_FAAC (enc); |
| gint width; |
| gulong fmt = 0; |
| gboolean result = FALSE; |
| |
| /* base class takes care */ |
| width = GST_AUDIO_INFO_WIDTH (info); |
| |
| if (GST_AUDIO_INFO_IS_INTEGER (info)) { |
| switch (width) { |
| case 16: |
| fmt = FAAC_INPUT_16BIT; |
| break; |
| case 24: |
| case 32: |
| fmt = FAAC_INPUT_32BIT; |
| break; |
| default: |
| g_return_val_if_reached (FALSE); |
| } |
| } else { |
| fmt = FAAC_INPUT_FLOAT; |
| } |
| |
| faac->format = fmt; |
| |
| /* finish up */ |
| result = gst_faac_configure_source_pad (faac, info); |
| if (!result) |
| goto done; |
| |
| gst_faac_set_tags (faac); |
| |
| /* report needs to base class */ |
| gst_audio_encoder_set_frame_samples_min (enc, faac->samples); |
| gst_audio_encoder_set_frame_samples_max (enc, faac->samples); |
| gst_audio_encoder_set_frame_max (enc, 1); |
| |
| done: |
| return result; |
| } |
| |
| /* check downstream caps to configure format */ |
| static void |
| gst_faac_negotiate (GstFaac * faac) |
| { |
| GstCaps *caps; |
| |
| /* default setup */ |
| faac->profile = LOW; |
| faac->mpegversion = 4; |
| faac->outputformat = 0; |
| |
| caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (faac)); |
| |
| GST_DEBUG_OBJECT (faac, "allowed caps: %" GST_PTR_FORMAT, caps); |
| |
| if (caps && gst_caps_get_size (caps) > 0) { |
| GstStructure *s = gst_caps_get_structure (caps, 0); |
| const gchar *str = NULL; |
| gint i = 4; |
| |
| if ((str = gst_structure_get_string (s, "stream-format"))) { |
| if (strcmp (str, "adts") == 0) { |
| GST_DEBUG_OBJECT (faac, "use ADTS format for output"); |
| faac->outputformat = 1; |
| } else if (strcmp (str, "raw") == 0) { |
| GST_DEBUG_OBJECT (faac, "use RAW format for output"); |
| faac->outputformat = 0; |
| } else { |
| GST_DEBUG_OBJECT (faac, "unknown stream-format: %s", str); |
| faac->outputformat = 0; |
| } |
| } |
| |
| if ((str = gst_structure_get_string (s, "profile"))) { |
| if (strcmp (str, "main") == 0) { |
| faac->profile = MAIN; |
| } else if (strcmp (str, "lc") == 0) { |
| faac->profile = LOW; |
| } else if (strcmp (str, "ssr") == 0) { |
| faac->profile = SSR; |
| } else if (strcmp (str, "ltp") == 0) { |
| faac->profile = LTP; |
| } else { |
| faac->profile = LOW; |
| } |
| } |
| |
| if (!gst_structure_get_int (s, "mpegversion", &i) || i == 4) { |
| faac->mpegversion = 4; |
| } else { |
| faac->mpegversion = 2; |
| } |
| } |
| |
| if (caps) |
| gst_caps_unref (caps); |
| } |
| |
| static gboolean |
| gst_faac_open_encoder (GstFaac * faac, GstAudioInfo * info) |
| { |
| faacEncHandle *handle; |
| faacEncConfiguration *conf; |
| guint maxbitrate; |
| gulong samples, bytes; |
| |
| g_return_val_if_fail (info->rate != 0 && info->channels != 0, FALSE); |
| |
| /* clean up in case of re-configure */ |
| gst_faac_close_encoder (faac); |
| |
| if (!(handle = faacEncOpen (info->rate, info->channels, &samples, &bytes))) |
| goto setup_failed; |
| |
| /* mind channel count */ |
| samples /= info->channels; |
| |
| /* record */ |
| faac->handle = handle; |
| faac->samples = samples; |
| faac->bytes = bytes; |
| |
| GST_DEBUG_OBJECT (faac, "faac needs samples %d, output size %d", |
| faac->samples, faac->bytes); |
| |
| /* we negotiated caps update current configuration */ |
| conf = faacEncGetCurrentConfiguration (faac->handle); |
| conf->mpegVersion = (faac->mpegversion == 4) ? MPEG4 : MPEG2; |
| conf->aacObjectType = faac->profile; |
| conf->allowMidside = faac->midside; |
| conf->useLfe = 0; |
| conf->useTns = faac->tns; |
| |
| if (faac->brtype == VBR) { |
| conf->quantqual = faac->quality; |
| } else if (faac->brtype == ABR) { |
| conf->bitRate = faac->bitrate / info->channels; |
| } |
| |
| conf->inputFormat = faac->format; |
| conf->outputFormat = faac->outputformat; |
| conf->shortctl = faac->shortctl; |
| |
| /* check, warn and correct if the max bitrate for the given samplerate is |
| * exceeded. Maximum of 6144 bit for a channel */ |
| maxbitrate = |
| (unsigned int) (6144.0 * (double) info->rate / (double) 1024.0 + .5); |
| if (conf->bitRate > maxbitrate) { |
| GST_ELEMENT_WARNING (faac, RESOURCE, SETTINGS, (NULL), |
| ("bitrate %lu exceeds maximum allowed bitrate of %u for samplerate %d. " |
| "Setting bitrate to %u", conf->bitRate, maxbitrate, |
| info->rate, maxbitrate)); |
| conf->bitRate = maxbitrate; |
| } |
| |
| /* default 0 to start with, libfaac chooses based on bitrate */ |
| conf->bandWidth = 0; |
| |
| if (!faacEncSetConfiguration (faac->handle, conf)) |
| goto setup_failed; |
| |
| /* let's see what really happened, |
| * note that this may not really match desired rate */ |
| GST_DEBUG_OBJECT (faac, "average bitrate: %lu kbps", |
| (conf->bitRate + 500) / 1000 * info->channels); |
| GST_DEBUG_OBJECT (faac, "quantization quality: %ld", conf->quantqual); |
| GST_DEBUG_OBJECT (faac, "bandwidth: %d Hz", conf->bandWidth); |
| |
| return TRUE; |
| |
| /* ERRORS */ |
| setup_failed: |
| { |
| GST_ELEMENT_ERROR (faac, LIBRARY, SETTINGS, (NULL), (NULL)); |
| return FALSE; |
| } |
| } |
| |
| static gboolean |
| gst_faac_configure_source_pad (GstFaac * faac, GstAudioInfo * info) |
| { |
| GstCaps *srccaps; |
| gboolean ret; |
| |
| /* negotiate stream format */ |
| gst_faac_negotiate (faac); |
| |
| if (!gst_faac_open_encoder (faac, info)) |
| goto set_failed; |
| |
| /* now create a caps for it all */ |
| srccaps = gst_caps_new_simple ("audio/mpeg", |
| "mpegversion", G_TYPE_INT, faac->mpegversion, |
| "channels", G_TYPE_INT, info->channels, |
| "rate", G_TYPE_INT, info->rate, |
| "stream-format", G_TYPE_STRING, (faac->outputformat ? "adts" : "raw"), |
| "framed", G_TYPE_BOOLEAN, TRUE, NULL); |
| |
| /* DecoderSpecificInfo is only available for mpegversion=4 */ |
| if (faac->mpegversion == 4) { |
| guint8 *config = NULL; |
| gulong config_len = 0; |
| |
| /* get the config string */ |
| GST_DEBUG_OBJECT (faac, "retrieving decoder info"); |
| faacEncGetDecoderSpecificInfo (faac->handle, &config, &config_len); |
| |
| if (!gst_codec_utils_aac_caps_set_level_and_profile (srccaps, config, |
| config_len)) { |
| free (config); |
| gst_caps_unref (srccaps); |
| goto invalid_codec_data; |
| } |
| |
| if (!faac->outputformat) { |
| GstBuffer *codec_data; |
| |
| /* copy it into a buffer */ |
| codec_data = gst_buffer_new_and_alloc (config_len); |
| gst_buffer_fill (codec_data, 0, config, config_len); |
| |
| /* add to caps */ |
| gst_caps_set_simple (srccaps, |
| "codec_data", GST_TYPE_BUFFER, codec_data, NULL); |
| |
| gst_buffer_unref (codec_data); |
| } |
| |
| free (config); |
| } else { |
| const gchar *profile; |
| |
| /* Add least add the profile to the caps */ |
| switch (faac->profile) { |
| case MAIN: |
| profile = "main"; |
| break; |
| case LTP: |
| profile = "ltp"; |
| break; |
| case SSR: |
| profile = "ssr"; |
| break; |
| case LOW: |
| default: |
| profile = "lc"; |
| break; |
| } |
| gst_caps_set_simple (srccaps, "profile", G_TYPE_STRING, profile, NULL); |
| /* FIXME: How to get the profile for mpegversion==2? */ |
| } |
| |
| GST_DEBUG_OBJECT (faac, "src pad caps: %" GST_PTR_FORMAT, srccaps); |
| |
| ret = gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (faac), srccaps); |
| gst_caps_unref (srccaps); |
| |
| return ret; |
| |
| /* ERROR */ |
| set_failed: |
| { |
| GST_WARNING_OBJECT (faac, "Faac doesn't support the current configuration"); |
| return FALSE; |
| } |
| invalid_codec_data: |
| { |
| GST_ERROR_OBJECT (faac, "Invalid codec data"); |
| return FALSE; |
| } |
| } |
| |
| static GstFlowReturn |
| gst_faac_handle_frame (GstAudioEncoder * enc, GstBuffer * in_buf) |
| { |
| GstFaac *faac = GST_FAAC (enc); |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstBuffer *out_buf; |
| gsize size, ret_size; |
| int enc_ret; |
| GstMapInfo map, omap; |
| guint8 *data; |
| GstAudioInfo *info = |
| gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (faac)); |
| |
| out_buf = gst_buffer_new_and_alloc (faac->bytes); |
| gst_buffer_map (out_buf, &omap, GST_MAP_WRITE); |
| |
| if (G_LIKELY (in_buf)) { |
| if (memcmp (info->position, aac_channel_positions[info->channels - 1], |
| sizeof (GstAudioChannelPosition) * info->channels) != 0) { |
| in_buf = gst_buffer_make_writable (in_buf); |
| gst_audio_buffer_reorder_channels (in_buf, info->finfo->format, |
| info->channels, info->position, |
| aac_channel_positions[info->channels - 1]); |
| } |
| gst_buffer_map (in_buf, &map, GST_MAP_READ); |
| data = map.data; |
| size = map.size; |
| } else { |
| data = NULL; |
| size = 0; |
| } |
| |
| if (G_UNLIKELY ((enc_ret = faacEncEncode (faac->handle, (gint32 *) data, |
| size / (info->finfo->width / 8), omap.data, omap.size)) < 0)) |
| goto encode_failed; |
| ret_size = enc_ret; |
| |
| if (in_buf) |
| gst_buffer_unmap (in_buf, &map); |
| |
| GST_LOG_OBJECT (faac, "encoder return: %" G_GSIZE_FORMAT, ret_size); |
| |
| if (ret_size > 0) { |
| gst_buffer_unmap (out_buf, &omap); |
| gst_buffer_resize (out_buf, 0, ret_size); |
| ret = gst_audio_encoder_finish_frame (enc, out_buf, faac->samples); |
| } else { |
| gst_buffer_unmap (out_buf, &omap); |
| gst_buffer_unref (out_buf); |
| /* re-create encoder after final flush */ |
| if (!in_buf) { |
| GST_DEBUG_OBJECT (faac, "flushed; recreating encoder"); |
| gst_faac_close_encoder (faac); |
| if (!gst_faac_open_encoder (faac, gst_audio_encoder_get_audio_info (enc))) |
| ret = GST_FLOW_ERROR; |
| } |
| } |
| |
| return ret; |
| |
| /* ERRORS */ |
| encode_failed: |
| { |
| GST_ELEMENT_ERROR (faac, LIBRARY, ENCODE, (NULL), (NULL)); |
| if (in_buf) |
| gst_buffer_unmap (in_buf, &map); |
| gst_buffer_unmap (out_buf, &omap); |
| gst_buffer_unref (out_buf); |
| return GST_FLOW_ERROR; |
| } |
| } |
| |
| static void |
| gst_faac_set_property (GObject * object, |
| guint prop_id, const GValue * value, GParamSpec * pspec) |
| { |
| GstFaac *faac = GST_FAAC (object); |
| |
| GST_OBJECT_LOCK (faac); |
| |
| switch (prop_id) { |
| case PROP_QUALITY: |
| faac->quality = g_value_get_int (value); |
| break; |
| case PROP_BITRATE: |
| faac->bitrate = g_value_get_int (value); |
| break; |
| case PROP_RATE_CONTROL: |
| faac->brtype = g_value_get_enum (value); |
| break; |
| case PROP_TNS: |
| faac->tns = g_value_get_boolean (value); |
| break; |
| case PROP_MIDSIDE: |
| faac->midside = g_value_get_boolean (value); |
| break; |
| case PROP_SHORTCTL: |
| faac->shortctl = g_value_get_enum (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| |
| GST_OBJECT_UNLOCK (faac); |
| } |
| |
| static void |
| gst_faac_get_property (GObject * object, |
| guint prop_id, GValue * value, GParamSpec * pspec) |
| { |
| GstFaac *faac = GST_FAAC (object); |
| |
| GST_OBJECT_LOCK (faac); |
| |
| switch (prop_id) { |
| case PROP_QUALITY: |
| g_value_set_int (value, faac->quality); |
| break; |
| case PROP_BITRATE: |
| g_value_set_int (value, faac->bitrate); |
| break; |
| case PROP_RATE_CONTROL: |
| g_value_set_enum (value, faac->brtype); |
| break; |
| case PROP_TNS: |
| g_value_set_boolean (value, faac->tns); |
| break; |
| case PROP_MIDSIDE: |
| g_value_set_boolean (value, faac->midside); |
| break; |
| case PROP_SHORTCTL: |
| g_value_set_enum (value, faac->shortctl); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| |
| GST_OBJECT_UNLOCK (faac); |
| } |
| |
| static gboolean |
| plugin_init (GstPlugin * plugin) |
| { |
| return gst_element_register (plugin, "faac", GST_RANK_SECONDARY, |
| GST_TYPE_FAAC); |
| } |
| |
| GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
| GST_VERSION_MINOR, |
| faac, |
| "Free AAC Encoder (FAAC)", |
| plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |