gstfaac.c 22.6 KB
Newer Older
1 2
/* GStreamer FAAC (Free AAC Encoder) plugin
 * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3
 * Copyright (C) 2009 Mark Nauwelaerts <mnauw@users.sourceforge.net>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

21 22
/**
 * SECTION:element-faac
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
23
 * @see_also: faad
24
 *
25
 * faac encodes raw audio to AAC (MPEG-4 part 3) streams.
26 27 28
 *
 * <refsect2>
 * <title>Example launch line</title>
Stefan Kost's avatar
Stefan Kost committed
29
 * |[
30
 * gst-launch audiotestsrc wave=sine num-buffers=100 ! audioconvert ! faac ! matroskamux ! filesink location=sine.mkv
Stefan Kost's avatar
Stefan Kost committed
31
 * ]| Encode a sine beep as aac and write to matroska container.
32 33 34
 * </refsect2>
 */

35 36 37
#ifdef HAVE_CONFIG_H
#include "config.h"
#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 47 48 49 50 51 52 53 54 55 56 57
#define SAMPLE_RATES " 8000, " \
                    "11025, " \
                    "12000, " \
                    "16000, " \
                    "22050, " \
                    "24000, " \
                    "32000, " \
                    "44100, " \
                    "48000, " \
                    "64000, " \
                    "88200, " \
                    "96000"
58
#define SINK_CAPS \
Wim Taymans's avatar
Wim Taymans committed
59 60
    "audio/x-raw, "                \
    "format = (string) "GST_AUDIO_NE (S16) ", "  \
61
    "layout = (string) interleaved, " \
62
    "rate = (int) {" SAMPLE_RATES "}, "   \
63
    "channels = (int) [ 1, 6 ] "
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

/* 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
79 80
#define SRC_CAPS \
    "audio/mpeg, "                     \
81
    "mpegversion = (int) 4, "   \
82
    "channels = (int) [ 1, 6 ], "      \
83
    "rate = (int) {" SAMPLE_RATES "}, "   \
84 85 86 87 88
    "stream-format = (string) { adts, raw }, " \
    "base-profile = (string) { main, lc, ssr, ltp }; " \
    "audio/mpeg, "                     \
    "mpegversion = (int) 2, "   \
    "channels = (int) [ 1, 6 ], "      \
89
    "rate = (int) {" SAMPLE_RATES "}, "   \
90 91
    "stream-format = (string) { adts, raw }, " \
    "profile = (string) { main, lc }"
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
92 93 94
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
95
    GST_STATIC_CAPS (SRC_CAPS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
96 97 98 99

static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
100
    GST_STATIC_CAPS (SINK_CAPS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
101 102 103

enum
{
104
  PROP_0,
105
  PROP_QUALITY,
106
  PROP_BITRATE,
107
  PROP_RATE_CONTROL,
108 109 110 111
  PROP_PROFILE,
  PROP_TNS,
  PROP_MIDSIDE,
  PROP_SHORTCTL
112 113
};

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
114 115 116 117 118 119
enum
{
  VBR = 1,
  ABR
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
120 121 122 123
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);
124

125 126
static gboolean gst_faac_configure_source_pad (GstFaac * faac,
    GstAudioInfo * info);
Wim Taymans's avatar
Wim Taymans committed
127
static GstCaps *gst_faac_getcaps (GstAudioEncoder * enc, GstCaps * filter);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
128 129 130 131 132 133 134

static gboolean gst_faac_start (GstAudioEncoder * enc);
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);
135

136 137 138
GST_DEBUG_CATEGORY_STATIC (faac_debug);
#define GST_CAT_DEFAULT faac_debug

139
#define FAAC_DEFAULT_QUALITY      100
140
#define FAAC_DEFAULT_BITRATE      128 * 1000
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
141
#define FAAC_DEFAULT_RATE_CONTROL VBR
142 143 144
#define FAAC_DEFAULT_TNS          FALSE
#define FAAC_DEFAULT_MIDSIDE      TRUE
#define FAAC_DEFAULT_SHORTCTL     SHORTCTL_NORMAL
145

Wim Taymans's avatar
Wim Taymans committed
146 147
#define gst_faac_parent_class parent_class
G_DEFINE_TYPE (GstFaac, gst_faac, GST_TYPE_AUDIO_ENCODER);
148

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
#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 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;
}

169 170 171 172 173 174 175 176
#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 GEnumValue gst_faac_shortctl[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
177 178 179 180
      {SHORTCTL_NORMAL, "SHORTCTL_NORMAL", "Normal block type"},
      {SHORTCTL_NOSHORT, "SHORTCTL_NOSHORT", "No short blocks"},
      {SHORTCTL_NOLONG, "SHORTCTL_NOLONG", "No long blocks"},
      {0, NULL, NULL},
181 182 183
    };

    gst_faac_shortctl_type = g_enum_register_static ("GstFaacShortCtl",
184
        gst_faac_shortctl);
185 186 187 188 189 190
  }

  return gst_faac_shortctl_type;
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
191
gst_faac_class_init (GstFaacClass * klass)
192 193
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Wim Taymans's avatar
Wim Taymans committed
194
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
195
  GstAudioEncoderClass *base_class = GST_AUDIO_ENCODER_CLASS (klass);
196

197 198
  gobject_class->set_property = gst_faac_set_property;
  gobject_class->get_property = gst_faac_get_property;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
199

Wim Taymans's avatar
Wim Taymans committed
200 201 202 203 204
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&src_template));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sink_template));

205
  gst_element_class_set_metadata (gstelement_class, "AAC audio encoder",
Wim Taymans's avatar
Wim Taymans committed
206 207 208 209
      "Codec/Encoder/Audio",
      "Free MPEG-2/4 AAC encoder",
      "Ronald Bultje <rbultje@ronald.bitfreak.net>");

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
210 211 212 213 214
  base_class->start = GST_DEBUG_FUNCPTR (gst_faac_start);
  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);
  base_class->getcaps = GST_DEBUG_FUNCPTR (gst_faac_getcaps);
215

216
  /* properties */
