Merge branch 'master' into 0.11
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 10 Jan 2012 14:17:11 +0000 (15:17 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 10 Jan 2012 14:17:11 +0000 (15:17 +0100)
Conflicts:
ext/a52dec/gsta52dec.c
ext/a52dec/gsta52dec.h
ext/lame/gstlame.c
ext/lame/gstlame.h
ext/lame/gstlamemp3enc.c
ext/mad/gstmad.c
ext/mad/gstmad.h
gst/mpegaudioparse/gstmpegaudioparse.c
gst/mpegstream/gstdvddemux.c
gst/realmedia/rdtdepay.c
po/es.po
po/lv.po
po/sr.po

1  2 
ext/lame/Makefile.am
ext/lame/gstlamemp3enc.c

@@@ -1,10 -1,10 +1,10 @@@
  plugin_LTLIBRARIES = libgstlame.la
  
 -libgstlame_la_SOURCES = gstlame.c gstlamemp3enc.c plugin.c
 +libgstlame_la_SOURCES = gstlamemp3enc.c plugin.c
  libgstlame_la_CFLAGS = -DGST_USE_UNSTABLE_API \
-       $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(LAME_CFLAGS)
- libgstlame_la_LIBADD = $(LAME_LIBS) $(GST_PLUGINS_BASE_LIBS) \
-       -lgstaudio-$(GST_MAJORMINOR) $(GST_LIBS)
+         $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(LAME_CFLAGS)
+ libgstlame_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \
+       $(GST_BASE_LIBS) $(GST_LIBS) $(LAME_LIBS)
  libgstlame_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
  libgstlame_la_LIBTOOLFLAGS = --tag=disable-static
  
@@@ -486,30 -499,221 +499,226 @@@ gst_lamemp3enc_get_property (GObject * 
    }
  }
  
 -    data = gst_adapter_peek (lame->adapter, 4);
+ /* **** 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;
++    data = gst_adapter_map (lame->adapter, 4);
+     header = GST_READ_UINT32_BE (data);
++    gst_adapter_unmap (lame->adapter);
++
+     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,
+           "unexpected mp3 header with rate %u, version %u, layer %u",
+           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;
+     }
+     /* 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),
+         mp3_buf, version == 1 ? 1152 : 576);
+   }
+ 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;
+   }
+ }
  static GstFlowReturn
  gst_lamemp3enc_flush_full (GstLameMP3Enc * lame, gboolean push)
  {
    GstBuffer *buf;
    gint size;
 +  guint8 *data;
    GstFlowReturn result = GST_FLOW_OK;
+   gint av;
  
    if (!lame->lgf)
      return GST_FLOW_OK;
  
    buf = gst_buffer_new_and_alloc (7200);
 -  size = lame_encode_flush (lame->lgf, GST_BUFFER_DATA (buf), 7200);
 +  data = gst_buffer_map (buf, NULL, NULL, GST_MAP_WRITE);
 +  size = lame_encode_flush (lame->lgf, data, 7200);
  
    if (size > 0) {
 -    GST_BUFFER_SIZE (buf) = size;
 +    gst_buffer_unmap (buf, data, size);
-     if (push) {
-       GST_DEBUG_OBJECT (lame, "pushing final packet of %u bytes", size);
-       result =
-           gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (lame), buf, -1);
-     }
+     GST_DEBUG_OBJECT (lame, "collecting final %d bytes", size);
+     gst_adapter_push (lame->adapter, buf);
    } else {
 +    gst_buffer_unmap (buf, data, 0);
      GST_DEBUG_OBJECT (lame, "no final packet (size=%d, push=%d)", size, push);
      gst_buffer_unref (buf);
      result = GST_FLOW_OK;
@@@ -560,16 -781,17 +785,19 @@@ gst_lamemp3enc_handle_frame (GstAudioEn
          (short int *) data,
          num_samples / lame->num_channels, mp3_data, mp3_buffer_size);
    }
 +  gst_buffer_unmap (in_buf, data, size);
  
 -  GST_LOG_OBJECT (lame, "encoded %d bytes of audio to %d bytes of mp3",
 -      size, mp3_size);
 +  GST_LOG_OBJECT (lame, "encoded %" G_GSIZE_FORMAT " bytes of audio "
 +      "to %d bytes of mp3", size, mp3_size);
  
    if (G_LIKELY (mp3_size > 0)) {
 -    GST_BUFFER_SIZE (mp3_buf) = mp3_size;
+     /* unfortunately lame does not provide frame delineated output,
+      * so collect output and parse into frames ... */
 +    gst_buffer_unmap (mp3_buf, mp3_data, mp3_size);
-     result = gst_audio_encoder_finish_frame (enc, mp3_buf, -1);
+     gst_adapter_push (lame->adapter, mp3_buf);
+     result = gst_lamemp3enc_finish_frames (lame);
    } else {
 +    gst_buffer_unmap (mp3_buf, mp3_data, 0);
      if (mp3_size < 0) {
        /* eat error ? */
        g_warning ("error %d", mp3_size);