21

ライブ オーディオ ストリームを再生する必要があります。実際にはラジオです。問題は、ストリーミング用に 20 分のバッファーも管理する必要があることです。私が理解している限り、アンドロイドで実装するのは簡単ではありません。

最初に MediaPlayer を確認しましたが、バッファ管理のためのメソッドは提供されていません。実際、バッファサイズを直接設定することさえできません。

次に、ローカル ファイルを使用してバッファを管理しようとしました。ストリームを一時ファイルに徐々にダウンロードし、それらを切り替えました。ただし、次のファイルに切り替えたい (MediaPlayer でデータソースを変更する) と、オーディオが連続して再生されません。短い中断が聞こえます。

最後のアイデアは、ストリーム プロキシを使用することです。通常、Android バージョン 8 未満でストリームを再生するために使用されます。ストリーム プロキシでは、ServerSocket を作成し、オーディオ ストリームから読み取り、プレーヤーに書き込みます。したがって、実際にはそこでバッファリングを管理できます。ストリームをキャッシュして、必要に応じて MediaPlayer に書き込むことができます。しかし。Android 8 では動作しません。

例外が発生しました: Connection reset by peer java.net.SocketException: Connection reset by peer. MediaPlayer 8 は、ソケットからデータを読み取りたくありません。

したがって、2 つの質問があります。1) ストリーム バッファリングを実装する他の方法はありますか? 2) StreamProxy を Android 8 に適応させる方法は?

どんなアイデアでも大歓迎です。

ありがとう

4

5 に答える 5

8

みんなが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 のみを消費します。ファイルを使用したソリューションは機能しません。そのため、ストリーム送信にはソケットを使用する必要があります。

ありがとう

于 2010-12-30T15:23:51.877 に答える
5

次の修正をテストしたところ、動作します。この問題は、 のメソッドのヘッダーで使用される「\n」が原因で発生します。これを「\r\n」に変更すると、エラーが解消されます。processRequest()StreamProxy

カスタムストリーミングの実装に関しては、主にラジオを無限にストリーミングするためのようですが、これを使用したことがあります。 http://blog.pocketjourney.com/2009/12/27/android-streaming-mediaplayer-tutorial-updated-to-v1-5-cupcake/

于 2011-06-08T22:12:42.140 に答える
3

シークまたはスキップするか、接続が失われ、MediaPlayer がプロキシ サーバーに再接続し続ける場合、クライアントから要求と範囲 (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";

また、HTTP ヘッダーに Range が含まれていない MediaPlayer からの要求を受信すると、新しいストリーム ファイルが要求されます。この場合、応答ヘッダーは次のようになります。

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";

楽しみ!

于 2013-01-21T04:06:45.677 に答える
2

SDK 8 の Mediaplayer は、プロキシ URL を読み取ることができません。しかし、私の現在の仕事に基づいて、これはデバイスごとに異なります. 私の Samsung ACE (SDK 8) では、プロキシ接続は正常に機能しますが、私の HTC Incredible S では、プロキシ接続によって同じ問題が発生します。オーディオ ストリームへの直接接続は正常に機能しますが、EVO on Sprint などの一部のデバイスでブリップやブープが発生します。

この問題の解決策はありましたか? これをどのように処理しましたか?

-ハリ

于 2011-06-06T04:08:50.680 に答える
1

この質問は 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

于 2016-01-22T05:22:29.573 に答える