音频流的最佳实践

2024-05-14

我正在编写一个应用程序来播放远程服务器的音频。我尝试了多种方法来实现流音频,但它们对我来说都不够好。 这就是我尝试过的:

幼稚地使用 MediaPlayer

就像是:

MediaPlayer player = new MediaPlayer(); 
player.setDataSource(context, Uri.parse("http://whatever.com/track.mp3"));
player.prepare();
player.start();

(or prepareAsync, 不管)

但标准MediaPlayer播放远程内容时相当不稳定。它经常掉落或停止播放,我无法处理这个问题。另一方面,我想实现媒体缓存。但我还没有找到任何方法从 MediaPlayer 获取缓冲内容并将其保存在设备上的某个位置。

实现自定义缓冲

然后就产生了一种想法,即按块下载媒体文件,将它们组合成一个本地文件并播放该文件。由于连接不良,下载整个文件可能会很慢,因此最好先下载足够的初始片段,然后开始播放并继续下载并附加本地文件。此外,我们还获得了缓存功能。

听起来像是一个计划,但它并不总是有效。它在 HTC Sensation XE 上完美运行,但在完成这首曲子后,在 4.1 平板电脑上播放并没有停止。不知道,为什么会这样。我问过question https://stackoverflow.com/questions/16273133/play-media-file-and-append-it-in-the-same-time对此,但没有得到答复。

使用两个媒体播放器

我创建了两个 MediaPlayer 实例并尝试使它们相互更改。逻辑如下:

  • 开始下载初始媒体片段
  • 下载后,通过 currentMediaPlayer 开始播放。其余媒体继续报道 下载
  • 当下载的片段即将播放时(完成前 1 秒),使用相同的源文件准备 secondaryMediaPlayer(因为它是在播放过程中附加的)
  • currentMediaPlayer 完成前 261 毫秒 – 暂停它,启动辅助播放器,将辅助播放器设置为当前播放器,安排下一个辅助播放器的准备。

来源:

private static final String FILE_NAME="local.mp3";
private static final String URL = ...;
private static final long FILE_SIZE = 7084032;

private static final long PREPARE_NEXT_PLAYER_OFFSET = 1000;
private static final int START_NEXT_OFFSET = 261;

private static final int INIT_PERCENTAGE = 3;

private MediaPlayer mPlayer;
private MediaPlayer mSecondaryPlayer;

private Handler mHandler = new Handler();

public void startDownload() {
    mDownloader = new Mp3Downloader(FILE_NAME, URL, getExternalCacheDir());
    mDownloader.setDownloadListener(mInitDownloadListener);
    mDownloader.startDownload();
}


private Mp3Downloader.DownloadListener mInitDownloadListener = new Mp3Downloader.DownloadListener() {
    public void onDownloaded(long bytes) {
        int percentage = Math.round(bytes * 100f / FILE_SIZE);

        // Start playback when appropriate piece of media downloaded
        if (percentage >= INIT_PERCENTAGE) {
            mPlayer = new MediaPlayer();
            try {
                mPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
                mPlayer.prepare();
                mPlayer.start();

                mHandler.postDelayed(prepareSecondaryPlayerRunnable, mPlayer.getDuration() - PREPARE_NEXT_PLAYER_OFFSET);
                mHandler.postDelayed(startNextPlayerRunnable, mPlayer.getDuration() - START_NEXT_OFFSET);

            } catch (IOException e) {
                Log.e(e);
            }

            mDownloader.setDownloadListener(null);
        }
    }
};

// Starting to prepare secondary MediaPlayer
private Runnable prepareSecondaryPlayerRunnable = new Runnable() {
    public void run() {
        mSecondaryPlayer = new MediaPlayer();
        try {
            mSecondaryPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
            mSecondaryPlayer.prepare();
            mSecondaryPlayer.seekTo(mPlayer.getDuration() - START_NEXT_OFFSET);

        } catch (IOException e) {
            Log.e(e);
        }
    }
};

// Starting secondary MediaPlayer playback, scheduling creating next MediaPlayer
private Runnable startNextPlayerRunnable = new Runnable() {
    public void run() {
        mSecondaryPlayer.start();

        mHandler.postDelayed(prepareSecondaryPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - PREPARE_NEXT_PLAYER_OFFSET);
        mHandler.postDelayed(startNextPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - START_NEXT_OFFSET);

        mPlayer.pause();
        mPlayer.release();
        
        mPlayer = mSecondaryPlayer;

    }
};

再说一遍——听起来像是一个计划,但效果并不完美。切换媒体播放器的时刻非常清晰。这里我遇到相反的情况:在 4.1 平板电脑上没问题,但在 HTC Sensation 上有明显的滞后。

我还尝试实现不同的下载技术。我已经实现了按 10Kb 块和 MP3 帧进行下载。我不太清楚,但似乎在 MP3 帧的情况下,seekTo 和 start 工作得更好。但这只是一种感觉,我不知道解释。

流媒体播放器

我在谷歌搜索时多次看到这个词,并找到了这个实现:https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r=18 https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r=18

这是每个人都使用的解决方案吗?

如果是的话,那就很难过,因为它对我来说也没有好处。我在实施中没有看到任何新的想法。

所以,问题

你们如何在应用程序中实现音频流?我不相信我是唯一遇到此类问题的人。应该有一些好的做法。


就我而言,我将 FFMPEG 与 OpenSL ES 结合使用。缺点是复杂。很多东西你一定很熟悉:JNI、OpenSL、FFMPEG。它也很难调试(与纯java android应用程序相比)。对于你的情况,我建议你尝试低级别媒体API http://www.youtube.com/watch?v=RQws6vsoav8。唯一的问题是缺乏例子。但有一个单元测试 https://code.google.com/p/android-source-browsing/source/browse/tests/tests/media/src/android/media/cts/DecoderTest.java?repo=platform--cts它显示了如何处理音频(您需要更改InputStream参考 - 第 82 行)。

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

音频流的最佳实践 的相关文章

