【问题标题】:Audio stream buffering音频流缓冲
【发布时间】:2011-06-01 05:50:03
【问题描述】:

我需要播放实时音频流,实际上是广播。问题是我还需要管理 20 分钟的流媒体缓冲区。据我了解,用android实现并不容易。

首先我检查了 MediaPlayer,但它没有提供任何缓冲区管理方法。实际上你甚至不能直接设置缓冲区大小。

其次,我尝试使用本地文件管理缓冲区:逐步将流下载到临时文件并切换它们。但是当您想切换到以下文件时(在 MediaPlayer 中更改数据源),音频不会连续播放。您可以听到短暂的打扰。

最后一个想法是使用流代理。通常它用于在 8 以下的 Android 版本中播放流。在流代理中,您创建 ServerSocket,从音频流中读取并写入播放器。所以实际上我可以在那里管理缓冲。我可以随心所欲地缓存流并写入 MediaPlayer。但。它不适用于 Android 8。

我遇到异常:对等方重置连接 java.net.SocketException:对等方重置连接。 MediaPlayer 8 不想从套接字读取数据。

因此我有两个问题: 1)还有什么其他方式来实现流缓冲? 2) 如何为 android 8 适配 StreamProxy?

感谢任何想法。

谢谢

【问题讨论】:

  • "2) 如何为 Android 8 适配 StreamProxy?"...除非您显示您正在使用的代码,否则任何人都无法提供帮助。
  • 您是如何将流写入 MediaPlayer 的,您能再解释一下吗?

标签: android proxy stream media-player buffering


【解决方案1】:

我使用与 NPR 项目相同的 StreamProxy - https://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java

所以它得到原始音频流:

  String url = request.getRequestLine().getUri();
  HttpResponse realResponse = download(url);
  ...
  InputStream data = realResponse.getEntity().getContent();

并从此流写入客户端套接字:

  byte[] buff = new byte[1024 * 50];
  while (isRunning && (readBytes = data.read(buff, 0, buff.length)) != -1) {
    client.getOutputStream().write(buff, 0, readBytes);
  }

(您可以从上面的链接获得完整的代码。)

最后他们如何初始化播放器(PlaybackService):

  if (stream && sdkVersion < 8) {
    if (proxy == null) {
      proxy = new StreamProxy();
      proxy.init();
      proxy.start();
    }
    String proxyUrl = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), url);
    playUrl = proxyUrl;
   }
  ...
  mediaPlayer.setDataSource(playUrl);
  mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  mediaPlayer.prepareAsync();

所以他们检查 SDK 版本。但是,如果我省略此检查并使用 SDK 8 的代理,我也会遇到异常。 奇怪的是,MediaPlayer 甚至不尝试读取流:

12-30 15:09:41.576: DEBUG/StreamProxy(266): downloading...
12-30 15:09:41.597: DEBUG/StreamProxy(266): reading headers
12-30 15:09:41.597: DEBUG/StreamProxy(266): headers done
12-30 15:09:41.647: DEBUG/StreamProxy(266): writing to client
12-30 15:09:41.857: INFO/AwesomePlayer(34): mConnectingDataSource->connect() returned -    1007
12-30 15:09:41.857: ERROR/MediaPlayer(266): error (1, -1007)
12-30 15:09:41.867: ERROR/MediaPlayer(266): Error (1,-1007)
12-30 15:09:41.867: WARN/AudioService(266): onError(1, -1007)
12-30 15:09:41.867: WARN/AudioService(266): MediaPlayer refused to play current item.  Bailing on prepare.
12-30 15:09:41.867: WARN/AudioService(266): onComplete()
12-30 15:09:42.097: ERROR/StreamProxy(266): Connection reset by peer
        java.net.SocketException: Connection reset by peer
        at org.apache.harmony.luni.platform.OSNetworkSystem.writeSocketImpl(Native Method)
        at org.apache.harmony.luni.platform.OSNetworkSystem.write(OSNetworkSystem.java:723)
        at org.apache.harmony.luni.net.PlainSocketImpl.write(PlainSocketImpl.java:578)
        at org.apache.harmony.luni.net.SocketOutputStream.write(SocketOutputStream.java:59)
        at com.skyblue.service.media.StreamProxy.processRequest(StreamProxy.java:204)
        at com.skyblue.service.media.StreamProxy.run(StreamProxy.java:103)
        at java.lang.Thread.run(Thread.java:1096)

