ライブオーディオストリームを再生する必要があります。実際はラジオです。問題は、ストリーミング用に20分のバッファーも管理する必要があることです。私の知る限り、Androidで実装するのは簡単ではありません。
最初にMediaPlayerをチェックしましたが、バッファ管理のメソッドが提供されていません。実際、バッファサイズを直接設定することさえできません。
次に、ローカルファイルを使用してバッファを管理しようとしました。ストリームを一時ファイルに段階的にダウンロードして切り替えます。ただし、次のファイルに切り替えたい場合(MediaPlayerでデータソースを変更した場合)、オーディオは継続的に再生されません。短い中断が聞こえます。
最後のアイデアは、ストリームプロキシを使用することです。通常、これはAndroidバージョン8未満のバージョンでストリームを再生するために使用されます。ストリームプロキシでは、ServerSocketを作成し、オーディオストリームから読み取り、プレーヤーに書き込みます。したがって、実際にはそこでバッファリングを管理できます。ストリームをキャッシュできます。そして、MediaPlayerに好きなように書き込みます。しかし、Android 8では機能しません。
例外が発生しました:ピアによって接続がリセットされましたJava.net.SocketException:ピアによって接続がリセットされました。 MediaPlayer 8は、ソケットからデータを読み取りたくありません。
したがって、2つの質問があります:1)ストリームバッファリングを実装する他の方法は何ですか? 2)StreamProxyをAndroid 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のみを消費します。ファイルを使用したソリューションは機能しません。そのため、ストリーム送信にはソケットを使用する必要があります。
ありがとう
次の修正をテストしたところ、機能します。この問題は、StreamProxy
のprocessRequest()
メソッドのヘッダーで使用されている "\ n"が原因で発生します。これを "\ r\n"に変更すると、エラーが解消されます。
カスタムストリーミングの実装に関しては、これは過去に使用したことがありますが、主に無限ストリーミングラジオ用のようです。 http://blog.pocketjourney.com/2009/12/27/Android-streaming-mediaplayer-tutorial-updated-to-v1-5-cupcake/
シークまたはスキップするか、接続が失われ、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";
また、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";
楽しい!
SDK 8のMediaplayerは、プロキシURLを読み取ることができません。しかし、私の現在の作業に基づくと、これはデバイスごとに異なります。私のSamsungACE(SDK 8)では、プロキシ接続は正常に機能しますが、私のHTC Incredible Sでは、プロキシ接続で同じ問題が発生します。オーディオストリームへの直接接続は正常に機能しますが、これにより、スプリントのEVOなどの一部のデバイスでブリップやブープが発生します。
この問題の解決策はありましたか?これをどのように処理しましたか?
-ハリ
この質問は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.htmlhttps://github.com/google/ExoPlayer