一些MP3文件在Music app中看不到Duration的Debug过程

针对Android JellyBean系统中音乐应用无法正确显示部分MP3文件的时长问题,通过详细调试发现是由于非ASCII字符导致媒体扫描器提前退出,最终定位并修复了该bug。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bug资料:

平台:Android 4.1(JellyBean)

芯片:高通

手机型号:笨蛋(开发中,不能写啦。笨蛋是我给起的代号。我这里还有小红、小黄之类的。这一台屏幕大,但比较厚,我喊它笨蛋)

问题描述:如本人标题所述,批量导入一批MP3格式的文件到手机的存储空间中,进入Music app查看歌曲列表,里面会有一些歌曲不显示duration。



Debug过程:

 Music app里面Duration是从数据库里面读取的,所以首先查看数据库中是否有相关的讯息.
     数据库位置:
          data/data/com.android.providers.media/databases/external.db
     数据库情况如下:


很明显,这些异常的歌曲在数据库中就没有duration讯息.
     到目前为止,根据已经获得的讯息,我推定如下:
     1. 问题发生在Framework层而非App层.
     2. 最可能的位置是MediaScanner模块.(这里的推定我是"根据经验",省去推导步骤.后面的过程可以印证这里的推定是正确的.)
     可能的情况猜测有两种:
     1.MediaScanner获取媒体duration讯息失败.
     2.duration讯息获取成功,但写入数据库失败.
     下面就上述两种猜测进入debug.
     根据流程,先获取duration讯息,再写入数据库.所以debug反向进行,先看是否有写入数据库.
     MediaScanner在准备写入资料的时候,会先创建一条记录,初始化各个字段的值.duration初始化的时候是0,我关心的就是,在其初始化之后,是否发生过赋值.这关系到异常文件duration=0的原因.到底是直接使用了初始化的值,还是初始化之后有赋值过,只是第二次赋值为0.
     StagefrightMediaScanner.cpp中相关代码如下:

     static const KeyMap kKeyMap[] = {
        { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER },
        { "discnumber", METADATA_KEY_DISC_NUMBER },
        { "album", METADATA_KEY_ALBUM },
        { "artist", METADATA_KEY_ARTIST },
        { "albumartist", METADATA_KEY_ALBUMARTIST },
        { "composer", METADATA_KEY_COMPOSER },
        { "genre", METADATA_KEY_GENRE },
        { "title", METADATA_KEY_TITLE },
        { "year", METADATA_KEY_YEAR },
        { "duration", METADATA_KEY_DURATION },
        { "writer", METADATA_KEY_WRITER },
        { "compilation", METADATA_KEY_COMPILATION },
        { "isdrm", METADATA_KEY_IS_DRM },
        { "width", METADATA_KEY_VIDEO_WIDTH },
        { "height", METADATA_KEY_VIDEO_HEIGHT },
    };
    static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]);
    for (size_t i = 0; i < kNumEntries; ++i) {
          ALOGV("Vinky|processFileInternal()|current is [%d]/[%d]",i,kNumEntries);//这里我输出循环的当前次数和总次数,顺便看看是否有完成所有循环.每次循环取的就是上面的kKeyMap里面写的tag值
        const char *value;
        if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) {//如果对应的Tag有值,就把该值写入前面提到的创建出来的数据库记录中.
            status = client.addStringTag(kKeyMap[i].tag, value);
            ALOGV("Vinky|processFileInternal()|client.addString tag[%s] value[%s]",  kKeyMap[i].tag, value);//如果读到duration,这里会有反应的.根据kKeyMap的记录,duration的下标应该是9号.
          if (status != OK) {
              ALOGV("Vinky|processFileInternal()|ERROR! current is [%d]/[%d]",i,kNumEntries);//这里是可能导致错误的地方,如果出错,把当前的次数打出来,可以知道在解析哪个tag的时候出错
              return MEDIA_SCAN_RESULT_ERROR;
          }
      }





     加入上述log之后,重现编译libmedia.so,替换进手机里面,重启.
     触发这段函数的方法很简单,找任何一个第三方的文件管理器,修改一个MP3文件的名字,都会触发MediaScanner对其进行扫描.
     后面的log我当时忘记截图了,我直接说结果.
     1. 正常的MP3文件,可以走完上面的15次循环.
     2. 不正常的MP3文件,走到i==3的时候,就触发了if (status != OK) 这个条件,导致返回了MEDIA_SCAN_RESULT_ERROR.这个返回导致整个循环终止,这个时候我们的duration还没轮上呢.前面说过,它是可怜的9号.

     所以,问题符合上面的情况1,MediaScanner获取媒体duration讯息失败.后面的music app使用的duration==0的值,是初始化的时候生成的.
     根据刚刚的log讯息,问题就归纳为,i==3,即解析artist的时候,导致status!=OK.(顺带一提,这里OK==0,status==1).
     i==3的时候,打出来的log如下:
          Vinky|processFileInternal()|client.addString tag[artist] value[¥j¥‥°o]
     value是一个乱码(这里根据编译器设置的编码,乱得可能不一样,但结论是一样的,这不是一个ASCII码)

     下面就转到client.addStringTag(kKeyMap[i].tag, value);里面看看发生了什麽吧.
     文件是MediaScannerClient.cpp
     函数如下:

     bool MediaScannerClient::addStringTag(const char* name, const char* value)
     ...
          if (nonAscii) {
             // save the strings for later so they can be used for native encoding detection
             mNames->push_back(name);
             mValues->push_back(value);

             return true;
         }
     ...


    
     很明显,如果value不是一个ASCII码的话,会暂存起来,到最后一起根据当前的编码做转换.(分析到这里,我看了一下这个MP3的artist,是中文的"古巨基",的确不是ASCII码)
     这里的return true很奇怪,正是这里导致了我们刚刚的status!=OK.(OK==0,status==1)
     只看逻辑的话,这里就很有问题,如果有非ASCII码的value过来,这里一定会导致外层循环中断而读不到duration的.所以我断定这里被其他同事改过,虽然这家伙很不厚道的没有留下任何comment.
     找来Google官方代码的同名文件,以及MTK平台某项目的同名文件做对比,其他的文件在这里都是return OK的.return OK的话,就不会触发外层的status!=OK,循环当然是可以走完的.
     测试return OK,bug解除.

     总结:
     1. 非ASCII码的value,是会在最后集中转换的.
     2. 永远不要以为没有加comment的地方,都是和官方原始代码一样的.

     声明:
     本文所使用代码,均来自Google官方release的Android JellyBean.
     如需转载,烦请保留本文链接,谢谢您的尊重.    




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值