Android VLC播放器二次开发3——音乐播放(歌曲列表+歌词同步滚动)

2023-11-10

 今天讲一下对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 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android VLC播放器二次开发3——音乐播放(歌曲列表+歌词同步滚动) 的相关文章

  • 如何获取之前的碎片?

    为了在我的应用程序中重用某些片段 我需要知道哪个片段是返回堆栈上的第二个片段 为了做到这一点 我正在使用getFragmentManager getFragments 显示以下错误 但有效 FragmentManager getFragme
  • onBeaconServiceConnect 未调用

    和以前一样 我使用 Android Beacon 库 它已经工作了 我可以通过 BLE 低功耗蓝牙找到信标 但是现在 更新到最新版本的库后 现在方法onBeaconServiceConnect 不再跑了 请告诉我我需要做什么才能让它发挥作用
  • Android webview 滚动不起作用

    我正在尝试在网络视图中向下滚动到页面底部 我正在使用谷歌在其教程中提供的网络视图示例 我正在使用这行代码来尝试滚动 但它不起作用 mWebView pageDown true 关于如何使其以编程方式滚动有什么建议吗 谢谢 public cl
  • Android:使用 OAuth 访问 google 任务时出现问题

    由于 google 任务没有公共 api 我想编写解决方法并像浏览器一样请求数据 然后解析结果以进一步显示 为了访问数据 我使用 google 实现了 OAuth 身份验证来访问此 url https mail google com htt
  • 在android中通过BLE传输图像

    我使用以下代码传输 1 MB 的图像 如果在每个数据包之间实现线程延迟 则图像将成功传输 如果未设置线程延迟 则所有数据包均从BluetoothGattServer 发送 但BluetoothGattCallback 不会接收所有数据包 任
  • Manifest Merger工具:替换失败

    我正在使用一个使用自己的 android theme 的库 因此在构建时收到以下错误 错误 55 9 任务 contacit processDebugManifest 执行失败 清单合并失败 AndroidManifest xml 中的属性
  • Android 上的 SVG 支持

    Android 支持 SVG 吗 有什么例子吗 最完整的答案是这样的 Android 2 x 默认浏览器本身不支持 SVG Android 3 默认浏览器支持 SVG 要将 SVG 支持添加到 2 x 版本的平台 您有两个基本选择 安装功能
  • ffmpeg视频已压缩但无法在浏览器中播放

    我已经集成了ffmpeg4android lib 视频压缩工作正常 但视频无法在除 safari 浏览器之外的浏览器中播放 上传到服务器后 我使用了以下命令 ffmpeg y i
  • 播放 SoundCloud 曲目

    我可以在 Android 应用程序中播放 SoundCloud 中的曲目吗 我正在尝试这段代码 但它不起作用 String res https api soundcloud com tracks 84973999 stream client
  • 如何在React Native Android中获取响应头?

    您好 我想在获取 POST 请求后获取响应标头 我尝试调试看看里面有什么response with console log response 我可以从以下位置获取响应机构responseData但我不知道如何获取标题 我想同时获得标题和正文
  • Android 自定义布局 - onDraw() 永远不会被调用

    public class MainActivity extends Activity Override public void onCreate Bundle savedInstanceState super onCreate savedI
  • 使用 gradlew assembleRelease 从 React Native 创建发布 apk 时出现错误

    我想发布 apk 但我收到错误 文件已存在 mkdir D mobile 它在 d 驱动器中生成名为 mobile 的文件 删除文件后 再次执行 gradlew assembleRelease 创建该文件并抛出错误 任务 app bundl
  • 使用 PhoneGap 使 Android 应用程序易于访问(对于残障人士)

    有人有过使用 PhoneGap 使 Android 应用程序可访问的经验吗 至少我们需要使我们的应用程序符合第 508 条规定 我尝试实现一些标准的辅助功能 文本框标签 向 div 添加标题属性等 但是 当在 Android 中使用 Tal
  • Android FragmentTransaction 自定义动画(未知动画师名称:Translate)

    我正在尝试让自定义动画与我的片段一起使用 我已按照在线教程进行操作 但出现以下错误 java lang RuntimeException 未知的动画师名称 翻译 动画的 XML 如下
  • 使用 SQLITE 按最近的纬度和经度坐标排序

    我必须获得一个 SQLite SQL 语句 以便在给定初始位置的情况下按最近的纬度和经度坐标进行排序 这是我在 sqlite 数据库中的表的例句 SELECT id name lat lng FROM items EXAMPLE RESUL
  • Android:滚动 Horizo​​ntalScrollView 时如何禁用 ScrollView 的垂直滚动?

    我正在开发一个带有带有 ScrollView 的 Activity 的 Android 应用程序 其中包含 Horizo ntalScrollView 等内容 当我触摸 Horizo ntalScrollView 时 我想禁用外部 Scro
  • 在 React Native 中调试应用程序崩溃

    我是 React Native 新手 我正在尝试安装 React Native Facebook SDK 以便我可以使用我的应用程序进行 Facebook 登录 我按照此处列出的步骤操作 https tylermcginnis com in
  • 如何在 Viewpager 中禁用预加载下一页? [复制]

    这个问题在这里已经有答案了 如何在 Viewpager 中禁用页面预加载 I tried viewPager setOffscreenPageLimit 0 但它不起作用 用这个viewPager setOffscreenPageLimit
  • 在Android Studio gradle项目中使用NDK和STL

    我在将 stlport 链接到 Android Studio 中的 gradle 项目时遇到问题 使用 NDK 的 Eclipse Android 项目迁移到 Android Studio 该项目使用 STL 我有包含内容的 android
  • Android 材料芯片组件崩溃应用程序。无法膨胀 xml

    Tried Chip来自两个支持库的组件 com google android support design 28 0 0 rc01和材料 com google android material material 1 0 0 rc01 堆栈

随机推荐