217 218 219
  g_object_class_install_property (gobject_class, PROP_QUALITY,
      g_param_spec_int ("quality", "Quality (%)",
          "Variable bitrate (VBR) quantizer quality in %", 1, 1000,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
220 221
          FAAC_DEFAULT_QUALITY,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222
  g_object_class_install_property (gobject_class, PROP_BITRATE,
223
      g_param_spec_int ("bitrate", "Bitrate (bps)",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
224 225 226
          "Average Bitrate (ABR) in bits/sec", 8 * 1000, 320 * 1000,
          FAAC_DEFAULT_BITRATE,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227 228 229
  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,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
230 231
          FAAC_DEFAULT_RATE_CONTROL,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
232
  g_object_class_install_property (gobject_class, PROP_TNS,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
233
      g_param_spec_boolean ("tns", "TNS", "Use temporal noise shaping",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
234 235
          FAAC_DEFAULT_TNS,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
236
  g_object_class_install_property (gobject_class, PROP_MIDSIDE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
237
      g_param_spec_boolean ("midside", "Midside", "Allow mid/side encoding",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
238 239
          FAAC_DEFAULT_MIDSIDE,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240
  g_object_class_install_property (gobject_class, PROP_SHORTCTL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
241
      g_param_spec_enum ("shortctl", "Block type",
242
          "Block type encorcing",
243
          GST_TYPE_FAAC_SHORTCTL, FAAC_DEFAULT_SHORTCTL,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
244
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
245 246

  GST_DEBUG_CATEGORY_INIT (faac_debug, "faac", 0, "AAC encoding");
247 248 249
}

static void
Wim Taymans's avatar
Wim Taymans committed
250
gst_faac_init (GstFaac * faac)
251
{
252 253 254
}

static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
255
gst_faac_close_encoder (GstFaac * faac)
256
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
257 258
  if (faac->handle)
    faacEncClose (faac->handle);
259
  faac->handle = NULL;
260 261
}

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
262 263
static gboolean
gst_faac_start (GstAudioEncoder * enc)
264
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
265
  GstFaac *faac = GST_FAAC (enc);
266

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
267 268
  GST_DEBUG_OBJECT (faac, "start");
  return TRUE;
269 270
}

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
271 272
static gboolean
gst_faac_stop (GstAudioEncoder * enc)
273
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
274 275 276 277 278
  GstFaac *faac = GST_FAAC (enc);

  GST_DEBUG_OBJECT (faac, "stop");
  gst_faac_close_encoder (faac);
  return TRUE;
279 280
}

281
static const GstAudioChannelPosition aac_channel_positions[][8] = {
282
  {GST_AUDIO_CHANNEL_POSITION_MONO},
283 284
  {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
      GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
David Schleef's avatar
David Schleef committed
285 286 287
  {
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
288
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
David Schleef's avatar
David Schleef committed
289 290
      },
  {
291
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
David Schleef's avatar
David Schleef committed
292
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
293
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
David Schleef's avatar
David Schleef committed
294 295
      GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
  {
296
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
David Schleef's avatar
David Schleef committed
297 298
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
299 300
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
David Schleef's avatar
David Schleef committed
301 302 303
  {
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
304 305
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
David Schleef's avatar
David Schleef committed
306
        GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
307
      GST_AUDIO_CHANNEL_POSITION_LFE1}
308 309 310
};

static GstCaps *
Wim Taymans's avatar
Wim Taymans committed
311
gst_faac_getcaps (GstAudioEncoder * enc, GstCaps * filter)
312 313 314 315 316 317 318
{
  static volatile gsize sinkcaps = 0;

  if (g_once_init_enter (&sinkcaps)) {
    GstCaps *tmp = gst_caps_new_empty ();
    GstStructure *s, *t;
    gint i, c;
319 320 321 322 323 324 325 326 327 328 329 330 331 332
    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);
333

Wim Taymans's avatar
Wim Taymans committed
334
    s = gst_structure_new ("audio/x-raw",
335 336
        "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
        "layout", G_TYPE_STRING, "interleaved", NULL);
337
    gst_structure_set_value (s, "rate", &rates_arr);
338 339

    for (i = 1; i <= 6; i++) {
340
      guint64 channel_mask = 0;
341 342 343
      t = gst_structure_copy (s);

      gst_structure_set (t, "channels", G_TYPE_INT, i, NULL);
344 345 346 347 348 349 350 351
      if (i > 1) {
        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);
      }
352 353 354
      gst_caps_append_structure (tmp, t);
    }
    gst_structure_free (s);
355
    g_value_unset (&rates_arr);
356

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
357
    GST_DEBUG_OBJECT (enc, "Generated sinkcaps: %" GST_PTR_FORMAT, tmp);
358 359 360 361

    g_once_init_leave (&sinkcaps, (gsize) tmp);
  }

362
  return gst_audio_encoder_proxy_getcaps (enc, (GstCaps *) sinkcaps, filter);
363 364
}

365
static gboolean
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
366
gst_faac_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
367
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
368
  GstFaac *faac = GST_FAAC (enc);
369 370
  gint width;
  gulong fmt = 0;
371
  gboolean result = FALSE;
372

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
373 374
  /* base class takes care */
  width = GST_AUDIO_INFO_WIDTH (info);
375

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
376
  if (GST_AUDIO_INFO_IS_INTEGER (info)) {
377 378 379 380 381 382 383 384 385 386 387
    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);
    }
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
388
  } else {
389
    fmt = FAAC_INPUT_FLOAT;
390
  }
391

392 393
  faac->format = fmt;

394
  /* finish up */
395
  result = gst_faac_configure_source_pad (faac, info);
396 397
  if (!result)
    goto done;
398

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
399
  /* report needs to base class */
400 401
  gst_audio_encoder_set_frame_samples_min (enc, faac->samples);
  gst_audio_encoder_set_frame_samples_max (enc, faac->samples);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
402 403
  gst_audio_encoder_set_frame_max (enc, 1);

404 405
done:
  return result;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
}

/* 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;
    }
460
  }
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
461 462 463

  if (caps)
    gst_caps_unref (caps);
464 465
}

466
static gboolean
467
gst_faac_open_encoder (GstFaac * faac, GstAudioInfo * info)
468
{
469
  faacEncHandle *handle;
470
  faacEncConfiguration *conf;
471
  guint maxbitrate;
472
  gulong samples, bytes;
473

474
  g_return_val_if_fail (info->rate != 0 && info->channels != 0, FALSE);
475 476 477 478

  /* clean up in case of re-configure */
  gst_faac_close_encoder (faac);

479
  if (!(handle = faacEncOpen (info->rate, info->channels, &samples, &bytes)))
480 481 482
    goto setup_failed;

  /* mind channel count */
483
  samples /= info->channels;
484 485 486 487 488 489 490 491

  /* 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);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
492

493 494
  /* we negotiated caps update current configuration */
  conf = faacEncGetCurrentConfiguration (faac->handle);
495
  conf->mpegVersion = (faac->mpegversion == 4) ? MPEG4 : MPEG2;
496 497 498 499
  conf->aacObjectType = faac->profile;
  conf->allowMidside = faac->midside;
  conf->useLfe = 0;
  conf->useTns = faac->tns;
500 501 502 503

  if (faac->brtype == VBR) {
    conf->quantqual = faac->quality;
  } else if (faac->brtype == ABR) {
504
    conf->bitRate = faac->bitrate / info->channels;
505 506
  }

507 508 509
  conf->inputFormat = faac->format;
  conf->outputFormat = faac->outputformat;
  conf->shortctl = faac->shortctl;
510 511 512 513

  /* check, warn and correct if the max bitrate for the given samplerate is
   * exceeded. Maximum of 6144 bit for a channel */
  maxbitrate =
514
      (unsigned int) (6144.0 * (double) info->rate / (double) 1024.0 + .5);
515 516
  if (conf->bitRate > maxbitrate) {
    GST_ELEMENT_WARNING (faac, RESOURCE, SETTINGS, (NULL),
517
        ("bitrate %lu exceeds maximum allowed bitrate of %u for samplerate %d. "
518
            "Setting bitrate to %u", conf->bitRate, maxbitrate,
519
            info->rate, maxbitrate));
520 521 522
    conf->bitRate = maxbitrate;
  }

523 524 525
  /* default 0 to start with, libfaac chooses based on bitrate */
  conf->bandWidth = 0;

526
  if (!faacEncSetConfiguration (faac->handle, conf))
527
    goto setup_failed;
528

529 530
  /* let's see what really happened,
   * note that this may not really match desired rate */
531
  GST_DEBUG_OBJECT (faac, "average bitrate: %lu kbps",
532
      (conf->bitRate + 500) / 1000 * info->channels);
533 534 535
  GST_DEBUG_OBJECT (faac, "quantization quality: %ld", conf->quantqual);
  GST_DEBUG_OBJECT (faac, "bandwidth: %d Hz", conf->bandWidth);

536 537 538 539 540 541 542 543 544 545 546
  return TRUE;

  /* ERRORS */
setup_failed:
  {
    GST_ELEMENT_ERROR (faac, LIBRARY, SETTINGS, (NULL), (NULL));
    return FALSE;
  }
}

static gboolean
547
gst_faac_configure_source_pad (GstFaac * faac, GstAudioInfo * info)
548 549 550 551 552 553 554
{
  GstCaps *srccaps;
  gboolean ret;

  /* negotiate stream format */
  gst_faac_negotiate (faac);

555
  if (!gst_faac_open_encoder (faac, info))
556 557
    goto set_failed;

558 559
  /* now create a caps for it all */
  srccaps = gst_caps_new_simple ("audio/mpeg",
560
      "mpegversion", G_TYPE_INT, faac->mpegversion,
561 562
      "channels", G_TYPE_INT, info->channels,
      "rate", G_TYPE_INT, info->rate,
563
      "stream-format", G_TYPE_STRING, (faac->outputformat ? "adts" : "raw"),
564
      NULL);
565

566 567
  /* DecoderSpecificInfo is only available for mpegversion=4 */
  if (faac->mpegversion == 4) {
568 569 570 571 572 573 574
    guint8 *config = NULL;
    gulong config_len = 0;

    /* get the config string */
    GST_DEBUG_OBJECT (faac, "retrieving decoder info");
    faacEncGetDecoderSpecificInfo (faac->handle, &config, &config_len);

575 576 577 578 579 580 581 582 583 584 585 586
    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);
Wim Taymans's avatar
Wim Taymans committed
587
      gst_buffer_fill (codec_data, 0, config, config_len);
588 589 590 591 592 593 594

      /* add to caps */
      gst_caps_set_simple (srccaps,
          "codec_data", GST_TYPE_BUFFER, codec_data, NULL);

      gst_buffer_unref (codec_data);
    }
595

596 597 598
    free (config);
  } else {
    const gchar *profile;
599

600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
    /* 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? */
618 619
  }

620
  GST_DEBUG_OBJECT (faac, "src pad caps: %" GST_PTR_FORMAT, srccaps);
621

622
  ret = gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (faac), srccaps);
623
  gst_caps_unref (srccaps);
624 625

  return ret;
626 627

  /* ERROR */
628
set_failed:
629
  {
630
    GST_WARNING_OBJECT (faac, "Faac doesn't support the current configuration");
631 632
    return FALSE;
  }
633
invalid_codec_data:
634
  {
635
    GST_ERROR_OBJECT (faac, "Invalid codec data");
636 637
    return FALSE;
  }
638 639
}

640
static GstFlowReturn
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
641
gst_faac_handle_frame (GstAudioEncoder * enc, GstBuffer * in_buf)
642
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
643
  GstFaac *faac = GST_FAAC (enc);
644
  GstFlowReturn ret = GST_FLOW_OK;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
645
  GstBuffer *out_buf;
Wim Taymans's avatar
Wim Taymans committed
646
  gsize size, ret_size;
647
  GstMapInfo map, omap;
Wim Taymans's avatar
Wim Taymans committed
648
  guint8 *data;
649 650
  GstAudioInfo *info =
      gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (faac));
651

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
652
  out_buf = gst_buffer_new_and_alloc (faac->bytes);
653
  gst_buffer_map (out_buf, &omap, GST_MAP_WRITE);
654

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
655
  if (G_LIKELY (in_buf)) {
656 657 658 659 660 661 662
    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]);
    }
663 664 665
    gst_buffer_map (in_buf, &map, GST_MAP_READ);
    data = map.data;
    size = map.size;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
666 667 668
  } else {
    data = NULL;
    size = 0;
669 670
  }

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
671
  if (G_UNLIKELY ((ret_size = faacEncEncode (faac->handle, (gint32 *) data,
672
                  size / (info->finfo->width / 8), omap.data, omap.size)) < 0))
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
673 674
    goto encode_failed;

