1 /* GStreamer FAAC (Free AAC Encoder) plugin
2  * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  * Copyright (C) 2009 Mark Nauwelaerts <mnauw@users.sourceforge.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-faac
23  * @title: faac
24  * @see_also: faad
25  *
26  * faac encodes raw audio to AAC (MPEG-4 part 3) streams.
27  *
28  * ## Example launch line
29  * |[
30  * gst-launch-1.0 audiotestsrc wave=sine num-buffers=100 ! audioconvert ! faac ! matroskamux ! filesink location=sine.mkv
31  * ]| Encode a sine beep as aac and write to matroska container.
32  *
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <gst/audio/audio.h>
42 #include <gst/pbutils/codec-utils.h>
43 
44 #include "gstfaac.h"
45 
46 #define SAMPLE_RATES " 8000, " \
47                     "11025, " \
48                     "12000, " \
49                     "16000, " \
50                     "22050, " \
51                     "24000, " \
52                     "32000, " \
53                     "44100, " \
54                     "48000, " \
55                     "64000, " \
56                     "88200, " \
57                     "96000"
58 
59 /* these don't seem to work? */
60 #if 0
61 "audio/x-raw-int, "
62     "endianness = (int) BYTE_ORDER, "
63     "signed = (boolean) true, "
64     "width = (int) 32, "
65     "depth = (int) { 24, 32 }, "
66     "rate = (int) [ 8000, 96000], "
67     "channels = (int) [ 1, 6]; "
68     "audio/x-raw-float, "
69     "endianness = (int) BYTE_ORDER, "
70     "width = (int) 32, "
71     "rate = (int) [ 8000, 96000], " "channels = (int) [ 1, 6]"
72 #endif
73 #define SRC_CAPS \
74     "audio/mpeg, "                     \
75     "mpegversion = (int) 4, "   \
76     "channels = (int) [ 1, 6 ], "      \
77     "rate = (int) {" SAMPLE_RATES "}, "   \
78     "stream-format = (string) { adts, raw }, " \
79     "base-profile = (string) { main, lc, ssr, ltp }, " \
80     "framed = (boolean) true; " \
81     "audio/mpeg, "                     \
82     "mpegversion = (int) 2, "   \
83     "channels = (int) [ 1, 6 ], "      \
84     "rate = (int) {" SAMPLE_RATES "}, "   \
85     "stream-format = (string) { adts, raw }, " \
86     "profile = (string) { main, lc }," \
87     "framed = (boolean) true; "
88 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
89     GST_PAD_SRC,
90     GST_PAD_ALWAYS,
91     GST_STATIC_CAPS (SRC_CAPS));
92 
93 enum
94 {
95   PROP_0,
96   PROP_QUALITY,
97   PROP_BITRATE,
98   PROP_RATE_CONTROL,
99   PROP_PROFILE,
100   PROP_TNS,
101   PROP_MIDSIDE,
102   PROP_SHORTCTL
103 };
104 
105 enum
106 {
107   VBR = 1,
108   ABR
109 };
110 
111 static void gst_faac_set_property (GObject * object,
112     guint prop_id, const GValue * value, GParamSpec * pspec);
113 static void gst_faac_get_property (GObject * object,
114     guint prop_id, GValue * value, GParamSpec * pspec);
115 
116 static GstCaps *gst_faac_enc_generate_sink_caps (void);
117 static gboolean gst_faac_configure_source_pad (GstFaac * faac,
118     GstAudioInfo * info);
119 
120 static gboolean gst_faac_stop (GstAudioEncoder * enc);
121 static gboolean gst_faac_set_format (GstAudioEncoder * enc,
122     GstAudioInfo * info);
123 static GstFlowReturn gst_faac_handle_frame (GstAudioEncoder * enc,
124     GstBuffer * in_buf);
125 
126 GST_DEBUG_CATEGORY_STATIC (faac_debug);
127 #define GST_CAT_DEFAULT faac_debug
128 
129 #define FAAC_DEFAULT_QUALITY      100
130 #define FAAC_DEFAULT_BITRATE      128 * 1000
131 #define FAAC_DEFAULT_RATE_CONTROL VBR
132 #define FAAC_DEFAULT_TNS          FALSE
133 #define FAAC_DEFAULT_MIDSIDE      TRUE
134 #define FAAC_DEFAULT_SHORTCTL     SHORTCTL_NORMAL
135 
136 #define gst_faac_parent_class parent_class
137 G_DEFINE_TYPE (GstFaac, gst_faac, GST_TYPE_AUDIO_ENCODER);
138 
139 #define GST_TYPE_FAAC_RATE_CONTROL (gst_faac_brtype_get_type ())
140 static GType
gst_faac_brtype_get_type(void)141 gst_faac_brtype_get_type (void)
142 {
143   static GType gst_faac_brtype_type = 0;
144 
145   if (!gst_faac_brtype_type) {
146     static const GEnumValue gst_faac_brtype[] = {
147       {VBR, "VBR", "VBR encoding"},
148       {ABR, "ABR", "ABR encoding"},
149       {0, NULL, NULL},
150     };
151 
152     gst_faac_brtype_type = g_enum_register_static ("GstFaacBrtype",
153         gst_faac_brtype);
154   }
155 
156   return gst_faac_brtype_type;
157 }
158 
159 #define GST_TYPE_FAAC_SHORTCTL (gst_faac_shortctl_get_type ())
160 static GType
gst_faac_shortctl_get_type(void)161 gst_faac_shortctl_get_type (void)
162 {
163   static GType gst_faac_shortctl_type = 0;
164 
165   if (!gst_faac_shortctl_type) {
166     static const GEnumValue gst_faac_shortctl[] = {
167       {SHORTCTL_NORMAL, "SHORTCTL_NORMAL", "Normal block type"},
168       {SHORTCTL_NOSHORT, "SHORTCTL_NOSHORT", "No short blocks"},
169       {SHORTCTL_NOLONG, "SHORTCTL_NOLONG", "No long blocks"},
170       {0, NULL, NULL},
171     };
172 
173     gst_faac_shortctl_type = g_enum_register_static ("GstFaacShortCtl",
174         gst_faac_shortctl);
175   }
176 
177   return gst_faac_shortctl_type;
178 }
179 
180 static void
gst_faac_class_init(GstFaacClass * klass)181 gst_faac_class_init (GstFaacClass * klass)
182 {
183   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
184   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
185   GstAudioEncoderClass *base_class = GST_AUDIO_ENCODER_CLASS (klass);
186   GstCaps *sink_caps;
187   GstPadTemplate *sink_templ;
188 
189   gobject_class->set_property = gst_faac_set_property;
190   gobject_class->get_property = gst_faac_get_property;
191 
192   GST_DEBUG_CATEGORY_INIT (faac_debug, "faac", 0, "AAC encoding");
193 
194   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
195 
196   sink_caps = gst_faac_enc_generate_sink_caps ();
197   sink_templ = gst_pad_template_new ("sink",
198       GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps);
199   gst_element_class_add_pad_template (gstelement_class, sink_templ);
200   gst_caps_unref (sink_caps);
201 
202   gst_element_class_set_static_metadata (gstelement_class, "AAC audio encoder",
203       "Codec/Encoder/Audio",
204       "Free MPEG-2/4 AAC encoder",
205       "Ronald Bultje <rbultje@ronald.bitfreak.net>");
206 
207   base_class->stop = GST_DEBUG_FUNCPTR (gst_faac_stop);
208   base_class->set_format = GST_DEBUG_FUNCPTR (gst_faac_set_format);
209   base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_faac_handle_frame);
210 
211   /* properties */
212   g_object_class_install_property (gobject_class, PROP_QUALITY,
213       g_param_spec_int ("quality", "Quality (%)",
214           "Variable bitrate (VBR) quantizer quality in %", 1, 1000,
215           FAAC_DEFAULT_QUALITY,
216           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
217   g_object_class_install_property (gobject_class, PROP_BITRATE,
218       g_param_spec_int ("bitrate", "Bitrate (bps)",
219           "Average Bitrate (ABR) in bits/sec", 8 * 1000, 320 * 1000,
220           FAAC_DEFAULT_BITRATE,
221           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222   g_object_class_install_property (gobject_class, PROP_RATE_CONTROL,
223       g_param_spec_enum ("rate-control", "Rate Control (ABR/VBR)",
224           "Encoding bitrate type (VBR/ABR)", GST_TYPE_FAAC_RATE_CONTROL,
225           FAAC_DEFAULT_RATE_CONTROL,
226           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227   g_object_class_install_property (gobject_class, PROP_TNS,
228       g_param_spec_boolean ("tns", "TNS", "Use temporal noise shaping",
229           FAAC_DEFAULT_TNS,
230           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
231   g_object_class_install_property (gobject_class, PROP_MIDSIDE,
232       g_param_spec_boolean ("midside", "Midside", "Allow mid/side encoding",
233           FAAC_DEFAULT_MIDSIDE,
234           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
235   g_object_class_install_property (gobject_class, PROP_SHORTCTL,
236       g_param_spec_enum ("shortctl", "Block type",
237           "Block type encorcing",
238           GST_TYPE_FAAC_SHORTCTL, FAAC_DEFAULT_SHORTCTL,
239           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240 }
241 
242 static void
gst_faac_init(GstFaac * faac)243 gst_faac_init (GstFaac * faac)
244 {
245   GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (faac));
246 }
247 
248 static void
gst_faac_close_encoder(GstFaac * faac)249 gst_faac_close_encoder (GstFaac * faac)
250 {
251   if (faac->handle)
252     faacEncClose (faac->handle);
253   faac->handle = NULL;
254 }
255 
256 static gboolean
gst_faac_stop(GstAudioEncoder * enc)257 gst_faac_stop (GstAudioEncoder * enc)
258 {
259   GstFaac *faac = GST_FAAC (enc);
260 
261   GST_DEBUG_OBJECT (faac, "stop");
262   gst_faac_close_encoder (faac);
263   return TRUE;
264 }
265 
266 static const GstAudioChannelPosition aac_channel_positions[][8] = {
267   {GST_AUDIO_CHANNEL_POSITION_MONO},
268   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
269       GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
270   {
271         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
272         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
273         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
274       },
275   {
276         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
277         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
278         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
279       GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
280   {
281         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
282         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
283         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
284         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
285       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
286   {
287         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
288         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
289         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
290         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
291         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
292       GST_AUDIO_CHANNEL_POSITION_LFE1}
293 };
294 
295 static GstCaps *
gst_faac_enc_generate_sink_caps(void)296 gst_faac_enc_generate_sink_caps (void)
297 {
298   GstCaps *caps = gst_caps_new_empty ();
299   GstStructure *s, *t;
300   gint i, c;
301   static const int rates[] = {
302     8000, 11025, 12000, 16000, 22050, 24000,
303     32000, 44100, 48000, 64000, 88200, 96000
304   };
305   GValue rates_arr = { 0, };
306   GValue tmp_v = { 0, };
307 
308   g_value_init (&rates_arr, GST_TYPE_LIST);
309   g_value_init (&tmp_v, G_TYPE_INT);
310   for (i = 0; i < G_N_ELEMENTS (rates); i++) {
311     g_value_set_int (&tmp_v, rates[i]);
312     gst_value_list_append_value (&rates_arr, &tmp_v);
313   }
314   g_value_unset (&tmp_v);
315 
316   s = gst_structure_new ("audio/x-raw",
317       "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
318       "layout", G_TYPE_STRING, "interleaved", NULL);
319   gst_structure_set_value (s, "rate", &rates_arr);
320 
321   t = gst_structure_copy (s);
322   gst_structure_set (t, "channels", G_TYPE_INT, 1, NULL);
323   gst_caps_append_structure (caps, t);
324 
325   for (i = 2; i <= 6; i++) {
326     guint64 channel_mask = 0;
327     t = gst_structure_copy (s);
328 
329     gst_structure_set (t, "channels", G_TYPE_INT, i, NULL);
330     for (c = 0; c < i; c++)
331       channel_mask |= G_GUINT64_CONSTANT (1) << aac_channel_positions[i - 1][c];
332 
333     gst_structure_set (t, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
334     gst_caps_append_structure (caps, t);
335   }
336   gst_structure_free (s);
337   g_value_unset (&rates_arr);
338 
339   GST_DEBUG ("Generated sinkcaps: %" GST_PTR_FORMAT, caps);
340   return caps;
341 }
342 
343 static void
gst_faac_set_tags(GstFaac * faac)344 gst_faac_set_tags (GstFaac * faac)
345 {
346   GstTagList *taglist;
347 
348   /* create a taglist and add a bitrate tag to it */
349   taglist = gst_tag_list_new_empty ();
350   gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
351       GST_TAG_BITRATE, faac->bitrate, NULL);
352 
353   gst_audio_encoder_merge_tags (GST_AUDIO_ENCODER (faac), taglist,
354       GST_TAG_MERGE_REPLACE);
355 
356   gst_tag_list_unref (taglist);
357 }
358 
359 static gboolean
gst_faac_set_format(GstAudioEncoder * enc,GstAudioInfo * info)360 gst_faac_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
361 {
362   GstFaac *faac = GST_FAAC (enc);
363   gint width;
364   gulong fmt = 0;
365   gboolean result = FALSE;
366 
367   /* base class takes care */
368   width = GST_AUDIO_INFO_WIDTH (info);
369 
370   if (GST_AUDIO_INFO_IS_INTEGER (info)) {
371     switch (width) {
372       case 16:
373         fmt = FAAC_INPUT_16BIT;
374         break;
375       case 24:
376       case 32:
377         fmt = FAAC_INPUT_32BIT;
378         break;
379       default:
380         g_return_val_if_reached (FALSE);
381     }
382   } else {
383     fmt = FAAC_INPUT_FLOAT;
384   }
385 
386   faac->format = fmt;
387 
388   /* finish up */
389   result = gst_faac_configure_source_pad (faac, info);
390   if (!result)
391     goto done;
392 
393   gst_faac_set_tags (faac);
394 
395   /* report needs to base class */
396   gst_audio_encoder_set_frame_samples_min (enc, faac->samples);
397   gst_audio_encoder_set_frame_samples_max (enc, faac->samples);
398   gst_audio_encoder_set_frame_max (enc, 1);
399 
400 done:
401   return result;
402 }
403 
404 /* check downstream caps to configure format */
405 static void
gst_faac_negotiate(GstFaac * faac)406 gst_faac_negotiate (GstFaac * faac)
407 {
408   GstCaps *caps;
409 
410   /* default setup */
411   faac->profile = LOW;
412   faac->mpegversion = 4;
413   faac->outputformat = 0;
414 
415   caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (faac));
416 
417   GST_DEBUG_OBJECT (faac, "allowed caps: %" GST_PTR_FORMAT, caps);
418 
419   if (caps && gst_caps_get_size (caps) > 0) {
420     GstStructure *s = gst_caps_get_structure (caps, 0);
421     const gchar *str = NULL;
422     gint i = 4;
423 
424     if ((str = gst_structure_get_string (s, "stream-format"))) {
425       if (strcmp (str, "adts") == 0) {
426         GST_DEBUG_OBJECT (faac, "use ADTS format for output");
427         faac->outputformat = 1;
428       } else if (strcmp (str, "raw") == 0) {
429         GST_DEBUG_OBJECT (faac, "use RAW format for output");
430         faac->outputformat = 0;
431       } else {
432         GST_DEBUG_OBJECT (faac, "unknown stream-format: %s", str);
433         faac->outputformat = 0;
434       }
435     }
436 
437     if ((str = gst_structure_get_string (s, "profile"))) {
438       if (strcmp (str, "main") == 0) {
439         faac->profile = MAIN;
440       } else if (strcmp (str, "lc") == 0) {
441         faac->profile = LOW;
442       } else if (strcmp (str, "ssr") == 0) {
443         faac->profile = SSR;
444       } else if (strcmp (str, "ltp") == 0) {
445         faac->profile = LTP;
446       } else {
447         faac->profile = LOW;
448       }
449     }
450 
451     if (!gst_structure_get_int (s, "mpegversion", &i) || i == 4) {
452       faac->mpegversion = 4;
453     } else {
454       faac->mpegversion = 2;
455     }
456   }
457 
458   if (caps)
459     gst_caps_unref (caps);
460 }
461 
462 static gboolean
gst_faac_open_encoder(GstFaac * faac,GstAudioInfo * info)463 gst_faac_open_encoder (GstFaac * faac, GstAudioInfo * info)
464 {
465   faacEncHandle *handle;
466   faacEncConfiguration *conf;
467   guint maxbitrate;
468   gulong samples, bytes;
469 
470   g_return_val_if_fail (info->rate != 0 && info->channels != 0, FALSE);
471 
472   /* clean up in case of re-configure */
473   gst_faac_close_encoder (faac);
474 
475   if (!(handle = faacEncOpen (info->rate, info->channels, &samples, &bytes)))
476     goto setup_failed;
477 
478   /* mind channel count */
479   samples /= info->channels;
480 
481   /* record */
482   faac->handle = handle;
483   faac->samples = samples;
484   faac->bytes = bytes;
485 
486   GST_DEBUG_OBJECT (faac, "faac needs samples %d, output size %d",
487       faac->samples, faac->bytes);
488 
489   /* we negotiated caps update current configuration */
490   conf = faacEncGetCurrentConfiguration (faac->handle);
491   conf->mpegVersion = (faac->mpegversion == 4) ? MPEG4 : MPEG2;
492   conf->aacObjectType = faac->profile;
493   conf->allowMidside = faac->midside;
494   conf->useLfe = 0;
495   conf->useTns = faac->tns;
496 
497   if (faac->brtype == VBR) {
498     conf->quantqual = faac->quality;
499   } else if (faac->brtype == ABR) {
500     conf->bitRate = faac->bitrate / info->channels;
501   }
502 
503   conf->inputFormat = faac->format;
504   conf->outputFormat = faac->outputformat;
505   conf->shortctl = faac->shortctl;
506 
507   /* check, warn and correct if the max bitrate for the given samplerate is
508    * exceeded. Maximum of 6144 bit for a channel */
509   maxbitrate =
510       (unsigned int) (6144.0 * (double) info->rate / (double) 1024.0 + .5);
511   if (conf->bitRate > maxbitrate) {
512     GST_ELEMENT_WARNING (faac, RESOURCE, SETTINGS, (NULL),
513         ("bitrate %lu exceeds maximum allowed bitrate of %u for samplerate %d. "
514             "Setting bitrate to %u", conf->bitRate, maxbitrate,
515             info->rate, maxbitrate));
516     conf->bitRate = maxbitrate;
517   }
518 
519   /* default 0 to start with, libfaac chooses based on bitrate */
520   conf->bandWidth = 0;
521 
522   if (!faacEncSetConfiguration (faac->handle, conf))
523     goto setup_failed;
524 
525   /* let's see what really happened,
526    * note that this may not really match desired rate */
527   GST_DEBUG_OBJECT (faac, "average bitrate: %lu kbps",
528       (conf->bitRate + 500) / 1000 * info->channels);
529   GST_DEBUG_OBJECT (faac, "quantization quality: %ld", conf->quantqual);
530   GST_DEBUG_OBJECT (faac, "bandwidth: %d Hz", conf->bandWidth);
531 
532   return TRUE;
533 
534   /* ERRORS */
535 setup_failed:
536   {
537     GST_ELEMENT_ERROR (faac, LIBRARY, SETTINGS, (NULL), (NULL));
538     return FALSE;
539   }
540 }
541 
542 static gboolean
gst_faac_configure_source_pad(GstFaac * faac,GstAudioInfo * info)543 gst_faac_configure_source_pad (GstFaac * faac, GstAudioInfo * info)
544 {
545   GstCaps *srccaps;
546   gboolean ret;
547 
548   /* negotiate stream format */
549   gst_faac_negotiate (faac);
550 
551   if (!gst_faac_open_encoder (faac, info))
552     goto set_failed;
553 
554   /* now create a caps for it all */
555   srccaps = gst_caps_new_simple ("audio/mpeg",
556       "mpegversion", G_TYPE_INT, faac->mpegversion,
557       "channels", G_TYPE_INT, info->channels,
558       "rate", G_TYPE_INT, info->rate,
559       "stream-format", G_TYPE_STRING, (faac->outputformat ? "adts" : "raw"),
560       "framed", G_TYPE_BOOLEAN, TRUE, NULL);
561 
562   /* DecoderSpecificInfo is only available for mpegversion=4 */
563   if (faac->mpegversion == 4) {
564     guint8 *config = NULL;
565     gulong config_len = 0;
566 
567     /* get the config string */
568     GST_DEBUG_OBJECT (faac, "retrieving decoder info");
569     faacEncGetDecoderSpecificInfo (faac->handle, &config, &config_len);
570 
571     if (!gst_codec_utils_aac_caps_set_level_and_profile (srccaps, config,
572             config_len)) {
573       free (config);
574       gst_caps_unref (srccaps);
575       goto invalid_codec_data;
576     }
577 
578     if (!faac->outputformat) {
579       GstBuffer *codec_data;
580 
581       /* copy it into a buffer */
582       codec_data = gst_buffer_new_and_alloc (config_len);
583       gst_buffer_fill (codec_data, 0, config, config_len);
584 
585       /* add to caps */
586       gst_caps_set_simple (srccaps,
587           "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
588 
589       gst_buffer_unref (codec_data);
590     }
591 
592     free (config);
593   } else {
594     const gchar *profile;
595 
596     /* Add least add the profile to the caps */
597     switch (faac->profile) {
598       case MAIN:
599         profile = "main";
600         break;
601       case LTP:
602         profile = "ltp";
603         break;
604       case SSR:
605         profile = "ssr";
606         break;
607       case LOW:
608       default:
609         profile = "lc";
610         break;
611     }
612     gst_caps_set_simple (srccaps, "profile", G_TYPE_STRING, profile, NULL);
613     /* FIXME: How to get the profile for mpegversion==2? */
614   }
615 
616   GST_DEBUG_OBJECT (faac, "src pad caps: %" GST_PTR_FORMAT, srccaps);
617 
618   ret = gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (faac), srccaps);
619   gst_caps_unref (srccaps);
620 
621   return ret;
622 
623   /* ERROR */
624 set_failed:
625   {
626     GST_WARNING_OBJECT (faac, "Faac doesn't support the current configuration");
627     return FALSE;
628   }
629 invalid_codec_data:
630   {
631     GST_ERROR_OBJECT (faac, "Invalid codec data");
632     return FALSE;
633   }
634 }
635 
636 static GstFlowReturn
gst_faac_handle_frame(GstAudioEncoder * enc,GstBuffer * in_buf)637 gst_faac_handle_frame (GstAudioEncoder * enc, GstBuffer * in_buf)
638 {
639   GstFaac *faac = GST_FAAC (enc);
640   GstFlowReturn ret = GST_FLOW_OK;
641   GstBuffer *out_buf;
642   gsize size, ret_size;
643   int enc_ret;
644   GstMapInfo map, omap;
645   guint8 *data;
646   GstAudioInfo *info =
647       gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (faac));
648 
649   out_buf = gst_buffer_new_and_alloc (faac->bytes);
650   gst_buffer_map (out_buf, &omap, GST_MAP_WRITE);
651 
652   if (G_LIKELY (in_buf)) {
653     if (memcmp (info->position, aac_channel_positions[info->channels - 1],
654             sizeof (GstAudioChannelPosition) * info->channels) != 0) {
655       in_buf = gst_buffer_make_writable (in_buf);
656       gst_audio_buffer_reorder_channels (in_buf, info->finfo->format,
657           info->channels, info->position,
658           aac_channel_positions[info->channels - 1]);
659     }
660     gst_buffer_map (in_buf, &map, GST_MAP_READ);
661     data = map.data;
662     size = map.size;
663   } else {
664     data = NULL;
665     size = 0;
666   }
667 
668   if (G_UNLIKELY ((enc_ret = faacEncEncode (faac->handle, (gint32 *) data,
669                   size / (info->finfo->width / 8), omap.data, omap.size)) < 0))
670     goto encode_failed;
671   ret_size = enc_ret;
672 
673   if (in_buf)
674     gst_buffer_unmap (in_buf, &map);
675 
676   GST_LOG_OBJECT (faac, "encoder return: %" G_GSIZE_FORMAT, ret_size);
677 
678   if (ret_size > 0) {
679     gst_buffer_unmap (out_buf, &omap);
680     gst_buffer_resize (out_buf, 0, ret_size);
681     ret = gst_audio_encoder_finish_frame (enc, out_buf, faac->samples);
682   } else {
683     gst_buffer_unmap (out_buf, &omap);
684     gst_buffer_unref (out_buf);
685     /* re-create encoder after final flush */
686     if (!in_buf) {
687       GST_DEBUG_OBJECT (faac, "flushed; recreating encoder");
688       gst_faac_close_encoder (faac);
689       if (!gst_faac_open_encoder (faac, gst_audio_encoder_get_audio_info (enc)))
690         ret = GST_FLOW_ERROR;
691     }
692   }
693 
694   return ret;
695 
696   /* ERRORS */
697 encode_failed:
698   {
699     GST_ELEMENT_ERROR (faac, LIBRARY, ENCODE, (NULL), (NULL));
700     if (in_buf)
701       gst_buffer_unmap (in_buf, &map);
702     gst_buffer_unmap (out_buf, &omap);
703     gst_buffer_unref (out_buf);
704     return GST_FLOW_ERROR;
705   }
706 }
707 
708 static void
gst_faac_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)709 gst_faac_set_property (GObject * object,
710     guint prop_id, const GValue * value, GParamSpec * pspec)
711 {
712   GstFaac *faac = GST_FAAC (object);
713 
714   GST_OBJECT_LOCK (faac);
715 
716   switch (prop_id) {
717     case PROP_QUALITY:
718       faac->quality = g_value_get_int (value);
719       break;
720     case PROP_BITRATE:
721       faac->bitrate = g_value_get_int (value);
722       break;
723     case PROP_RATE_CONTROL:
724       faac->brtype = g_value_get_enum (value);
725       break;
726     case PROP_TNS:
727       faac->tns = g_value_get_boolean (value);
728       break;
729     case PROP_MIDSIDE:
730       faac->midside = g_value_get_boolean (value);
731       break;
732     case PROP_SHORTCTL:
733       faac->shortctl = g_value_get_enum (value);
734       break;
735     default:
736       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
737       break;
738   }
739 
740   GST_OBJECT_UNLOCK (faac);
741 }
742 
743 static void
gst_faac_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)744 gst_faac_get_property (GObject * object,
745     guint prop_id, GValue * value, GParamSpec * pspec)
746 {
747   GstFaac *faac = GST_FAAC (object);
748 
749   GST_OBJECT_LOCK (faac);
750 
751   switch (prop_id) {
752     case PROP_QUALITY:
753       g_value_set_int (value, faac->quality);
754       break;
755     case PROP_BITRATE:
756       g_value_set_int (value, faac->bitrate);
757       break;
758     case PROP_RATE_CONTROL:
759       g_value_set_enum (value, faac->brtype);
760       break;
761     case PROP_TNS:
762       g_value_set_boolean (value, faac->tns);
763       break;
764     case PROP_MIDSIDE:
765       g_value_set_boolean (value, faac->midside);
766       break;
767     case PROP_SHORTCTL:
768       g_value_set_enum (value, faac->shortctl);
769       break;
770     default:
771       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
772       break;
773   }
774 
775   GST_OBJECT_UNLOCK (faac);
776 }
777 
778 static gboolean
plugin_init(GstPlugin * plugin)779 plugin_init (GstPlugin * plugin)
780 {
781   return gst_element_register (plugin, "faac", GST_RANK_SECONDARY,
782       GST_TYPE_FAAC);
783 }
784 
785 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
786     GST_VERSION_MINOR,
787     faac,
788     "Free AAC Encoder (FAAC)",
789     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
790