随机推荐

  • PHP实现的机票预订系统

    如何防止预订系统中的座位被重复预订 我正在用 PHP 和 MYSQL 制作一个航空旅行预订系统模型作为一个项目 我有一个小问题 仅在付款后 门票和座位详细信息才会永久存储在此处 座位号在付款前分配 假设人 1 预订了飞机上的座位 x 并支付
  • 连接外部 Accumulo 实例和 java

    我正在尝试使用 Accumulo 连接到虚拟机 问题是 我无法将其连接到 Java 中 我可以看到 Apache 抛出的网页 但我无法让它与代码一起工作 我认为这是缺乏知识的问题而不是真正的问题 但我找不到这方面的文档 所有示例都使用 lo
  • Ioncube 编码的文件是否可以解码?

    我是一名 php 开发人员 我的客户计划分发一个使用 Php 开发的软件 计划使用 ioncube 或类似软件对文件进行编码 在谷歌搜索时 我发现很少有人解码这些文件 这些文件使用 ioncube 甚至其他软件进行编码 如果您询问是否可以破
  • getappdata 在 MATLAB 中返回空矩阵

    我有一段代码 我在其中使用setappdata然后我使用以下方式调用数据getappdata即使它不为空 它也会返回一个空矩阵 我的一段简化代码如下 function edit1 Callback hObject eventdata han
  • 在哪里可以获得几乎所有英语单词的列表? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我想生成一些随机文本 我尝试写一个基本的Java程序 int nowords r nextInt 2000 int i j for i 0
  • Windows 上的 Node.js 和 Express

    今晚 我决定尝试在我的 Windows 7 计算机上使用 Express 构建一个简单的 Node js 应用程序 安装过程还算顺利 但 Express 拒绝配合 以下是我已采取的步骤 使用以下位置提供的 MSI 安装 Node jshtt
  • 处理聚合的所有事件

    请参阅下面我的第一个持久订阅 namespace PersistentSubscription internal class Program private static void Main var subscription new Per
  • 使用 InlineKeyboardButton python telegram bot 发送命令

    在Python电报机器人中 是否有可能InlineKeyboardButton发送类似命令 cancel当它被按下时 例如 当用户按下取消按钮时 他们将自动发送 cancel 命令 然后由机器人处理 从这里的例子来看 https githu
  • 作为对象访问 vue-i18n 消息

    我想创建一个取决于页面的动态滑块 security signin slide1 Kitten1 slide2 Kitten2 signup slide1 Kitten1 slide2 Kitten2 slide3 Kitten3 问题是我想
  • flutter web http.get 返回“不支持的操作:Platform._version”

    我正在开发 flutter web 应用程序并尝试从互联网获取一些数据 我正在使用包 http http dart 并尝试这样做 await http get url 但我得到了这个错误 不支持的操作 Platform version da
  • Mapkit 在 IOS 13 中使用过多的 CPU

    最近 在一些用户更新到 iOS 13 x 后 我的 iOS 应用程序开始频繁崩溃 在 iOS 12 x 中没有出现该问题 我正在使用 Mapkit 渲染一些 MKPolygons 和 MKPolylines MKPolylines 被删除并
  • 使用 JPA 标准 API 进行日期比较

    我有一个包含两个日期的范围日期选择器 start and end 其中两者都可以为空 我想过滤一张表 其中实体具有确切的一个日期 date 所以 这里有一些例子 我愿意匹配 想象要匹配的日期是当前日期 17 07 2016 空 17 07
  • Angular *ngFor 循环遍历数组的数组

    我有一个数组 其中包含其他数组 如下所示 array element A element B YES NO 我想使用 ngFor 循环遍历 HTML 表中的这个对象数组 table thead tr th th th COLUMN 1 th
  • 如果条件长度 > 1 并且仅使用第一个元素,为什么我会在 R 中收到此警告

    我有下面的源代码 这if is na monthData 用于检查是否monthData is NA 如果是 则为其分配一个初始值 monthData lt NA if category QUARTER for m in c rep 1 4
  • 如果tinyMCE编辑器为空,如何通过在其旁边附加字符串来验证它?

    我需要验证一个表格 此表单有一些下拉菜单和tinyMCE编辑器 我通过在每个字段后附加字符串 Required 如果为空 来验证此表单 但是我无法验证tinyMCE编辑器 如果编辑器为空 我尝试了类似的操作 tinyMCE get tiny
  • 为什么不能在 Fragment 中使用 ViewPager?它实际上是

    有信息无法使用ViewPager在一个Fragment在许多来源中 例如 Android 开发者繁忙编码指南 http commonsware com 作者 Mark Murphy 或者类似的帖子this https stackoverfl
  • PHP MySQL 查询带有 %s 和 %d

    SELECT COUNT AS test FROM s WHERE id d AND tmp mail lt gt 什么是 s and d for 这些是使用的格式符号 例如经过sprintf 例子 Output SELECT COUNT
  • 无法在我的 Django 项目中使用 Sphinx 生成自动文档

    我正在向我的 Django 项目添加文档 github链接 https github com augustakingfoundation queryjane app 该项目是开源的 使用sphinx 但是当尝试生成python文件的auto
  • OAuth2 隐式流程 - IFrame 刷新身份

    我正在为隐式流开发 OAuth2 客户端 并正在实现基于 IFrame 的刷新 因为隐式流中没有刷新令牌 我所坚持的是试图找出将访问令牌传递回服务器的 标准 我是否通过 access token 查询字符串参数传回 或者在设置 IFrame
  • 音频流的最佳实践

    我正在编写一个应用程序来播放远程服务器的音频 我尝试了多种方法来实现流音频 但它们对我来说都不够好 这就是我尝试过的 幼稚地使用 MediaPlayer 就像是 MediaPlayer player new MediaPlayer play