675
  if (in_buf)
676
    gst_buffer_unmap (in_buf, &map);
Wim Taymans's avatar
Wim Taymans committed
677

678
  GST_LOG_OBJECT (faac, "encoder return: %" G_GSIZE_FORMAT, ret_size);
Wim Taymans's avatar
Wim Taymans committed
679

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
680
  if (ret_size > 0) {
681 682
    gst_buffer_unmap (out_buf, &omap);
    gst_buffer_resize (out_buf, 0, ret_size);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
683
    ret = gst_audio_encoder_finish_frame (enc, out_buf, faac->samples);
Wim Taymans's avatar
Wim Taymans committed
684
  } else {
685
    gst_buffer_unmap (out_buf, &omap);
Wim Taymans's avatar
Wim Taymans committed
686
    gst_buffer_unref (out_buf);
687 688 689 690
    /* re-create encoder after final flush */
    if (!in_buf) {
      GST_DEBUG_OBJECT (faac, "flushed; recreating encoder");
      gst_faac_close_encoder (faac);
691
      if (!gst_faac_open_encoder (faac, gst_audio_encoder_get_audio_info (enc)))
692 693
        ret = GST_FLOW_ERROR;
    }
694 695
  }

696 697 698 699 700 701
  return ret;

  /* ERRORS */
