1 /* GStreamer AAC encoder plugin
2 * Copyright (C) 2011 Kan Hu <kan.hu@linaro.org>
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.
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.
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.
21 * SECTION:element-voaacenc
24 * AAC audio encoder based on vo-aacenc library.
26 * [vo-aacenc library source file](http://sourceforge.net/projects/opencore-amr/files/vo-aacenc/)
28 * ## Example launch line
30 * gst-launch-1.0 filesrc location=abc.wav ! wavparse ! audioresample ! audioconvert ! voaacenc ! filesink location=abc.aac
41 #include <gst/pbutils/codec-utils.h>
43 #include "gstvoaacenc.h"
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)
57 #define SAMPLE_RATES " 8000, " \
70 /* voaacenc only supports 1 or 2 channels */
71 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
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;"
79 "format = (string) " GST_AUDIO_NE (S16) ", "
80 "layout = (string) interleaved, "
81 "rate = (int) { " SAMPLE_RATES " }, " "channels = (int) 2, "
82 "channel-mask=(bitmask)0x3")
85 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
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")
95 GST_DEBUG_CATEGORY_STATIC (gst_voaacenc_debug);
96 #define GST_CAT_DEFAULT gst_voaacenc_debug
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);
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,
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);
114 gst_voaacenc_set_property (GObject * object, guint prop_id,
115 const GValue * value, GParamSpec * pspec)
117 GstVoAacEnc *self = GST_VOAACENC (object);
121 self->bitrate = g_value_get_int (value);
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 gst_voaacenc_get_property (GObject * object, guint prop_id,
132 GValue * value, GParamSpec * pspec)
134 GstVoAacEnc *self = GST_VOAACENC (object);
138 g_value_set_int (value, self->bitrate);
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148 gst_voaacenc_class_init (GstVoAacEncClass * klass)
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);
154 object_class->set_property = GST_DEBUG_FUNCPTR (gst_voaacenc_set_property);
155 object_class->get_property = GST_DEBUG_FUNCPTR (gst_voaacenc_get_property);
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);
162 g_object_class_install_property (object_class, PROP_BITRATE,
163 g_param_spec_int ("bitrate",
165 "Target Audio Bitrate (bits per second)",
166 0, 320000, VOAAC_ENC_DEFAULT_BITRATE,
167 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
169 gst_element_class_add_static_pad_template (element_class, &sink_template);
170 gst_element_class_add_static_pad_template (element_class, &src_template);
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>");
175 GST_DEBUG_CATEGORY_INIT (gst_voaacenc_debug, "voaacenc", 0, "voaac encoder");
179 gst_voaacenc_init (GstVoAacEnc * voaacenc)
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;
186 voaacenc->handle = NULL;
190 gst_voaacenc_start (GstAudioEncoder * enc)
192 GstVoAacEnc *voaacenc = GST_VOAACENC (enc);
194 GST_DEBUG_OBJECT (enc, "start");
196 if (voaacenc_core_init (voaacenc) == FALSE)
200 voaacenc->channels = 0;
206 gst_voaacenc_stop (GstAudioEncoder * enc)
208 GstVoAacEnc *voaacenc = GST_VOAACENC (enc);
210 GST_DEBUG_OBJECT (enc, "stop");
211 voaacenc_core_uninit (voaacenc);
216 #define VOAAC_ENC_MAX_CHANNELS 6
217 /* describe the channels position */
218 static const GstAudioChannelPosition
219 aac_channel_positions[][VOAAC_ENC_MAX_CHANNELS] = {
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}
249 /* check downstream caps to configure format */
251 gst_voaacenc_negotiate (GstVoAacEnc * voaacenc)
255 caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (voaacenc));
257 GST_DEBUG_OBJECT (voaacenc, "allowed caps: %" GST_PTR_FORMAT, caps);
259 if (caps && gst_caps_get_size (caps) > 0) {
260 GstStructure *s = gst_caps_get_structure (caps, 0);
261 const gchar *str = NULL;
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;
271 GST_DEBUG_OBJECT (voaacenc, "unknown stream-format: %s", str);
272 voaacenc->output_format = VOAAC_ENC_DEFAULT_OUTPUTFORMAT;
278 gst_caps_unref (caps);
282 gst_voaacenc_get_rate_index (gint rate)
284 static const gint rate_table[] = {
285 96000, 88200, 64000, 48000, 44100, 32000,
286 24000, 22050, 16000, 12000, 11025, 8000
289 for (i = 0; i < G_N_ELEMENTS (rate_table); ++i) {
290 if (rate == rate_table[i]) {
298 gst_voaacenc_create_source_pad_caps (GstVoAacEnc * voaacenc)
300 GstCaps *caps = NULL;
302 GstBuffer *codec_data;
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);
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);
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);
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);
326 gst_caps_set_simple (caps,
327 "stream-format", G_TYPE_STRING, "adts",
328 "framed", G_TYPE_BOOLEAN, TRUE, NULL);
330 gst_buffer_unref (codec_data);
337 gst_voaacenc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
339 gboolean ret = FALSE;
340 GstVoAacEnc *voaacenc;
343 voaacenc = GST_VOAACENC (benc);
345 /* get channel count */
346 voaacenc->channels = GST_AUDIO_INFO_CHANNELS (info);
347 voaacenc->rate = GST_AUDIO_INFO_RATE (info);
349 /* precalc buffer size as it's constant now */
350 voaacenc->inbuf_size = voaacenc->channels * 2 * 1024;
352 gst_voaacenc_negotiate (voaacenc);
354 /* create reverse caps */
355 src_caps = gst_voaacenc_create_source_pad_caps (voaacenc);
358 gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (voaacenc),
360 gst_caps_unref (src_caps);
361 ret = voaacenc_core_set_parameter (voaacenc);
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);
373 gst_voaacenc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf)
375 GstVoAacEnc *voaacenc;
376 GstFlowReturn ret = GST_FLOW_OK;
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);
384 voaacenc = GST_VOAACENC (benc);
386 g_return_val_if_fail (voaacenc->handle, GST_FLOW_NOT_NEGOTIATED);
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");
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]);
402 gst_buffer_map (buf, &map, GST_MAP_READ);
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);
412 out = gst_buffer_new_and_alloc (voaacenc->inbuf_size);
413 gst_buffer_map (out, &omap, GST_MAP_WRITE);
415 output.Buffer = omap.data;
416 output.Length = voaacenc->inbuf_size;
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);
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);
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);
437 ret = gst_audio_encoder_finish_frame (benc, out, 1024);
445 GST_ELEMENT_ERROR (voaacenc, STREAM, ENCODE, (NULL), ("encode failed"));
446 ret = GST_FLOW_ERROR;
452 voaacenc_core_mem_alloc (VO_S32 uID, VO_MEM_INFO * pMemInfo)
455 return VO_ERR_INVALID_ARG;
457 pMemInfo->VBuffer = g_malloc (pMemInfo->Size);
462 voaacenc_core_mem_free (VO_S32 uID, VO_PTR pMem)
469 voaacenc_core_mem_set (VO_S32 uID, VO_PTR pBuff, VO_U8 uValue, VO_U32 uSize)
471 memset (pBuff, uValue, uSize);
476 voaacenc_core_mem_copy (VO_S32 uID, VO_PTR pDest, VO_PTR pSource, VO_U32 uSize)
478 memcpy (pDest, pSource, uSize);
483 voaacenc_core_mem_check (VO_S32 uID, VO_PTR pBuffer, VO_U32 uSize)
489 voaacenc_core_init (GstVoAacEnc * voaacenc)
491 VO_CODEC_INIT_USERDATA user_data = { 0 };
492 voGetAACEncAPI (&voaacenc->codec_api);
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);
503 if (voaacenc->handle == NULL) {
511 voaacenc_core_set_parameter (GstVoAacEnc * voaacenc)
513 AACENC_PARAM params = { 0 };
516 params.sampleRate = voaacenc->rate;
517 params.bitRate = voaacenc->bitrate;
518 params.nChannels = voaacenc->channels;
519 if (voaacenc->output_format) {
526 voaacenc->codec_api.SetParam (voaacenc->handle, VO_PID_AAC_ENCPARAM,
528 if (ret != VO_ERR_NONE) {
529 GST_ERROR_OBJECT (voaacenc, "Failed to set encoder parameters");
536 voaacenc_core_uninit (GstVoAacEnc * voaacenc)
538 if (voaacenc->handle) {
539 voaacenc->codec_api.Uninit (voaacenc->handle);
540 voaacenc->handle = NULL;