gstlamemp3enc.c 29.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 * Copyright (C) <2004> Wim Taymans <wim@fluendo.com>
 * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
 * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
19 20
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
21 22 23 24 25 26 27 28 29 30 31 32
 */

/**
 * SECTION:element-lamemp3enc
 * @see_also: lame, mad, vorbisenc
 *
 * This element encodes raw integer audio into an MPEG-1 layer 3 (MP3) stream.
 * Note that <ulink url="http://en.wikipedia.org/wiki/MP3">MP3</ulink> is not
 * a free format, there are licensing and patent issues to take into
 * consideration. See <ulink url="http://www.vorbis.com/">Ogg/Vorbis</ulink>
 * for a royalty free (and often higher quality) alternative.
 *
33 34
 * ## Output sample rate
 *
35 36 37 38 39
 * If no fixed output sample rate is negotiated on the element's src pad,
 * the element will choose an optimal sample rate to resample to internally.
 * For example, a 16-bit 44.1 KHz mono audio stream encoded at 48 kbit will
 * get resampled to 32 KHz.  Use filter caps on the src pad to force a
 * particular sample rate.
40 41 42
 *
 * ## Example pipelines
 *
43
 * |[
44
 * gst-launch-1.0 -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! lamemp3enc ! filesink location=sine.mp3
45 46
 * ]| Encode a test sine signal to MP3.
 * |[
47
 * gst-launch-1.0 -v autoaudiosrc ! audioconvert ! lamemp3enc target=bitrate bitrate=192 ! filesink location=alsasrc.mp3
48
 * ]| Record from a sound card using ALSA and encode to MP3 with an average bitrate of 192kbps
49
 * |[
50
 * gst-launch-1.0 -v filesrc location=music.wav ! decodebin ! audioconvert ! audioresample ! lamemp3enc target=quality quality=0 ! id3v2mux ! filesink location=music.mp3
51
 * ]| Transcode from a .wav file to MP3 (the id3v2mux element is optional) with best VBR quality
52
 * |[
53
 * gst-launch-1.0 -v cdda://5 ! audioconvert ! lamemp3enc target=bitrate cbr=true bitrate=192 ! filesink location=track5.mp3
54
 * ]| Encode Audio CD track 5 to MP3 with a constant bitrate of 192kbps
55
 * |[
56
 * gst-launch-1.0 -v audiotestsrc num-buffers=10 ! audio/x-raw,rate=44100,channels=1 ! lamemp3enc target=bitrate cbr=true bitrate=48 ! filesink location=test.mp3
57 58 59 60 61 62 63 64 65 66 67
 * ]| Encode to a fixed sample rate
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include "gstlamemp3enc.h"
#include <gst/gst-i18n-plugin.h>

68 69 70 71 72
/* lame < 3.98 */
#ifndef HAVE_LAME_SET_VBR_QUALITY
#define lame_set_VBR_quality(flags,q) lame_set_VBR_q((flags),(int)(q))
#endif

73 74 75 76 77 78 79 80
GST_DEBUG_CATEGORY_STATIC (debug);
#define GST_CAT_DEFAULT debug

/* elementfactory information */

/* LAMEMP3ENC can do MPEG-1, MPEG-2, and MPEG-2.5, so it has 9 possible
 * sample rates it supports */
static GstStaticPadTemplate gst_lamemp3enc_sink_template =
81
    GST_STATIC_PAD_TEMPLATE ("sink",
82 83
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
Wim Taymans's avatar
Wim Taymans committed
84 85
    GST_STATIC_CAPS ("audio/x-raw, "
        "format = (string) " GST_AUDIO_NE (S16) ", "
86
        "layout = (string) interleaved, "
87
        "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, "
88 89 90 91 92 93
        "channels = (int) 1; "
        "audio/x-raw, "
        "format = (string) " GST_AUDIO_NE (S16) ", "
        "layout = (string) interleaved, "
        "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, "
        "channels = (int) 2, " "channel-mask = (bitmask) 0x3")
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    );

static GstStaticPadTemplate gst_lamemp3enc_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/mpeg, "
        "mpegversion = (int) 1, "
        "layer = (int) 3, "
        "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, "
        "channels = (int) [ 1, 2 ]")
    );

/********** Define useful types for non-programmatic interfaces **********/
enum
{
  LAMEMP3ENC_TARGET_QUALITY = 0,
  LAMEMP3ENC_TARGET_BITRATE
};

