Document Actions
Gstreamer 0.10 MP3 id3tag 乱码问题
Gstreamer 0.10 乱码再现
在搞定"Gstreamer (0.8.x) 读取 mp3 id3 标记乱码":http://www.gnome-cn.org/newsitems/news_item.2005-03-25.5988737797/?searchterm=gstreamer 问题一年多后的今天,我重新开始挑战世俗,征服 0.10 的乱码问题。
基本思路没有任何变化——只是将 0.10 里退化掉的功能重新补上而已。
分析v1与v2
首先我们得知这样一个事实,即 ID3v1 标记仍然遵循环境变量 GST_ID3_TAG_ENCODING 的指示,可以自动转码,出问题的是 id3v2。那么 v1 和 v2 必然被不等同地对待,我们需要找出地方。
首先从 gstmad 着手。从 gstid3tag.c 中大约1210行的地方我们找到一行字:
newtag = (GST_BUFFER_DATA (buffer));
我想,这就是区别对待 v1 和 v2 的地方吧。下载并打开 gst-plugins-base 源码后,查看 gst_tag_list_new_from_id3v1 函数。果然在 gst_tag_extract_id3v1_string 函数里找到了根据环境变量转码的代码:
env = g_getenv ("GST_ID3V1_TAG_ENCODING");
if (!env || *env == '\0')
env = g_getenv ("GST_ID3_TAG_ENCODING");
if (!env || *env == '\0')
env = g_getenv ("GST_TAG_ENCODING");
/* Try charsets specified via the environment */
if (env && *env != '\0') {
....
显然,只有 id3v1 才能享受此待遇,这就能解释上述区别现象。当然,还需要一次运行实验来验证。
然而,运行实验否定了我的猜测——gstmad中相应的函数根本就没有被执行。
运行时跟踪ID3处理的方法
用以下命令可以跟踪 gstreamer 在处理媒体文件时的日志,同时又不需要牵扯太多别的东西,是个调试 gstreamer 的好办法:
$ gst-launch -t filesrc location=变的坚强.MP3 ! decodebin ! audio/x-raw-int ! fakesink
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
FOUND TAG : found by element "id3demux0".
标题: 变的坚强
艺人: 苏慧伦
专辑: 黑白道
FOUND TAG : found by element "mad0".
layer: 3
mode: joint
emphasis: none
audio codec: MPEG-1 layer 3
bitrate: 128000
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
显然,这个mp3使用了v1 tag
现在,在 gst-launch 命令后添加 --gst-debug-level=3 命令,就会打印出无数的调试信息。在其中可以找到这样的字样:
INFO (0x82cb9a0 - 0:00:00.255922000) typefind(24146) gsttypefindelement.c(158):gst_type_find_element_have_type:<typefind> found caps application/x-id3 INFO (0x82cb9a0 - 0:00:00.255977000) GST_ELEMENT_PADS(24146) gstelement.c(817):gst_element_get_static_pad: found pad typefind:src INFO (0x82cb9a0 - 0:00:00.256337000) GST_ELEMENT_FACTORY(24146) gstelementfactory.c(363):gst_element_factory_create: creating element "id3demux"
从中我们可以看见,在所有模块开始工作之前,启动了一个叫做 id3demux 的东西。接下来,我们再通过 gdb 来运行上述命令。
输入 gdb /usr/bin/gst-launch-0.10 回车,进入后打 run -t filesrc location=变的坚强.MP3 ! decodebin ! audio/x-raw-int ! fakesink。在程序走完之前抓住机会按 ctrl-c,此时回到 gdb 命令提示符下,输入 b gst_tag_list_new_from_id3v1 。然后重新输入上述 run 命令,在被问及是否重头执行时答y。片刻,程序将断点在 new_from_id3v1 函数入口处。输入 bt 查看呼叫栈:
#0 0x0036af96 in gst_tag_list_new_from_id3v1 () from /usr/lib/libgsttag-0.10.so.0 #1 0x003e947a in id3demux_read_id3v1_tag () from /usr/lib/gstreamer-0.10/libgstid3demux.so #2 0x003e89e5 in gst_id3demux_get_type () from /usr/lib/gstreamer-0.10/libgstid3demux.so #3 0x003e8bda in gst_id3demux_get_type () from /usr/lib/gstreamer-0.10/libgstid3demux.so #4 0x4a117286 in gst_pad_set_active () from /usr/lib/libgstreamer-0.10.so.0 #5 0x4a100d8e in gst_element_release_request_pad () from /usr/lib/libgstreamer-0.10.so.0 #6 0x4a10b5f7 in gst_iterator_fold () from /usr/lib/libgstreamer-0.10.so.0 #7 0x4a1007fb in gst_element_release_request_pad () from /usr/lib/libgstreamer-0.10.so.0 #8 0x4a10099e in gst_element_release_request_pad () from /usr/lib/libgstreamer-0.10.so.0 #9 0x4a100d4d in gst_element_release_request_pad () from /usr/lib/libgstreamer-0.10.so.0 #10 0x003e8fb5 in gst_id3demux_get_type () from /usr/lib/gstreamer-0.10/libgstid3demux.so #11 0x4a0fd428 in gst_element_continue_state () from /usr/lib/libgstreamer-0.10.so.0 #12 0x4a100478 in gst_element_release_request_pad ()
从上述两点都可以看出,处理 ID3 标记已经不再是 gstmad 的任务,而被转移到了一个单独的对象,id3demux 中去了。这才是我们要追踪的目标。
修改 id3demux
确定目标后,事情就很简单了。很简单就可以在 id3demux 里找到 id3demux_id3v2_parse_frame 函数,进而是 parse_*_frame 函数,最后是 parse_insert_string_field,字符串就是在这里通过 g_convert 进行的转码。转码只有 UTF-* 和 ISO-8859-1 两种转法,当然不可能支持 locale 编码。而我们要做的就是修改这个函数,加入必须的处理。而所需的处理也是现成的——从 id3v1 的处理函数里拷贝一段过来即可。
我用 evolution 的便笺功能记笔记,但这阵子我的 evolution 持续崩溃,对我影响很大。
下载
好了,点这里下载补丁:
- gst-id3tagv2-encoding.patch , against gst-plugins-good-0.10.3
- gst-id3tagv2-encoding.patch , against gst-plugins-good CVS HEAD.
对于 FC5 用户,这里有个预编译的 RPM:
gstreamer-plugins-good-0.10.3-1xz.i386.rpm
gstreamer-plugins-good-devel-0.10.3-1xz.i386.rpm
其他所依赖的 rpm 包 "均可在此找到":http://gstreamer.freedesktop.org/pkg/fedora/5/0.10/i386/


期待后续进展