encode_failed:
  {
    GST_ELEMENT_ERROR (faac, LIBRARY, ENCODE, (NULL), (NULL));
702
    if (in_buf)
703 704
      gst_buffer_unmap (in_buf, &map);
    gst_buffer_unmap (out_buf, &omap);
Wim Taymans's avatar
Wim Taymans committed
705
    gst_buffer_unref (out_buf);
706 707 708 709
    return GST_FLOW_ERROR;
  }
}

710
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
711 712
gst_faac_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
713 714 715
{
  GstFaac *faac = GST_FAAC (object);

716
  GST_OBJECT_LOCK (faac);
717

718
  switch (prop_id) {
719 720 721
    case PROP_QUALITY:
      faac->quality = g_value_get_int (value);
      break;
722
    case PROP_BITRATE:
723 724
      faac->bitrate = g_value_get_int (value);
      break;
725 726 727
    case PROP_RATE_CONTROL:
      faac->brtype = g_value_get_enum (value);
      break;
728
    case PROP_TNS:
729 730
      faac->tns = g_value_get_boolean (value);
      break;
731
    case PROP_MIDSIDE:
732 733
      faac->midside = g_value_get_boolean (value);
      break;
734
    case PROP_SHORTCTL:
735 736 737 738 739 740
      faac->shortctl = g_value_get_enum (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
741

742
  GST_OBJECT_UNLOCK (faac);
743 744 745
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
746 747
gst_faac_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
748 749 750
{
  GstFaac *faac = GST_FAAC (object);

751
  GST_OBJECT_LOCK (faac);
752

753
  switch (prop_id) {
754 755 756
    case PROP_QUALITY:
      g_value_set_int (value, faac->quality);
      break;
757
    case PROP_BITRATE:
758 759
      g_value_set_int (value, faac->bitrate);
      break;
760 761 762
    case PROP_RATE_CONTROL:
      g_value_set_enum (value, faac->brtype);
      break;
763
    case PROP_TNS:
764 765
      g_value_set_boolean (value, faac->tns);
      break;
766
    case PROP_MIDSIDE:
767 768
      g_value_set_boolean (value, faac->midside);
      break;
769
    case PROP_SHORTCTL:
770 771 772 773 774 775
      g_value_set_enum (value, faac->shortctl);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
776

777
  GST_OBJECT_UNLOCK (faac);
778 779 780
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
781
plugin_init (GstPlugin * plugin)
782
{
783 784
  return gst_element_register (plugin, "faac", GST_RANK_SECONDARY,
      GST_TYPE_FAAC);
785 786
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
787 788
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
789
    faac,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
790
    "Free AAC Encoder (FAAC)",
791
    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)