#define GST_TYPE_LAMEMP3ENC_TARGET (gst_lamemp3enc_target_get_type())
static GType
gst_lamemp3enc_target_get_type (void)
{
  static GType lame_target_type = 0;
119
  static const GEnumValue lame_targets[] = {
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    {LAMEMP3ENC_TARGET_QUALITY, "Quality", "quality"},
    {LAMEMP3ENC_TARGET_BITRATE, "Bitrate", "bitrate"},
    {0, NULL, NULL}
  };

  if (!lame_target_type) {
    lame_target_type =
        g_enum_register_static ("GstLameMP3EncTarget", lame_targets);
  }
  return lame_target_type;
}

enum
{
  LAMEMP3ENC_ENCODING_ENGINE_QUALITY_FAST = 0,
  LAMEMP3ENC_ENCODING_ENGINE_QUALITY_STANDARD,
  LAMEMP3ENC_ENCODING_ENGINE_QUALITY_HIGH
};

#define GST_TYPE_LAMEMP3ENC_ENCODING_ENGINE_QUALITY (gst_lamemp3enc_encoding_engine_quality_get_type())
static GType
gst_lamemp3enc_encoding_engine_quality_get_type (void)
{
  static GType lame_encoding_engine_quality_type = 0;
144
  static const GEnumValue lame_encoding_engine_quality[] = {
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
    {0, "Fast", "fast"},
    {1, "Standard", "standard"},
    {2, "High", "high"},
    {0, NULL, NULL}
  };

  if (!lame_encoding_engine_quality_type) {
    lame_encoding_engine_quality_type =
        g_enum_register_static ("GstLameMP3EncEncodingEngineQuality",
        lame_encoding_engine_quality);
  }
  return lame_encoding_engine_quality_type;
}

/********** Standard stuff for signals and arguments **********/

enum
{
  ARG_0,
  ARG_TARGET,
  ARG_BITRATE,
  ARG_CBR,
167
  ARG_QUALITY,
168 169 170 171 172 173 174
  ARG_ENCODING_ENGINE_QUALITY,
  ARG_MONO
};

#define DEFAULT_TARGET LAMEMP3ENC_TARGET_QUALITY
#define DEFAULT_BITRATE 128
#define DEFAULT_CBR FALSE
175
#define DEFAULT_QUALITY 4
176 177 178
#define DEFAULT_ENCODING_ENGINE_QUALITY LAMEMP3ENC_ENCODING_ENGINE_QUALITY_STANDARD
#define DEFAULT_MONO FALSE

179 180 181 182 183 184 185 186
static gboolean gst_lamemp3enc_start (GstAudioEncoder * enc);
static gboolean gst_lamemp3enc_stop (GstAudioEncoder * enc);
static gboolean gst_lamemp3enc_set_format (GstAudioEncoder * enc,
    GstAudioInfo * info);
static GstFlowReturn gst_lamemp3enc_handle_frame (GstAudioEncoder * enc,
    GstBuffer * in_buf);
static void gst_lamemp3enc_flush (GstAudioEncoder * enc);

187 188 189 190
static void gst_lamemp3enc_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_lamemp3enc_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
191
static gboolean gst_lamemp3enc_setup (GstLameMP3Enc * lame, GstTagList ** tags);
192

Wim Taymans's avatar
Wim Taymans committed
193 194
#define gst_lamemp3enc_parent_class parent_class
G_DEFINE_TYPE (GstLameMP3Enc, gst_lamemp3enc, GST_TYPE_AUDIO_ENCODER);
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

static void
gst_lamemp3enc_release_memory (GstLameMP3Enc * lame)
{
  if (lame->lgf) {
    lame_close (lame->lgf);
    lame->lgf = NULL;
  }
}

static void
gst_lamemp3enc_finalize (GObject * obj)
{
  gst_lamemp3enc_release_memory (GST_LAMEMP3ENC (obj));

  G_OBJECT_CLASS (parent_class)->finalize (obj);
}

static void
gst_lamemp3enc_class_init (GstLameMP3EncClass * klass)
{
  GObjectClass *gobject_class;
Wim Taymans's avatar
Wim Taymans committed
217
  GstElementClass *gstelement_class;
218
  GstAudioEncoderClass *base_class;
219 220

  gobject_class = (GObjectClass *) klass;
Wim Taymans's avatar
Wim Taymans committed
221
  gstelement_class = (GstElementClass *) klass;
222
  base_class = (GstAudioEncoderClass *) klass;
223 224 225 226 227

  gobject_class->set_property = gst_lamemp3enc_set_property;
  gobject_class->get_property = gst_lamemp3enc_get_property;
  gobject_class->finalize = gst_lamemp3enc_finalize;

228 229 230 231
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_lamemp3enc_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_lamemp3enc_sink_template);
Wim Taymans's avatar
Wim Taymans committed
232