MediaPlayer 似乎变得更聪明了。如果我通过这样的 url "http://127.0.0.1:%d/%s" 它不仅要获得字节,还要获得“完整”的 http 响应。

另外我想知道是否还有其他方法可以实现缓冲?据我所知,MediaPlayer 只消耗文件和 url。 文件解决方案不起作用。所以我必须使用socket进行流传输。

谢谢

【讨论】:

  • 您好 Eugenious,您发布的 NPR 项目的链接不再存在,您可以发布一个有效的链接吗?
  • 您好 Eugenious,您找到原始问题的解决方案了吗? brack 发布的修复对您有用吗?
  • 感谢您发布此消息!谁能解释以下 Uri 如何/为什么起作用? String proxyUrl = String.format("127.0.0.1:%d/%s", proxy.getPort(), url);谢谢!
【解决方案2】:

SDK 8 中的 Mediaplayer 将无法读取代理 URL。但根据我目前的工作,这因设备而异。在我的三星 ACE (SDK 8) 中,代理连接工作正常,但在我的 HTC Incredible S 中,代理连接会出现与您相同的问题。直接连接到音频流可以正常工作,但这会导致某些设备(如 EVO 在 sprint 上)出现故障。

您对此问题有解决方案吗?你是怎么处理的?

-哈里

【讨论】:

  • 我也经历过同样的行为,我 99% 确信它与底层媒体播放器相关联。在大多数使用当前媒体播放器 (AwesomePlayer/StageFright) 的 SDK 8 设备上,会发生“连接重置”。但是,有些设备使用过时的媒体播放器 (PVPlayer),不会出现错误。
【解决方案3】:

我刚刚测试了以下修复程序,它可以工作。此问题是由 StreamProxyprocessRequest() 方法的标头中使用的 "\n" 引起的。将其更改为 "\r\n" 应该可以消除错误。

至于实现自定义流式传输,我过去曾使用过它,尽管它似乎主要用于无限流式广播。 http://blog.pocketjourney.com/2009/12/27/android-streaming-mediaplayer-tutorial-updated-to-v1-5-cupcake/

【讨论】:

  • 博文网址不存在。
【解决方案4】:

当您搜索或跳过或连接丢失并且 MediaPlayer 不断重新连接到代理服务器时,您必须在从客户端获取请求和 range(int) 后发送此响应,状态为 206。

String headers += "HTTP/1.1 206 Partial Content\r\n";
headers += "Content-Type: audio/mpeg\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Content-Length: " + (fileSize-range) + "\r\n";
headers += "Content-Range: bytes "+range + "-" + fileSize + "/*\r\n";
headers += "\r\n";

当您从 MediaPlayer 收到 HTTP 标头中不包含 Range 的请求时,它正在请求一个新的流文件,在这种情况下,您的响应标头应如下所示:

String headers = "HTTP/1.1 200 OK\r\n";
headers += "Content-Type: audio/mpeg\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Content-Length: " + fileSize + "\r\n";
headers += "\r\n";

享受吧!

【讨论】:

    【解决方案5】:

    我意识到这个问题已经有 5 年的历史了,但以防其他人在徘徊:ExoPlayer 是 Google 的库,可让您更好地控制缓冲区大小等选项。

        //DefaultUriDataSource – For playing media that can be either local or loaded over the network.
        DefaultUriDataSource dataSource = new DefaultUriDataSource(WgtechApplication.getAppContext(),
                Util.getUserAgent(WgtechApplication.getAppContext(), WgtechApplication.class.getSimpleName()));
        Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
    
        //ExtractorSampleSource – For formats such as MP3, M4A, MP4, WebM, MPEG-TS and AAC.
        ExtractorSampleSource sampleSource = new ExtractorSampleSource(Uri.parse(RADIO_STREAMING_URL),
                dataSource, allocator, BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE);
        player.prepare(new MediaCodecAudioTrackRenderer(sampleSource));
    

    这是我创建的一个小项目,用于学习如何使用它。 https://github.com/feresr/MyMediaPlayer

    http://developer.android.com/guide/topics/media/exoplayer.html https://github.com/google/ExoPlayer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-13
      • 1970-01-01
      • 1970-01-01
      • 2017-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多