GNOME Bugzilla – Bug 620322
lame mp3 encoder does not save lame tags
Last modified: 2017-08-20 16:31:58 UTC
As originally reported here: https://thomas.apestaart.org/morituri/trac/ticket/40 It seems the gstreamer lame mp3 encoder isn't correctly saving the lame tags to the files it creates. These tags are required in order to make the mp3s gaplessly playback. For comparison, here's the tags from a gstreamer encoded mp3 and one made using lame directly: GSTREAMER: $ eyeD3 --lametag 01.\ Café\ Tacvba\ -\ Seguir\ siendo.mp3 01. Café Tacvba - Seguir siendo.mp3 [ 4.02 MB ] No LAME Tag FROM LAME: $eyeD3 --lametag Track01.mp3 Track01.mp3 [ 3.77 MB ] Encoder Version : LAME3.98r LAME Tag Revision : 0 VBR Method : Variable Bitrate method2 (mtrh) Lowpass Filter : 18500 Radio Replay Gain : -2.0 dB (Set automatically) Encoding Flags : --nspsytune --nssafejoint ATH Type : 4 Bitrate (Minimum) : 192 Encoder Delay : 576 samples Encoder Padding : 960 samples Noise Shaping : 1 Stereo Mode : Joint Unwise Settings : False Sample Frequency : 44.1 kHz MP3 Gain : 0 (+0.0 dB) Preset : V2 Surround Info : None Music Length : 3.77 MB Music CRC-16 : 07F9 LAME Tag CRC-16 : E5BF It's the encoder delay and padding tags which are especially important for gapless playback, since this information is impossible to retrieve after encoding.
Which mp3 encoder element was used here? lamemp3enc or lame?
This is using the lame element.
Is it included with the lamemp3enc element? (You shouldn't use lame anymore...)
A little different there... this time I get the lame encoder version tag, but nothing else. $ gst-launch-0.10 filesrc location=test.flac ! flacdec ! audioconvert ! lamemp3enc ! filesink location=test.mp3 Setting pipeline to PAUSED ... Pipeline is PREROLLING ... Pipeline is PREROLLED ... Setting pipeline to PLAYING ... New clock: GstSystemClock Got EOS from element "pipeline0". Execution ended after 9880881396 ns. Setting pipeline to PAUSED ... Setting pipeline to READY ... Setting pipeline to NULL ... Freeing pipeline ... $ eyeD3 --lame test.mp3 test.mp3 [ 2.82 MB ] ------------------------------------------------------------------------------- Encoder Version: LAME3.98.2 Incidentally, where would I find information about lamemp3enc? It's not listed at http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-ugly-plugins/html/
Someone should update the -ugly docs on the website... Could you file a new bug for that? :) What needs to be done to get lame output additional things in the lametag?
Do you mean when using lame manually? If so, nothing special needs to be done, lame adds them by default: $ flac -dc test.flac | lame -V0 - test2.mp3 flac 1.2.1, Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson flac comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. Type `flac' for details. test.flac: done LAME 3.98.2 64bits (http://www.mp3dev.org/) Using polyphase lowpass filter, transition band: 19383 Hz - 19916 Hz Encoding <stdin> to test2.mp3 Encoding as 44.1 kHz j-stereo MPEG-1 Layer III VBR(q=0) $ eyeD3 --lame test2.mp3 test2.mp3 [ 4.26 MB ] ------------------------------------------------------------------------------- Encoder Version : LAME3.98r LAME Tag Revision : 0 VBR Method : Variable Bitrate method2 (mtrh) Lowpass Filter : 19500 Radio Replay Gain : -2.0 dB (Set automatically) Encoding Flags : --nspsytune --nssafejoint ATH Type : 4 Bitrate (Minimum) : 32 Encoder Delay : 576 samples Encoder Padding : 2112 samples Noise Shaping : 1 Stereo Mode : Joint Unwise Settings : False Sample Frequency : 44.1 kHz MP3 Gain : 0 (+0.0 dB) Preset : V0 Surround Info : None Music Length : 4.26 MB Music CRC-16 : E8F2 LAME Tag CRC-16 : DD49
Ok, so I have no idea how to get liblame to write the lame tag :) I'll try to fix the element in the next days if I find something to generate the lame tag.
Do you want to put that into a GstTagList or emit it directly into the stream?
Directly into the stream, it's a valid MP3 frame containing no samples IIRC. I guess an element like xingmux might make sense for the lametag too though, but I'm not sure if we can easily get all required information from an mp3 stream...
I've never been too happy about lame emitting tags into the bitstream. It used to confuse muxers and other elements that expected each mp3 frame to contain 1440 samples. That was one of the reasons for why I originally removed all tag-writing from it. I'm not sure if that's still a problem, but I certainly wouldn't want gapless playback tags inside my AVI files.
> (...) That was one of the reasons for why I originally removed all > tag-writing from it. Oh it was you, wasn't it? ;-) Your reasons all seem very good, but tag writing in lame was also pretty broken for all practical purposes, see bug #329184 for details (and a whole bunch of related bugs). > I guess an element like xingmux might make sense for the lametag too though, > but I'm not sure if we can easily get all required information from an mp3 > stream... If we want to support this special lame tag, maybe we can just send a custom event with a buffer containing the tag downstream, xingmux and/or id3*mux can then pick that up?
Surely gapless encoding is something that would be an option to the encoder? The lame binary doesn't do it by default, so neither should gstreamer. That solves the AVI issue :) From rootling around in lame.h, it looks like you're supposed to call lame_encode_flush_nogap() at the end of each track followed by lame_init_bitstream() to get lame to write the header tags (assuming it can seek() the output stream?) When you start encoding a set of gapless tracks, call lame_set_nogap_total() with the total number & then call lame_set_nogap_currentindex() for each track along the way. There are some more lametag specific functions, but it looks like those are deprecated. The probably would probably be that the above calls assume that liblame can seek() around the output stream, which might not mesh very well with the way gstreamer structures things. It also needs to not fire off a new instantiation of liblame for each track, but do them all in a single pass.
Oh wait, lame_mp3_tags_fid() is deprecated, but lame_get_lametag_frame() isn't. I guess what you're supposed to do if you're driving liblame yourself like gstreamer is to call lame_encode_flush_nogap() then call lame_get_lametag_frame() to get the LAME tag frame which you write over the blank frame at the beginning of the mp3 data stream for that track. Then you call lame_set_nogap_currentindex() and pick up encoding where you left off within the same liblame instance.
Further poking around the lame mailing lists reveals that they pretty much regard the gapless bits as being deprecated: the padding data in the lametag should be all that's required, in which case there's no need to faff around with lame_set_nogap_current_index() and friends: the only thing that's required to insert the lametag at the beginning of the generated mp3 data for each track. Passing the tag down the stream for xingmux or id3mux to pick up would seem sensible.
Phil, is this still an issue with GStreamer 1.x ?
I'll check.
Definitely still an issue with GStreamer 1.x. If I use the invocation: gst-launch-1.0 audiotestsrc num-buffers=1000 ! audioconvert ! lamemp3enc ! xingmux ! filesink location=test.mp3 Then the resultant file has no lametag according to eyeD3. If I omit the xingmux filter, then the output file has a lametag with no data in it. This means that it's still impossible to rip a gapless mp3 from a CD with sound-juicer.
Phil, perhaps you could try your hand at a patch? Or at least start a patch that gets us the data that should be communicated to xingmux? I can see something that looks like LAME tags at first glance in: gst-launch-1.0 audiotestsrc num-buffers=1000 ! audioconvert ! lamemp3enc ! xingmux ! fakesink dump=true | more (But should there be some in the initial xing buffer too?) It would also be helpful if you could attach the first 100kB or so of a file that is tagged properly.