• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer AAC encoder plugin
2  * Copyright (C) 2011 Kan Hu <kan.hu@linaro.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /**
21  * SECTION:element-voaacenc
22  * @title: voaacenc
23  *
24  * AAC audio encoder based on vo-aacenc library.
25  *
26  * [vo-aacenc library source file](http://sourceforge.net/projects/opencore-amr/files/vo-aacenc/)
27  *
28  * ## Example launch line
29  * |[
30  * gst-launch-1.0 filesrc location=abc.wav ! wavparse ! audioresample ! audioconvert ! voaacenc ! filesink location=abc.aac
31  * ]|
32  *
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include <string.h>
40 
41 #include <gst/pbutils/codec-utils.h>
42 
43 #include "gstvoaacenc.h"
44 
45 #define VOAAC_ENC_DEFAULT_BITRATE (128000)
46 #define VOAAC_ENC_DEFAULT_OUTPUTFORMAT (0)      /* RAW */
47 #define VOAAC_ENC_MPEGVERSION (4)
48 #define VOAAC_ENC_CODECDATA_LEN (2)
49 #define VOAAC_ENC_BITS_PER_SAMPLE (16)
50 
51 enum
52 {
53   PROP_0,
54   PROP_BITRATE
55 };
56 
57 #define SAMPLE_RATES " 8000, " \
58                     "11025, " \
59                     "12000, " \
60                     "16000, " \
61                     "22050, " \
62                     "24000, " \
63                     "32000, " \
64                     "44100, " \
65                     "48000, " \
66                     "64000, " \
67                     "88200, " \
68                     "96000"
69 
70 /* voaacenc only supports 1 or 2 channels */
71 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
72     GST_PAD_SINK,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS ("audio/x-raw, "
75         "format = (string) " GST_AUDIO_NE (S16) ", "
76         "layout = (string) interleaved, "
77         "rate = (int) { " SAMPLE_RATES " }, " "channels = (int) 1;"
78         "audio/x-raw, "
79         "format = (string) " GST_AUDIO_NE (S16) ", "
80         "layout = (string) interleaved, "
81         "rate = (int) { " SAMPLE_RATES " }, " "channels = (int) 2, "
82         "channel-mask=(bitmask)0x3")
83     );
84 
85 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
86     GST_PAD_SRC,
87     GST_PAD_ALWAYS,
88     GST_STATIC_CAPS ("audio/mpeg, "
89         "mpegversion = (int) 4, "
90         "rate = (int) { " SAMPLE_RATES " }, "
91         "channels = (int) [1, 2], "
92         "stream-format = (string) { adts, raw }, " "base-profile = (string) lc")
93     );
94 
95 GST_DEBUG_CATEGORY_STATIC (gst_voaacenc_debug);
96 #define GST_CAT_DEFAULT gst_voaacenc_debug
97 
98 static gboolean voaacenc_core_init (GstVoAacEnc * voaacenc);
99 static gboolean voaacenc_core_set_parameter (GstVoAacEnc * voaacenc);
100 static void voaacenc_core_uninit (GstVoAacEnc * voaacenc);
101 
102 static gboolean gst_voaacenc_start (GstAudioEncoder * enc);
103 static gboolean gst_voaacenc_stop (GstAudioEncoder * enc);
104 static gboolean gst_voaacenc_set_format (GstAudioEncoder * enc,
105     GstAudioInfo * info);
106 static GstFlowReturn gst_voaacenc_handle_frame (GstAudioEncoder * enc,
107     GstBuffer * in_buf);
108 
109 G_DEFINE_TYPE (GstVoAacEnc, gst_voaacenc, GST_TYPE_AUDIO_ENCODER);
110 GST_ELEMENT_REGISTER_DEFINE (voaacenc, "voaacenc",
111     GST_RANK_SECONDARY, GST_TYPE_VOAACENC);
112 
113 static void
gst_voaacenc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)114 gst_voaacenc_set_property (GObject * object, guint prop_id,
115     const GValue * value, GParamSpec * pspec)
116 {
117   GstVoAacEnc *self = GST_VOAACENC (object);
118 
119   switch (prop_id) {
120     case PROP_BITRATE:
121       self->bitrate = g_value_get_int (value);
122       break;
123     default:
124       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
125       break;
126   }
127   return;
128 }
129 
130 static void
gst_voaacenc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)131 gst_voaacenc_get_property (GObject * object, guint prop_id,
132     GValue * value, GParamSpec * pspec)
133 {
134   GstVoAacEnc *self = GST_VOAACENC (object);
135 
136   switch (prop_id) {
137     case PROP_BITRATE:
138       g_value_set_int (value, self->bitrate);
139       break;
140     default:
141       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
142       break;
143   }
144   return;
145 }
146 
147 static void
gst_voaacenc_class_init(GstVoAacEncClass * klass)148 gst_voaacenc_class_init (GstVoAacEncClass * klass)
149 {
150   GObjectClass *object_class = G_OBJECT_CLASS (klass);
151   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
152   GstAudioEncoderClass *base_class = GST_AUDIO_ENCODER_CLASS (klass);
153 
154   object_class->set_property = GST_DEBUG_FUNCPTR (gst_voaacenc_set_property);
155   object_class->get_property = GST_DEBUG_FUNCPTR (gst_voaacenc_get_property);
156 
157   base_class->start = GST_DEBUG_FUNCPTR (gst_voaacenc_start);
158   base_class->stop = GST_DEBUG_FUNCPTR (gst_voaacenc_stop);
159   base_class->set_format = GST_DEBUG_FUNCPTR (gst_voaacenc_set_format);
160   base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_voaacenc_handle_frame);
161 
162   g_object_class_install_property (object_class, PROP_BITRATE,
163       g_param_spec_int ("bitrate",
164           "Bitrate",
165           "Target Audio Bitrate (bits per second)",
166           0, 320000, VOAAC_ENC_DEFAULT_BITRATE,
167           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
168 
169   gst_element_class_add_static_pad_template (element_class, &sink_template);
170   gst_element_class_add_static_pad_template (element_class, &src_template);
171 
172   gst_element_class_set_static_metadata (element_class, "AAC audio encoder",
173       "Codec/Encoder/Audio", "AAC audio encoder", "Kan Hu <kan.hu@linaro.org>");
174 
175   GST_DEBUG_CATEGORY_INIT (gst_voaacenc_debug, "voaacenc", 0, "voaac encoder");
176 }
177 
178 static void
gst_voaacenc_init(GstVoAacEnc * voaacenc)179 gst_voaacenc_init (GstVoAacEnc * voaacenc)
180 {
181   GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (voaacenc));
182   voaacenc->bitrate = VOAAC_ENC_DEFAULT_BITRATE;
183   voaacenc->output_format = VOAAC_ENC_DEFAULT_OUTPUTFORMAT;
184 
185   /* init rest */
186   voaacenc->handle = NULL;
187 }
188 
189 static gboolean
gst_voaacenc_start(GstAudioEncoder * enc)190 gst_voaacenc_start (GstAudioEncoder * enc)
191 {
192   GstVoAacEnc *voaacenc = GST_VOAACENC (enc);
193 
194   GST_DEBUG_OBJECT (enc, "start");
195 
196   if (voaacenc_core_init (voaacenc) == FALSE)
197     return FALSE;
198 
199   voaacenc->rate = 0;
200   voaacenc->channels = 0;
201 
202   return TRUE;
203 }
204 
205 static gboolean
gst_voaacenc_stop(GstAudioEncoder * enc)206 gst_voaacenc_stop (GstAudioEncoder * enc)
207 {
208   GstVoAacEnc *voaacenc = GST_VOAACENC (enc);
209 
210   GST_DEBUG_OBJECT (enc, "stop");
211   voaacenc_core_uninit (voaacenc);
212 
213   return TRUE;
214 }
215 
216 #define VOAAC_ENC_MAX_CHANNELS 6
217 /* describe the channels position */
218 static const GstAudioChannelPosition
219     aac_channel_positions[][VOAAC_ENC_MAX_CHANNELS] = {
220   {                             /* 1 ch: Mono */
221       GST_AUDIO_CHANNEL_POSITION_MONO},
222   {                             /* 2 ch: front left + front right (front stereo) */
223         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
224       GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
225   {                             /* 3 ch: front center + front stereo */
226         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
227         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
228       GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
229   {                             /* 4 ch: front center + front stereo + back center */
230         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
231         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
232         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
233       GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
234   {                             /* 5 ch: front center + front stereo + back stereo */
235         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
236         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
237         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
238         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
239       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
240   {                             /* 6ch: front center + front stereo + back stereo + LFE */
241         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
242         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
243         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
244         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
245         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
246       GST_AUDIO_CHANNEL_POSITION_LFE1}
247 };
248 
249 /* check downstream caps to configure format */
250 static void
gst_voaacenc_negotiate(GstVoAacEnc * voaacenc)251 gst_voaacenc_negotiate (GstVoAacEnc * voaacenc)
252 {
253   GstCaps *caps;
254 
255   caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (voaacenc));
256 
257   GST_DEBUG_OBJECT (voaacenc, "allowed caps: %" GST_PTR_FORMAT, caps);
258 
259   if (caps && gst_caps_get_size (caps) > 0) {
260     GstStructure *s = gst_caps_get_structure (caps, 0);
261     const gchar *str = NULL;
262 
263     if ((str = gst_structure_get_string (s, "stream-format"))) {
264       if (strcmp (str, "adts") == 0) {
265         GST_DEBUG_OBJECT (voaacenc, "use ADTS format for output");
266         voaacenc->output_format = 1;
267       } else if (strcmp (str, "raw") == 0) {
268         GST_DEBUG_OBJECT (voaacenc, "use RAW format for output");
269         voaacenc->output_format = 0;
270       } else {
271         GST_DEBUG_OBJECT (voaacenc, "unknown stream-format: %s", str);
272         voaacenc->output_format = VOAAC_ENC_DEFAULT_OUTPUTFORMAT;
273       }
274     }
275   }
276 
277   if (caps)
278     gst_caps_unref (caps);
279 }
280 
281 static gint
gst_voaacenc_get_rate_index(gint rate)282 gst_voaacenc_get_rate_index (gint rate)
283 {
284   static const gint rate_table[] = {
285     96000, 88200, 64000, 48000, 44100, 32000,
286     24000, 22050, 16000, 12000, 11025, 8000
287   };
288   gint i;
289   for (i = 0; i < G_N_ELEMENTS (rate_table); ++i) {
290     if (rate == rate_table[i]) {
291       return i;
292     }
293   }
294   return -1;
295 }
296 
297 static GstCaps *
gst_voaacenc_create_source_pad_caps(GstVoAacEnc * voaacenc)298 gst_voaacenc_create_source_pad_caps (GstVoAacEnc * voaacenc)
299 {
300   GstCaps *caps = NULL;
301   gint index;
302   GstBuffer *codec_data;
303   GstMapInfo map;
304 
305   if ((index = gst_voaacenc_get_rate_index (voaacenc->rate)) >= 0) {
306     codec_data = gst_buffer_new_and_alloc (VOAAC_ENC_CODECDATA_LEN);
307     gst_buffer_map (codec_data, &map, GST_MAP_WRITE);
308     /* LC profile only */
309     map.data[0] = ((0x02 << 3) | (index >> 1));
310     map.data[1] = ((index & 0x01) << 7) | (voaacenc->channels << 3);
311 
312     caps = gst_caps_new_simple ("audio/mpeg",
313         "mpegversion", G_TYPE_INT, VOAAC_ENC_MPEGVERSION,
314         "channels", G_TYPE_INT, voaacenc->channels,
315         "rate", G_TYPE_INT, voaacenc->rate, NULL);
316 
317     gst_codec_utils_aac_caps_set_level_and_profile (caps, map.data,
318         VOAAC_ENC_CODECDATA_LEN);
319     gst_buffer_unmap (codec_data, &map);
320 
321     if (!voaacenc->output_format) {
322       gst_caps_set_simple (caps,
323           "stream-format", G_TYPE_STRING, "raw",
324           "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
325     } else {
326       gst_caps_set_simple (caps,
327           "stream-format", G_TYPE_STRING, "adts",
328           "framed", G_TYPE_BOOLEAN, TRUE, NULL);
329     }
330     gst_buffer_unref (codec_data);
331   }
332 
333   return caps;
334 }
335 
336 static gboolean
gst_voaacenc_set_format(GstAudioEncoder * benc,GstAudioInfo * info)337 gst_voaacenc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
338 {
339   gboolean ret = FALSE;
340   GstVoAacEnc *voaacenc;
341   GstCaps *src_caps;
342 
343   voaacenc = GST_VOAACENC (benc);
344 
345   /* get channel count */
346   voaacenc->channels = GST_AUDIO_INFO_CHANNELS (info);
347   voaacenc->rate = GST_AUDIO_INFO_RATE (info);
348 
349   /* precalc buffer size as it's constant now */
350   voaacenc->inbuf_size = voaacenc->channels * 2 * 1024;
351 
352   gst_voaacenc_negotiate (voaacenc);
353 
354   /* create reverse caps */
355   src_caps = gst_voaacenc_create_source_pad_caps (voaacenc);
356 
357   if (src_caps) {
358     gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (voaacenc),
359         src_caps);
360     gst_caps_unref (src_caps);
361     ret = voaacenc_core_set_parameter (voaacenc);
362   }
363 
364   /* report needs to base class */
365   gst_audio_encoder_set_frame_samples_min (benc, 1024);
366   gst_audio_encoder_set_frame_samples_max (benc, 1024);
367   gst_audio_encoder_set_frame_max (benc, 1);
368 
369   return ret;
370 }
371 
372 static GstFlowReturn
gst_voaacenc_handle_frame(GstAudioEncoder * benc,GstBuffer * buf)373 gst_voaacenc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf)
374 {
375   GstVoAacEnc *voaacenc;
376   GstFlowReturn ret = GST_FLOW_OK;
377   GstBuffer *out;
378   VO_AUDIO_OUTPUTINFO output_info = { {0} };
379   VO_CODECBUFFER input = { 0 };
380   VO_CODECBUFFER output = { 0 };
381   GstMapInfo map, omap;
382   GstAudioInfo *info = gst_audio_encoder_get_audio_info (benc);
383 
384   voaacenc = GST_VOAACENC (benc);
385 
386   g_return_val_if_fail (voaacenc->handle, GST_FLOW_NOT_NEGOTIATED);
387 
388   /* we don't deal with squeezing remnants, so simply discard those */
389   if (G_UNLIKELY (buf == NULL)) {
390     GST_DEBUG_OBJECT (benc, "no data");
391     goto exit;
392   }
393 
394   if (memcmp (info->position, aac_channel_positions[info->channels - 1],
395           sizeof (GstAudioChannelPosition) * info->channels) != 0) {
396     buf = gst_buffer_make_writable (buf);
397     gst_audio_buffer_reorder_channels (buf, info->finfo->format,
398         info->channels, info->position,
399         aac_channel_positions[info->channels - 1]);
400   }
401 
402   gst_buffer_map (buf, &map, GST_MAP_READ);
403 
404   if (G_UNLIKELY (map.size < voaacenc->inbuf_size)) {
405     gst_buffer_unmap (buf, &map);
406     GST_DEBUG_OBJECT (voaacenc, "discarding trailing data %d", (gint) map.size);
407     ret = gst_audio_encoder_finish_frame (benc, NULL, -1);
408     goto exit;
409   }
410 
411   /* max size */
412   out = gst_buffer_new_and_alloc (voaacenc->inbuf_size);
413   gst_buffer_map (out, &omap, GST_MAP_WRITE);
414 
415   output.Buffer = omap.data;
416   output.Length = voaacenc->inbuf_size;
417 
418   g_assert (map.size == voaacenc->inbuf_size);
419   input.Buffer = map.data;
420   input.Length = voaacenc->inbuf_size;
421   voaacenc->codec_api.SetInputData (voaacenc->handle, &input);
422 
423   /* encode */
424   if (voaacenc->codec_api.GetOutputData (voaacenc->handle, &output,
425           &output_info) != VO_ERR_NONE) {
426     gst_buffer_unmap (buf, &map);
427     gst_buffer_unmap (out, &omap);
428     gst_buffer_unref (out);
429     goto encode_failed;
430   }
431 
432   GST_LOG_OBJECT (voaacenc, "encoded to %lu bytes", output.Length);
433   gst_buffer_unmap (buf, &map);
434   gst_buffer_unmap (out, &omap);
435   gst_buffer_resize (out, 0, output.Length);
436 
437   ret = gst_audio_encoder_finish_frame (benc, out, 1024);
438 
439 exit:
440   return ret;
441 
442   /* ERRORS */
443 encode_failed:
444   {
445     GST_ELEMENT_ERROR (voaacenc, STREAM, ENCODE, (NULL), ("encode failed"));
446     ret = GST_FLOW_ERROR;
447     goto exit;
448   }
449 }
450 
451 static VO_U32
voaacenc_core_mem_alloc(VO_S32 uID,VO_MEM_INFO * pMemInfo)452 voaacenc_core_mem_alloc (VO_S32 uID, VO_MEM_INFO * pMemInfo)
453 {
454   if (!pMemInfo)
455     return VO_ERR_INVALID_ARG;
456 
457   pMemInfo->VBuffer = g_malloc (pMemInfo->Size);
458   return 0;
459 }
460 
461 static VO_U32
voaacenc_core_mem_free(VO_S32 uID,VO_PTR pMem)462 voaacenc_core_mem_free (VO_S32 uID, VO_PTR pMem)
463 {
464   g_free (pMem);
465   return 0;
466 }
467 
468 static VO_U32
voaacenc_core_mem_set(VO_S32 uID,VO_PTR pBuff,VO_U8 uValue,VO_U32 uSize)469 voaacenc_core_mem_set (VO_S32 uID, VO_PTR pBuff, VO_U8 uValue, VO_U32 uSize)
470 {
471   memset (pBuff, uValue, uSize);
472   return 0;
473 }
474 
475 static VO_U32
voaacenc_core_mem_copy(VO_S32 uID,VO_PTR pDest,VO_PTR pSource,VO_U32 uSize)476 voaacenc_core_mem_copy (VO_S32 uID, VO_PTR pDest, VO_PTR pSource, VO_U32 uSize)
477 {
478   memcpy (pDest, pSource, uSize);
479   return 0;
480 }
481 
482 static VO_U32
voaacenc_core_mem_check(VO_S32 uID,VO_PTR pBuffer,VO_U32 uSize)483 voaacenc_core_mem_check (VO_S32 uID, VO_PTR pBuffer, VO_U32 uSize)
484 {
485   return 0;
486 }
487 
488 static gboolean
voaacenc_core_init(GstVoAacEnc * voaacenc)489 voaacenc_core_init (GstVoAacEnc * voaacenc)
490 {
491   VO_CODEC_INIT_USERDATA user_data = { 0 };
492   voGetAACEncAPI (&voaacenc->codec_api);
493 
494   voaacenc->mem_operator.Alloc = voaacenc_core_mem_alloc;
495   voaacenc->mem_operator.Copy = voaacenc_core_mem_copy;
496   voaacenc->mem_operator.Free = voaacenc_core_mem_free;
497   voaacenc->mem_operator.Set = voaacenc_core_mem_set;
498   voaacenc->mem_operator.Check = voaacenc_core_mem_check;
499   user_data.memflag = VO_IMF_USERMEMOPERATOR;
500   user_data.memData = &voaacenc->mem_operator;
501   voaacenc->codec_api.Init (&voaacenc->handle, VO_AUDIO_CodingAAC, &user_data);
502 
503   if (voaacenc->handle == NULL) {
504     return FALSE;
505   }
506   return TRUE;
507 
508 }
509 
510 static gboolean
voaacenc_core_set_parameter(GstVoAacEnc * voaacenc)511 voaacenc_core_set_parameter (GstVoAacEnc * voaacenc)
512 {
513   AACENC_PARAM params = { 0 };
514   guint32 ret;
515 
516   params.sampleRate = voaacenc->rate;
517   params.bitRate = voaacenc->bitrate;
518   params.nChannels = voaacenc->channels;
519   if (voaacenc->output_format) {
520     params.adtsUsed = 1;
521   } else {
522     params.adtsUsed = 0;
523   }
524 
525   ret =
526       voaacenc->codec_api.SetParam (voaacenc->handle, VO_PID_AAC_ENCPARAM,
527       &params);
528   if (ret != VO_ERR_NONE) {
529     GST_ERROR_OBJECT (voaacenc, "Failed to set encoder parameters");
530     return FALSE;
531   }
532   return TRUE;
533 }
534 
535 static void
voaacenc_core_uninit(GstVoAacEnc * voaacenc)536 voaacenc_core_uninit (GstVoAacEnc * voaacenc)
537 {
538   if (voaacenc->handle) {
539     voaacenc->codec_api.Uninit (voaacenc->handle);
540     voaacenc->handle = NULL;
541   }
542 }
543