Android VLC播放器二次開發3——音樂播放(歌曲列表+歌詞同步滾動)


  今天講一下對VLC播放器音頻播放功能進行二次開發,講解如何改造音樂播放相關功能。最近一直在忙着優化視頻解碼部分代碼,因為我的視頻播放器需要在一台主頻比較低的機器上跑(800M主頻),所以視頻解碼能力受到極大考驗,VLC的解碼庫挺復雜,花了兩三周時間,也只看了點皮毛。

  這里說幾句題外話,中間也嘗試過使用其他的解碼器,其中選了目前比較有名的Vitamio來試驗,不過它讓我大失所望,對於720*420的視頻解碼能力竟然還不如Beta版的VLC的解碼,我測試一個立方體旋轉視頻,播放的時候,整個視頻畫面變形了o(╯□╰)o。這里噴一下Vitamio4.0,在中低端機器上表現實在讓人失望。不過對於1280*720視頻,它解碼能力比目前的VLC表現好很多。但是對於低碼率的視頻都解析不好,沒辦法只能放棄(不知為何Vitamio對高分辨率解析不錯,但是中低分辨率解析一團糟,我特意使用Vitamio官方的VPlayer測試也是這樣)。最后還是選擇自己去優化一下視頻視頻解碼。這方面以前在PC上也做過,所以還是有點經驗,后面一段時間估計要在這方面花不少精力。

 

  言歸正傳,幾天講一下對音樂播放方面二次開發,主要是因為我的多媒體程序是放到平板上面運行,所以屏幕空間比較大。原生的VLC的音樂播放界面有點簡潔,因此增加了一些功能。下面針對一些開發流程和VLC音頻控制(java層)播放講解。下面是我修改后的效果:

 

  看上去變化挺大,其實界面功能改動不是很多,主要增加了一個歌曲列表和LRC歌詞顯示。然后對布局進行了調整,主要是針對平板電腦屏幕空間比較大進行排布。下面針對歌曲列表和歌詞顯示以及開發過程中遇到的問題,講解一下。

(PS:新建的QQ群,有興趣可以加入一起討論:Android群:322599434)

 

1、增加歌曲列表

  這個不算什么新功能,原生VLC里面已經做得很好。你要做的其實很簡單,只要把相應的適配器數據導入到新的列表控件即可:

//Edited by mythou
//http://www.cnblogs.com/mythou/
 mSongsAdapter= new AudioListAdapter(getActivity());
 setListAdapter(mSongsAdapter);

這里說一下AudioAdapter適配器,它是生成所有歌曲列表信息的數據適配器,我們看看AudioListAdapter生成元素的getView方法:

//Edited by mythou
//http://www.cnblogs.com/mythou/
  public View getView(int position, View convertView, ViewGroup parent)
    {
        ViewHolder holder;
        View v = convertView;
     //使用緩沖機制,沒有View 緩存的時候才重新加載生成新的View
if (v == null) { LayoutInflater inflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = inflater.inflate(R.layout.audio_browser_item, parent, false); holder = new ViewHolder(); holder.layout = (View) v.findViewById(R.id.layout_item); holder.cover = (ImageView) v.findViewById(R.id.cover); holder.title = (TextView) v.findViewById(R.id.title); holder.artist = (TextView) v.findViewById(R.id.artist); v.setTag(holder); } else holder = (ViewHolder) v.getTag(); Media media = getItem(position);      //獲取歌曲ID3信息的封面,ID3信息都是VLC Lib庫下面用JNI實現的 Bitmap cover = AudioUtil.getCover(v.getContext(), media, 64); if (cover == null) cover = BitmapCache.GetFromResource(v, R.drawable.icon); holder.cover.setImageBitmap(cover); Util.setItemBackground(holder.layout, position); holder.title.setText(media.getTitle()); ColorStateList titleColor = v.getResources().getColorStateList( mCurrentIndex == position ? R.color.list_title_last : R.color.list_title); holder.title.setTextColor(titleColor); holder.artist.setText(media.getSubtitle()); return v; }

  上面就是適配器的主要getView方法,其他的方法跟我們使用基本List的時候需要的Adapter一樣,這里不多說。需要注意的是,這里的ListView都是用了緩存機制,這樣可以加快執行速度,也可以減少內存使用。是優化ListView的第一個需要改善的地方。

  這里補充一點有關ID3信息獲取問題,VLC里面默認的ID3信息獲取都是UTF-8編碼,對於很多國外的歌曲沒有任何問題。不過對於國內部分歌曲,ID3信息是使用GB2312編碼,最終會導致顯示亂碼問題。這個對於Android默認播放器也存在這個問題,因此為了更好兼容國內GBK或者G2312編碼的歌曲,還需要對VLC Lib里面有關獲取ID3信息的代碼進行判斷,加入轉碼機制。

 

2、VLC多媒體數據庫

  下面簡單講一下VLC里面多媒體數據管理,如果是插拔卡后。程序第一次啟動會重新掃描多媒體文件,並生成多媒體數據庫。這一點跟Android自帶的MediaScanner服務差不多。有關掃描的部分這里先不說,今天主要是講講如何獲取音樂部分的數據。

//Edited by mythou
//http://www.cnblogs.com/mythou/
     List<Media> audioList;
        List<String> itemList;
        String currentItem = null;
        int currentIndex = -1;

        if (name == null || mode == AudioBrowserFragment.MODE_SONG)
        {
            mTitle.setText(R.string.songs);
            itemList = AudioServiceController.getInstance().getItems();
            currentItem = AudioServiceController.getInstance().getItem();
            audioList = MediaLibrary.getInstance(getActivity()).getMediaItems(
                    itemList);
        } 
     else { mTitle.setText(name2 != null ? name2 : name); audioList = MediaLibrary.getInstance(getActivity()).getAudioItems( name, name2, mode); } mSongsAdapter.clear(); for (int i = 0; i < audioList.size(); i++) { Media media = audioList.get(i); if (currentItem != null && currentItem.equals(media.getLocation())) currentIndex = i; mSongsAdapter.add(media); }

  上面是獲取Audio音頻數據的方法,主要是通過MediaLibrary類實現,通過MediaLibrary接口可以獲取到一個Audio音頻文件的List表,我們的列表數據都是基於這里獲取的,只要設置到適配器里面就可以。、

  除了MediaLibrary以外,Media類也是我們需要關注的,它是一個抽象了所有多媒體文件屬性的類。用於保存多媒體文件相關數據以及識別哪些文件類型是我們支持的。具體源碼請自行查看,代碼難度不大,不過可以學到如何編寫一個大程序時分模塊的思路。

  下面就是程序默認支持的音頻文件過濾:

//Edited by mythou
//http://www.cnblogs.com/mythou/
  String[] audio_extensions = {
                ".3ga", ".a52", ".aac", ".ac3", ".adt", ".adts", ".aif", ".aifc", ".aiff", ".amr",
                ".aob", ".ape", ".awb", ".caf", ".dts", ".flac", ".it", ".m4a", ".m4p",
                ".mid", ".mka", ".mlp", ".mod", ".mpa", ".mp1", ".mp2", ".mp3", ".mpc", ".mpga",
                ".oga", ".ogg", ".oma", ".opus", ".ra", ".ram", ".rmi", ".s3m", ".spx", ".tta",
                ".voc", ".vqf", ".w64", ".wav", ".wma", ".wv", ".xa", ".xm" };

 

3、歌詞同步滾動

  歌詞顯示這部分是我另外加上去的,因為現在的Android市場上的音樂播放器,基本都是支持歌詞現在的,歌詞現在分兩部分,一部分是本地歌詞支持,另外一部分是下載在線歌詞。對於目前的應用環境來說,這兩個功能都很重要。java解析LRC類型歌詞的方法網上已經有很多,這個從以前java時代就有很多好的解析類,我們自己也沒必要重新寫一個,因此我也是在網絡上找了一個解析LRC歌詞比較好的方法,直接引用。然后根據Android的環境,重載了一個TextView用來滾動顯示歌詞。

  這里簡單說說歌詞同步滾動問題,我們把LRC歌詞分析出來后,保存到一個數據列表里面,然后根據歌曲播放的時間,動態高亮顯示對應歌詞即可。對LRC熟悉的朋友應該都明白如何工作。這方面資料網上很多,基本上只要找個解析LRC的類,然后重載一下TextView即可實現。

  對於網絡下載歌詞,因為我自己目前沒有歌詞服務器,只能依靠第三方的開放平台。我這里選用了baidu的開發平台服務。相關方法可以查看百度開發者平台網站的相關開發包。里面提供了Demo和詳細接口文檔。

  我這里只是做個引導,具體加入到自己工程方法,需要自己實踐。這個難度不大,baidu的開發平台還是做得很好,接口容易使用。代碼我這里就不給出來了。這個整合難度不高,只要自己動動手都能實現。

 

4、JNI調試Log輸出問題

  如果你打開Logcat看過VLC播放音樂后,會發現它會一直打印Log。這個雖然說不會很占用資源,不過一直打印Log,自己看着就不爽。而且也影響我們看自己的輸入打印信息。所以我們可以把它關了,一來可以清爽很多,二來也可以節省資源。

 
         
//Edited by mythou
//http://www.cnblogs.com/mythou/
Linux-VLC-Project\android-vlc-project\android\vlc\modules\audio_output\opensles_android.c

  上面是那個一直打印Buffer的路徑,Linux-VLC-Project是我VLC在Linux下面的根目錄。你可以按照我上面源碼路徑找到對應的C文件。下面就是一個時間輸出的時候,會打印音頻Buffer的接口,只要把msg_Dbg屏蔽即可。從這里我們也可以學到如何在JNI的C/C++代碼里面輸入Log到Logcat。如何實現,自己看看源碼吧,不過需要你有點C/C++的基礎。

 
         
//Edited by mythou
//http://www.cnblogs.com/mythou/
static int TimeGet(audio_output_t* aout, mtime_t* restrict drift)
{
    aout_sys_t *sys = aout->sys;

    SLAndroidSimpleBufferQueueState st;
    SLresult res = GetState(sys->playerBufferQueue, &st);
    if (unlikely(res != SL_RESULT_SUCCESS)) {
        msg_Err(aout, "Could not query buffer queue state in TimeGet (%lu)", res);
        return -1;
    }

    vlc_mutex_lock(&sys->lock);
    bool started = sys->started;
    vlc_mutex_unlock(&sys->lock);

    if (!started)
        return -1;

    *drift = (CLOCK_FREQ * OPENSLES_BUFLEN * st.count / 1000)
        + sys->samples * CLOCK_FREQ / sys->rate;

    msg_Dbg(aout, "latency %"PRId64" ms, %d/%d buffers", *drift / 1000,
        (int)st.count, OPENSLES_BUFFERS);

    return 0;
}

  整個VLC項目的核心還是在VLC的解碼庫里面,雖然Java層我們可以學到很多Android的應用開發知識,不過對於一個音視頻播放器來說,解碼才是核心。如果對這方面有興趣的朋友可以好好研究一下,因為這個研究熟悉了,基本上跟你做什么平台區別不大,目前所有平台播放器基本都是基於FFMpeg解碼庫移植。

 

5、結語

  今天就講到這里吧,其實java層改動並不難,我這里只是給個簡單思路,有這方面需求的可以自己看源碼,結合自己需要實踐一下。編程還是需要自己多寫寫代碼才能有長進。

  接下來一段時間應該會好好分析VLC的解碼庫,看它的解碼庫,真是考驗C的基本功。Java層的分析暫時就到這里,剩余的很多模塊,大家可以自己結合需要,自行分析和修改。

 

2013-8-16 

Edited by 泡泡糖

 

系列文章

Linux 下編譯Android-VLC開源播放器詳解(附源碼下載)

Android VLC播放器二次開發1——程序結構分析

Android VLC播放器二次開發2——CPU類型檢查+界面初始化

 

Edited by mythou

原創博文,轉載請標明出處:http://www.cnblogs.com/mythou/p/3293582.html 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM