Personal tools
You are here: Home 资源中心 原始部落 Happy Hacking Gstreamer 0.10 MP3 id3tag 乱码问题
Navigation
Log in


Forgot your password?
New user?
 
Document Actions

Gstreamer 0.10 MP3 id3tag 乱码问题

by Xin Zhen posted on May 14, 2006 09:13 PM last modified Jun 27, 2006 10:36 AM —
事隔1年多后,我又重新坐在了这里,重新开始解决最新版(0.10)GST乱码的问题。生活有时候就是这样无奈,唉……

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 持续崩溃,对我影响很大。

下载

好了,点这里下载补丁:

对于 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/

期待后续进展

Posted by yangh at May 16, 2006 05:50 AM
LoneStar 加油。:)

学了不少东西

Posted by heyuqi at May 16, 2006 06:24 PM

让我去找这个问题,觉得挺盲目的。

看这老大的文章,倒是学了不少东西。 :)

usage?

Posted by 倪跃 at Jun 13, 2006 08:07 AM

请问能大致说一下怎么用这个patch吗?谢谢

请教大虾!

Posted by zhangke at Jun 17, 2006 06:44 AM

看了这位大哥的文章,确是受益非浅.我最近刚开始学习关于Gstreamer这一块,这边关于刚好有个关于使用Gstreamer播放MP3的问题想请教,希望各位高手帮忙提点一二,感激不尽. 现在我这边使用Gstreamer相关播放器播放MP3时,只能听到"沙沙"声,经查看是他里面没有"mad"这个ele ment,即未经解码,通过在gst-plugin的./con figure --help确认,它里面的"mad"在编译时默认的是disable,未编译就自然没法解码.. 请教各位,怎样才能解决这个问题,正常播放MP3??谢谢!

谢谢

Posted by chibi at Jun 20, 2006 08:40 AM

非常好用,感谢

Lonestar, 不准备提交到 bugzilla.g.o 上了?

Posted by yangh at Jun 21, 2006 07:08 AM
我支持提交o

首先要b.g.o上有这个bug

Posted by Xin Zhen at Jun 26, 2006 08:00 AM
我才能提交上补丁,是吧。而现在,只要有人提这个,马上被 maintainer 标记为 resolved && invalid,我上哪儿提交去呀。