233
  gst_element_class_set_static_metadata (gstelement_class,
Wim Taymans's avatar
Wim Taymans committed
234 235 236 237
      "L.A.M.E. mp3 encoder", "Codec/Encoder/Audio",
      "High-quality free MP3 encoder",
      "Sebastian Dröge <sebastian.droege@collabora.co.uk>");

238 239 240 241 242 243
  base_class->start = GST_DEBUG_FUNCPTR (gst_lamemp3enc_start);
  base_class->stop = GST_DEBUG_FUNCPTR (gst_lamemp3enc_stop);
  base_class->set_format = GST_DEBUG_FUNCPTR (gst_lamemp3enc_set_format);
  base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_lamemp3enc_handle_frame);
  base_class->flush = GST_DEBUG_FUNCPTR (gst_lamemp3enc_flush);

244 245 246
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET,
      g_param_spec_enum ("target", "Target",
          "Optimize for quality or bitrate", GST_TYPE_LAMEMP3ENC_TARGET,
247 248
          DEFAULT_TARGET,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
249 250
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
      g_param_spec_int ("bitrate", "Bitrate (kb/s)",
251 252
          "Bitrate in kbit/sec (Only valid if target is bitrate, for CBR one "
          "of 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, "
253
          "256 or 320)", 8, 320, DEFAULT_BITRATE,
254
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
255
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CBR,
256
      g_param_spec_boolean ("cbr", "CBR", "Enforce constant bitrate encoding "
257
          "(Only valid if target is bitrate)", DEFAULT_CBR,
258
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
259 260
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
      g_param_spec_float ("quality", "Quality",
261 262
          "VBR Quality from 0 to 10, 0 being the best "
          "(Only valid if target is quality)", 0.0, 9.999,
263 264
          DEFAULT_QUALITY,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265 266
  g_object_class_install_property (G_OBJECT_CLASS (klass),
      ARG_ENCODING_ENGINE_QUALITY, g_param_spec_enum ("encoding-engine-quality",
267 268
          "Encoding Engine Quality", "Quality/speed of the encoding engine, "
          "this does not affect the bitrate!",
269
          GST_TYPE_LAMEMP3ENC_ENCODING_ENGINE_QUALITY,
270
          DEFAULT_ENCODING_ENGINE_QUALITY,
271
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
272 273
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MONO,
      g_param_spec_boolean ("mono", "Mono", "Enforce mono encoding",
274 275 276 277 278
          DEFAULT_MONO,
          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}

static void
Wim Taymans's avatar
Wim Taymans committed
279
gst_lamemp3enc_init (GstLameMP3Enc * lame)
280
{
281
  GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (lame));
282
}
283

284 285 286 287 288 289
static gboolean
gst_lamemp3enc_start (GstAudioEncoder * enc)
{
  GstLameMP3Enc *lame = GST_LAMEMP3ENC (enc);

  GST_DEBUG_OBJECT (lame, "start");
290 291 292 293 294

  if (!lame->adapter)
    lame->adapter = gst_adapter_new ();
  gst_adapter_clear (lame->adapter);

295
  return TRUE;
296 297 298
}

static gboolean
299
gst_lamemp3enc_stop (GstAudioEncoder * enc)
300
{
301 302 303 304
  GstLameMP3Enc *lame = GST_LAMEMP3ENC (enc);

  GST_DEBUG_OBJECT (lame, "stop");

305 306 307 308 309
  if (lame->adapter) {
    g_object_unref (lame->adapter);
    lame->adapter = NULL;
  }

310
  gst_lamemp3enc_release_memory (lame);
311 312 313 314
  return TRUE;
}

static gboolean
315
gst_lamemp3enc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
316 317 318 319 320
{
  GstLameMP3Enc *lame;
  gint out_samplerate;
  gint version;
  GstCaps *othercaps;
321
  GstClockTime latency;
322
  GstTagList *tags = NULL;
323

324
  lame = GST_LAMEMP3ENC (enc);
325

326 327 328 329 330 331
  /* parameters already parsed for us */
  lame->samplerate = GST_AUDIO_INFO_RATE (info);
  lame->num_channels = GST_AUDIO_INFO_CHANNELS (info);

  /* but we might be asked to reconfigure, so reset */
  gst_lamemp3enc_release_memory (lame);
332 333

  GST_DEBUG_OBJECT (lame, "setting up lame");
334
  if (!gst_lamemp3enc_setup (lame, &tags))
335 336 337 338 339 340 341 342 343 344
    goto setup_failed;

  out_samplerate = lame_get_out_samplerate (lame->lgf);
  if (out_samplerate == 0)
    goto zero_output_rate;
  if (out_samplerate != lame->samplerate) {
    GST_WARNING_OBJECT (lame,
        "output samplerate %d is different from incoming samplerate %d",
        out_samplerate, lame->samplerate);
  }
345
  lame->out_samplerate = out_samplerate;
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363

  version = lame_get_version (lame->lgf);
  if (version == 0)
    version = 2;
  else if (version == 1)
    version = 1;
  else if (version == 2)
    version = 3;

  othercaps =
      gst_caps_new_simple ("audio/mpeg",
      "mpegversion", G_TYPE_INT, 1,
      "mpegaudioversion", G_TYPE_INT, version,
      "layer", G_TYPE_INT, 3,
      "channels", G_TYPE_INT, lame->mono ? 1 : lame->num_channels,
      "rate", G_TYPE_INT, out_samplerate, NULL);

  /* and use these caps */
364
  gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (enc), othercaps);
365 366
  gst_caps_unref (othercaps);

367 368 369 370 371 372 373
  /* base class feedback:
   * - we will handle buffers, just hand us all available
   * - report latency */
  latency = gst_util_uint64_scale_int (lame_get_framesize (lame->lgf),
      GST_SECOND, lame->samplerate);
  gst_audio_encoder_set_latency (enc, latency, latency);

374
  if (tags) {
375
    gst_audio_encoder_merge_tags (enc, tags, GST_TAG_MERGE_REPLACE);
376
    gst_tag_list_unref (tags);
377
  }
378

379 380 381 382
  return TRUE;

zero_output_rate:
  {
383
    if (tags)
384
      gst_tag_list_unref (tags);
385
    GST_ELEMENT_ERROR (lame, LIBRARY, SETTINGS, (NULL),
386
        ("LAME mp3 audio decided on a zero sample rate"));
387 388 389 390 391
    return FALSE;
  }
setup_failed:
  {
    GST_ELEMENT_ERROR (lame, LIBRARY, SETTINGS,
392
        (_("Failed to configure LAME mp3 audio encoder. Check your encoding parameters.")), (NULL));
393 394 395 396 397 398 399 400 401 402 403 404 405 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
    return FALSE;
  }
}

/* <php-emulation-mode>three underscores for ___rate is really really really
 * private as opposed to one underscore<php-emulation-mode> */
/* call this MACRO outside of the NULL state so that we have a higher chance
 * of actually having a pipeline and bus to get the message through */

#define CHECK_AND_FIXUP_BITRATE(obj,param,rate)		 		  \
G_STMT_START {                                                            \
  gint ___rate = rate;                                                    \
  gint maxrate = 320;							  \
  gint multiplier = 64;							  \
  if (rate == 0) {                                                        \
    ___rate = rate;                                                       \
  } else if (rate <= 64) {				                  \
    maxrate = 64; multiplier = 8;                                         \
    if ((rate % 8) != 0) ___rate = GST_ROUND_UP_8 (rate); 		  \
  } else if (rate <= 128) {						  \
    maxrate = 128; multiplier = 16;                                       \
    if ((rate % 16) != 0) ___rate = GST_ROUND_UP_16 (rate);               \
  } else if (rate <= 256) {						  \
    maxrate = 256; multiplier = 32;                                       \
    if ((rate % 32) != 0) ___rate = GST_ROUND_UP_32 (rate);               \
  } else if (rate <= 320) { 						  \
    maxrate = 320; multiplier = 64;                                       \
    if ((rate % 64) != 0) ___rate = GST_ROUND_UP_64 (rate);               \
  }                                                                       \
  if (___rate != rate) {                                                  \
    GST_ELEMENT_WARNING (obj, LIBRARY, SETTINGS,			  \
      (_("The requested bitrate %d kbit/s for property '%s' "             \
       "is not allowed. "  					          \
       "The bitrate was changed to %d kbit/s."), rate,		          \
         param,  ___rate), 					          \
       ("A bitrate below %d should be a multiple of %d.", 		  \
          maxrate, multiplier));		  			  \
    rate = ___rate;                                                       \
  }                                                                       \
} G_STMT_END

static void
gst_lamemp3enc_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstLameMP3Enc *lame;

  lame = GST_LAMEMP3ENC (object);

  switch (prop_id) {
    case ARG_TARGET:
      lame->target = g_value_get_enum (value);
      break;
    case ARG_BITRATE:
      lame->bitrate = g_value_get_int (value);
      break;
    case ARG_CBR:
      lame->cbr = g_value_get_boolean (value);
      break;
452 453
    case ARG_QUALITY:
      lame->quality = g_value_get_float (value);
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
      break;
    case ARG_ENCODING_ENGINE_QUALITY:
      lame->encoding_engine_quality = g_value_get_enum (value);
      break;
    case ARG_MONO:
      lame->mono = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_lamemp3enc_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
  GstLameMP3Enc *lame;

  lame = GST_LAMEMP3ENC (object);

  switch (prop_id) {
    case ARG_TARGET:
      g_value_set_enum (value, lame->target);
      break;
    case ARG_BITRATE:
      g_value_set_int (value, lame->bitrate);
      break;
    case ARG_CBR:
      g_value_set_boolean (value, lame->cbr);
      break;
485 486
    case ARG_QUALITY:
      g_value_set_float (value, lame->quality);
487 488 489 490 491 492 493 494 495 496 497 498 499
      break;
    case ARG_ENCODING_ENGINE_QUALITY:
      g_value_set_enum (value, lame->encoding_engine_quality);
      break;
    case ARG_MONO:
      g_value_set_boolean (value, lame->mono);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
/* **** credits go to mpegaudioparse **** */

static const guint mp3types_bitrates[2][3][16] = {
  {
        {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,},
        {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,},
        {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,}
      },
  {
        {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,},
        {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,},
        {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}
      },
};

static const guint mp3types_freqs[3][3] = { {44100, 48000, 32000},
{22050, 24000, 16000},
{11025, 12000, 8000}
};

static inline guint
mp3_type_frame_length_from_header (GstLameMP3Enc * lame, guint32 header,
    guint * put_version, guint * put_layer, guint * put_channels,
    guint * put_bitrate, guint * put_samplerate, guint * put_mode,
    guint * put_crc)
{
  guint length;
  gulong mode, samplerate, bitrate, layer, channels, padding, crc;
  gulong version;
  gint lsf, mpg25;

  if (header & (1 << 20)) {
    lsf = (header & (1 << 19)) ? 0 : 1;
    mpg25 = 0;
  } else {
    lsf = 1;
    mpg25 = 1;
  }

  version = 1 + lsf + mpg25;

  layer = 4 - ((header >> 17) & 0x3);

  crc = (header >> 16) & 0x1;

  bitrate = (header >> 12) & 0xF;
  bitrate = mp3types_bitrates[lsf][layer - 1][bitrate] * 1000;
  /* The caller has ensured we have a valid header, so bitrate can't be
     zero here. */
  g_assert (bitrate != 0);

  samplerate = (header >> 10) & 0x3;
  samplerate = mp3types_freqs[lsf + mpg25][samplerate];

  padding = (header >> 9) & 0x1;

  mode = (header >> 6) & 0x3;
  channels = (mode == 3) ? 1 : 2;

  switch (layer) {
    case 1:
      length = 4 * ((bitrate * 12) / samplerate + padding);
      break;
    case 2:
      length = (bitrate * 144) / samplerate + padding;
      break;
    default:
    case 3:
      length = (bitrate * 144) / (samplerate << lsf) + padding;
      break;
  }

  GST_DEBUG_OBJECT (lame, "Calculated mp3 frame length of %u bytes", length);
  GST_DEBUG_OBJECT (lame, "samplerate = %lu, bitrate = %lu, version = %lu, "
      "layer = %lu, channels = %lu", samplerate, bitrate, version,
      layer, channels);

  if (put_version)
    *put_version = version;
  if (put_layer)
    *put_layer = layer;
  if (put_channels)
    *put_channels = channels;
  if (put_bitrate)
    *put_bitrate = bitrate;
  if (put_samplerate)
    *put_samplerate = samplerate;
  if (put_mode)
    *put_mode = mode;
  if (put_crc)
    *put_crc = crc;

  return length;
}

static gboolean
mp3_sync_check (GstLameMP3Enc * lame, unsigned long head)
{
  GST_DEBUG_OBJECT (lame, "checking mp3 header 0x%08lx", head);
  /* if it's not a valid sync */
  if ((head & 0xffe00000) != 0xffe00000) {
    GST_WARNING_OBJECT (lame, "invalid sync");
    return FALSE;
  }
  /* if it's an invalid MPEG version */
  if (((head >> 19) & 3) == 0x1) {
    GST_WARNING_OBJECT (lame, "invalid MPEG version: 0x%lx", (head >> 19) & 3);
    return FALSE;
  }
  /* if it's an invalid layer */
  if (!((head >> 17) & 3)) {
    GST_WARNING_OBJECT (lame, "invalid layer: 0x%lx", (head >> 17) & 3);
    return FALSE;
  }
  /* if it's an invalid bitrate */
  if (((head >> 12) & 0xf) == 0x0) {
    GST_WARNING_OBJECT (lame, "invalid bitrate: 0x%lx."
        "Free format files are not supported yet", (head >> 12) & 0xf);
    return FALSE;
  }
  if (((head >> 12) & 0xf) == 0xf) {
    GST_WARNING_OBJECT (lame, "invalid bitrate: 0x%lx", (head >> 12) & 0xf);
    return FALSE;
  }
  /* if it's an invalid samplerate */
  if (((head >> 10) & 0x3) == 0x3) {
    GST_WARNING_OBJECT (lame, "invalid samplerate: 0x%lx", (head >> 10) & 0x3);
    return FALSE;
  }

  if ((head & 0x3) == 0x2) {
    /* Ignore this as there are some files with emphasis 0x2 that can
     * be played fine. See BGO #537235 */
    GST_WARNING_OBJECT (lame, "invalid emphasis: 0x%lx", head & 0x3);
  }

  return TRUE;
}

/* **** end mpegaudioparse **** */

static GstFlowReturn
gst_lamemp3enc_finish_frames (GstLameMP3Enc * lame)
{
  gint av;
  guint header;
  GstFlowReturn result = GST_FLOW_OK;

  /* limited parsing, we don't expect to lose sync here */
  while ((result == GST_FLOW_OK) &&
      ((av = gst_adapter_available (lame->adapter)) > 4)) {
    guint rate, version, layer, size;
    GstBuffer *mp3_buf;
    const guint8 *data;
654
    guint samples_per_frame;
655

656
    data = gst_adapter_map (lame->adapter, 4);
657
    header = GST_READ_UINT32_BE (data);
658 659
    gst_adapter_unmap (lame->adapter);

660 661 662 663 664 665 666 667
    if (!mp3_sync_check (lame, header))
      goto invalid_header;

    size = mp3_type_frame_length_from_header (lame, header, &version, &layer,
        NULL, NULL, &rate, NULL, NULL);

    if (G_UNLIKELY (layer != 3 || rate != lame->out_samplerate)) {
      GST_DEBUG_OBJECT (lame,
668
          "unexpected mp3 header with rate %u, version %u, layer %u",
669 670 671 672 673 674 675 676 677 678
          rate, version, layer);
      goto invalid_header;
    }

    if (size > av) {
      /* pretty likely to occur when lame is holding back on us */
      GST_LOG_OBJECT (lame, "frame size %u (> %d)", size, av);
      break;
    }

679 680 681 682 683 684 685
    /* Account for the internal resampling, finish frame really wants to
     * know about the number of incoming samples
     */
    samples_per_frame = (version == 1) ? 1152 : 576;
    samples_per_frame *= lame->samplerate;
    samples_per_frame /= lame->out_samplerate;

686 687 688 689
    /* should be ok now */
    mp3_buf = gst_adapter_take_buffer (lame->adapter, size);
    /* number of samples for MPEG-1, layer 3 */
    result = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (lame),
690
        mp3_buf, samples_per_frame);
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
  }

exit:
  return result;

  /* ERRORS */
invalid_header:
  {
    GST_ELEMENT_ERROR (lame, STREAM, ENCODE,
        ("invalid lame mp3 sync header %08X", header), (NULL));
    result = GST_FLOW_ERROR;
    goto exit;
  }
}

706 707
static GstFlowReturn
gst_lamemp3enc_flush_full (GstLameMP3Enc * lame, gboolean push)
708
{
709
  GstBuffer *buf;
Wim Taymans's avatar
Wim Taymans committed
710
  GstMapInfo map;
711 712
  gint size;
  GstFlowReturn result = GST_FLOW_OK;
713
  gint av;
714

715 716
  if (!lame->lgf)
    return GST_FLOW_OK;
717

718
  buf = gst_buffer_new_and_alloc (7200);
Wim Taymans's avatar
Wim Taymans committed
719 720
  gst_buffer_map (buf, &map, GST_MAP_WRITE);
  size = lame_encode_flush (lame->lgf, map.data, 7200);
Wim Taymans's avatar
Wim Taymans committed
721 722

  if (size > 0) {
Wim Taymans's avatar
Wim Taymans committed
723 724
    gst_buffer_unmap (buf, &map);
    gst_buffer_resize (buf, 0, size);
725 726
    GST_DEBUG_OBJECT (lame, "collecting final %d bytes", size);
    gst_adapter_push (lame->adapter, buf);
727
  } else {
Wim Taymans's avatar
Wim Taymans committed
728
    gst_buffer_unmap (buf, &map);
729 730 731
    GST_DEBUG_OBJECT (lame, "no final packet (size=%d, push=%d)", size, push);
    gst_buffer_unref (buf);
    result = GST_FLOW_OK;
732
  }
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748

  if (push) {
    result = gst_lamemp3enc_finish_frames (lame);
  } else {
    /* never mind */
    gst_adapter_clear (lame->adapter);
  }

  /* either way, we expect nothing left */
  if ((av = gst_adapter_available (lame->adapter))) {
    /* should this be more fatal ?? */
    GST_WARNING_OBJECT (lame, "unparsed %d bytes left after flushing", av);
    /* clean up anyway */
    gst_adapter_clear (lame->adapter);
  }

749 750 751 752 753 754 755
  return result;
}

static void
gst_lamemp3enc_flush (GstAudioEncoder * enc)
{
  gst_lamemp3enc_flush_full (GST_LAMEMP3ENC (enc), FALSE);
756 757 758
}

static GstFlowReturn
759
gst_lamemp3enc_handle_frame (GstAudioEncoder * enc, GstBuffer * in_buf)
760 761 762
{
  GstLameMP3Enc *lame;
  gint mp3_buffer_size, mp3_size;
763
  GstBuffer *mp3_buf;
764 765
  GstFlowReturn result;
  gint num_samples;
Wim Taymans's avatar
Wim Taymans committed
766
  GstMapInfo in_map, mp3_map;
767

768
  lame = GST_LAMEMP3ENC (enc);
769

770 771 772
  /* squeeze remaining and push */
  if (G_UNLIKELY (in_buf == NULL))
    return gst_lamemp3enc_flush_full (lame, TRUE);
773

Wim Taymans's avatar
Wim Taymans committed
774
  gst_buffer_map (in_buf, &in_map, GST_MAP_READ);
775

Wim Taymans's avatar
Wim Taymans committed
776
  num_samples = in_map.size / 2;
777 778 779

  /* allocate space for output */
  mp3_buffer_size = 1.25 * num_samples + 7200;
Wim Taymans's avatar
Wim Taymans committed
780
  mp3_buf = gst_buffer_new_allocate (NULL, mp3_buffer_size, NULL);
Wim Taymans's avatar
Wim Taymans committed
781
  gst_buffer_map (mp3_buf, &mp3_map, GST_MAP_WRITE);
782 783 784 785

  /* lame seems to be too stupid to get mono interleaved going */
  if (lame->num_channels == 1) {
    mp3_size = lame_encode_buffer (lame->lgf,
Wim Taymans's avatar
Wim Taymans committed
786 787
        (short int *) in_map.data,
        (short int *) in_map.data, num_samples, mp3_map.data, mp3_buffer_size);
788 789
  } else {
    mp3_size = lame_encode_buffer_interleaved (lame->lgf,
Wim Taymans's avatar
Wim Taymans committed
790 791
        (short int *) in_map.data,
        num_samples / lame->num_channels, mp3_map.data, mp3_buffer_size);
792
  }
Wim Taymans's avatar
Wim Taymans committed
793
  gst_buffer_unmap (in_buf, &in_map);
794

795
  GST_LOG_OBJECT (lame, "encoded %" G_GSIZE_FORMAT " bytes of audio "
Wim Taymans's avatar
Wim Taymans committed
796
      "to %d bytes of mp3", in_map.size, mp3_size);
797

798
  if (G_LIKELY (mp3_size > 0)) {
799 800
    /* unfortunately lame does not provide frame delineated output,
     * so collect output and parse into frames ... */
Wim Taymans's avatar
Wim Taymans committed
801 802
    gst_buffer_unmap (mp3_buf, &mp3_map);
    gst_buffer_resize (mp3_buf, 0, mp3_size);
803 804
    gst_adapter_push (lame->adapter, mp3_buf);
    result = gst_lamemp3enc_finish_frames (lame);
805
  } else {
Wim Taymans's avatar
Wim Taymans committed
806
    gst_buffer_unmap (mp3_buf, &mp3_map);
807 808 809
    if (mp3_size < 0) {
      /* eat error ? */
      g_warning ("error %d", mp3_size);
810
    }
811
    gst_buffer_unref (mp3_buf);
Wim Taymans's avatar
Wim Taymans committed
812
    result = GST_FLOW_OK;
813 814 815 816 817 818 819
  }

  return result;
}

/* set up the encoder state */
static gboolean
820
gst_lamemp3enc_setup (GstLameMP3Enc * lame, GstTagList ** tags)
821
{
822
  gboolean res;
823 824 825 826

#define CHECK_ERROR(command) G_STMT_START {\
  if ((command) < 0) { \
    GST_ERROR_OBJECT (lame, "setup failed: " G_STRINGIFY (command)); \
827
    if (*tags) { \
828
      gst_tag_list_unref (*tags); \
829 830
      *tags = NULL; \
    } \
831 832 833 834 835 836 837 838 839 840 841 842 843 844
    return FALSE; \
  } \
}G_STMT_END

  int retval;
  GstCaps *allowed_caps;

  GST_DEBUG_OBJECT (lame, "starting setup");

  lame->lgf = lame_init ();

  if (lame->lgf == NULL)
    return FALSE;

845
  *tags = gst_tag_list_new_empty ();
846

847 848 849 850
  /* copy the parameters over */
  lame_set_in_samplerate (lame->lgf, lame->samplerate);

  /* let lame choose default samplerate unless outgoing sample rate is fixed */
851
  allowed_caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (lame));
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874

  if (allowed_caps != NULL) {
    GstStructure *structure;
    gint samplerate;

    structure = gst_caps_get_structure (allowed_caps, 0);

    if (gst_structure_get_int (structure, "rate", &samplerate)) {
      GST_DEBUG_OBJECT (lame, "Setting sample rate to %d as fixed in src caps",
          samplerate);
      lame_set_out_samplerate (lame->lgf, samplerate);
    } else {
      GST_DEBUG_OBJECT (lame, "Letting lame choose sample rate");
      lame_set_out_samplerate (lame->lgf, 0);
    }
    gst_caps_unref (allowed_caps);
    allowed_caps = NULL;
  } else {
    GST_DEBUG_OBJECT (lame, "No peer yet, letting lame choose sample rate");
    lame_set_out_samplerate (lame->lgf, 0);
  }

  CHECK_ERROR (lame_set_num_channels (lame->lgf, lame->num_channels));
875
  CHECK_ERROR (lame_set_bWriteVbrTag (lame->lgf, 0));
876 877

  if (lame->target == LAMEMP3ENC_TARGET_QUALITY) {
878 879
    CHECK_ERROR (lame_set_VBR (lame->lgf, vbr_default));
    CHECK_ERROR (lame_set_VBR_quality (lame->lgf, lame->quality));
880 881
  } else {
    if (lame->cbr) {
882
      CHECK_AND_FIXUP_BITRATE (lame, "bitrate", lame->bitrate);
883 884 885 886 887 888
      CHECK_ERROR (lame_set_VBR (lame->lgf, vbr_off));
      CHECK_ERROR (lame_set_brate (lame->lgf, lame->bitrate));
    } else {
      CHECK_ERROR (lame_set_VBR (lame->lgf, vbr_abr));
      CHECK_ERROR (lame_set_VBR_mean_bitrate_kbps (lame->lgf, lame->bitrate));
    }
889
    gst_tag_list_add (*tags, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
890
        lame->bitrate * 1000, NULL);
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
  }

  if (lame->encoding_engine_quality == LAMEMP3ENC_ENCODING_ENGINE_QUALITY_FAST)
    CHECK_ERROR (lame_set_quality (lame->lgf, 7));
  else if (lame->encoding_engine_quality ==
      LAMEMP3ENC_ENCODING_ENGINE_QUALITY_HIGH)
    CHECK_ERROR (lame_set_quality (lame->lgf, 2));
  /* else default */

  if (lame->mono)
    CHECK_ERROR (lame_set_mode (lame->lgf, MONO));

  /* initialize the lame encoder */
  if ((retval = lame_init_params (lame->lgf)) >= 0) {
    /* FIXME: it would be nice to print out the mode here */
906 907 908 909
    GST_INFO
        ("lame encoder setup (target %s, quality %f, bitrate %d, %d Hz, %d channels)",
        (lame->target == LAMEMP3ENC_TARGET_QUALITY) ? "quality" : "bitrate",
        lame->quality, lame->bitrate, lame->samplerate, lame->num_channels);
910
    res = TRUE;
911 912
  } else {
    GST_ERROR_OBJECT (lame, "lame_init_params returned %d", retval);
913
    res = FALSE;
914 915 916
  }

  GST_DEBUG_OBJECT (lame, "done with setup");
917
  return res;
918 919 920 921 922 923 924 925
#undef CHECK_ERROR
}

gboolean
gst_lamemp3enc_register (GstPlugin * plugin)
{
  GST_DEBUG_CATEGORY_INIT (debug, "lamemp3enc", 0, "lame mp3 encoder");

926
  if (!gst_element_register (plugin, "lamemp3enc", GST_RANK_PRIMARY,
927 928 929 930 931
          GST_TYPE_LAMEMP3ENC))
    return FALSE;

  return TRUE;
}