22

ローカル プロキシ サーバーを実行するオーディオ ストリーミング アプリがあります。ローカル プロキシ サーバーは、インターネット ストリーミング ソースへの http 接続を確立し、ストリーミング データをローカルで取得してバッファリングします。次に、アプリ内で MediaPlayer を使用してローカル プロキシ サーバーに接続し、次のメソッドを使用します。

mediaPlayer.setDataSource(...); // the url of the local proxy server

Nexus 7 がリリースされるまでは、すべて問題ありませんでした (多くの Android デバイスとさまざまな OS バージョン - 1.5...4.0)。

Nexus 7 では、メディア プレーヤーがローカル プロキシ サーバーからのソースの再生を拒否します。

ログを見てみると、MediaPlayer は範囲リクエストを内部的に使用しているようです。私のローカルプロキシサーバーはそれを処理しません。HTTP/1.0 200 OK とデータを返します。ただし、メディア プレーヤーはそれを好まず、例外をスローします。

Caused by: libcore.io.ErrnoException
?:??: W/?(?): [ 07-18 00:08:35.333  4962: 5149 E/radiobee ]
?:??: W/?(?): : sendto failed: ECONNRESET (Connection reset by peer)
?:??: W/?(?):   at libcore.io.Posix.sendtoBytes(Native Method)
?:??: W/?(?):   at libcore.io.Posix.sendto(Posix.java:146)
?:??: W/?(?):   at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:
?:??: W/?(?):   at libcore.io.IoBridge.sendto(IoBridge.java:473)
We requested a content range, but server didn't support that. (responded with 200)

http の仕様によると、サーバーが 1.1 ではなく HTTP/1.0 で応答する場合、クライアントは範囲要求を発行してはなりません (1.0 はとにかくそれをサポートしていません)。

また、サーバーが範囲要求をサポートしていない場合、200 OK で応答する場合は問題ありません (これが私が行っていることです) が、Nexus 7 の MediaPlayer 実装はそれを好みません。

このスレッドを見てみました: HTTP: Range is unsupported?

、彼らは 200 OK の応答で十分であると主張していますが、残念ながらそれは役に立ちません。

これが Jelly Bean の問題なのか、具体的には Nexus 7 の実装の問題なのかはわかりませんが、解決しなければならない問題です。

繰り返しますが、同じアプリを使用している他の多くの Android デバイスでは範囲要求はありません。何らかの理由で、これらの範囲リクエストは現在 Nexus 7 で発生しています (他の Android デバイスでも発生する可能性がありますが、これまでのところ発生したことはありません)。

MediaPlayer の範囲リクエストを無効にする方法はありますか?

何もない場合、可能であれば他のロジックを変更せずに、プロキシサーバーロジックの簡単な修正を提案できますか (この範囲リクエストを受信した場合、正確に何を返す必要がありますか?)

「HTTP/1.0 206 OK\r\nPartial Content\r\n\r\n」のようなものを返さなければならないようですが、おそらく部分コンテンツの最後に何らかの値があるはずです-何が必要なのかわかりませんこれであってください。

あなたの助けをいただければ幸いです。

ありがとう..

4

3 に答える 3

13

私たちはついにこれをきれいな方法で解決しました。私たちの場合、ストリーミングサーバーを完全に制御できますが、ローカルプロキシでもこれを行うことができると思います. オブジェクトBuild.VERSION_CODES.ICE_CREAM_SANDWICHのヘッダーを設定できるためです。MediaPlayerこのアプリでは、ユーザーがオーディオ ストリーム内をシークできるようにするため、このRangeヘッダーをクライアントに実装しました。サーバーが適切なヘッダーで応答する場合、MediaPlayerはストリームの要求を何度も試行しません。

Android クライアントのサーバー ヘッダーは次のようになります。

Content-Type: audio/mpeg
Accept-Ranges: bytes
Content-Length: XXXX
Content-Range: bytes XXX-XXX/XXX
Status: 206

重要な部分は206ステータス コード (部分的な内容) です。このヘッダーを送信しない場合、Android クライアントはソースを再要求しようとします。

プレイヤーがストリームでのシークを許可していない場合は、Rangeヘッダーを常に に設定できます0-some arbitrary large number

于 2012-11-27T08:18:21.103 に答える
8

私は大規模なオーディオ ストリーミング アプリ (ローカル ホスト HTTP プロキシを使用してオーディオを MediaPlayer にストリーミングする) に取り組んでおり、Google I/O 2012 で JellyBean デバイスを手にするとすぐにこの問題に遭遇しました。

さまざまなデバイスで (自動クラッシュ ログとユーザーが送信したログから受け取った情報を使用して) アプリケーションの品質テストを行ったところ、特定の MediaPlayer 実装が、私が言うところの異常な (そして時には完全に精神病的な) 動作をしていることに気付きました。

詳細は省きますが、これが私が見たものです。一部の実装では、同じ URL に対して複数のリクエスト (場合によっては 5 つ以上) が行われます。これらの要求はすべて、それぞれが異なるバイト範囲 (通常は最初または最後の 128 バイト) に対するものであるという点で、互いにわずかに異なっていました。結論として、MediaPlayer は埋め込まれたメタデータを見つけようとしていたが、ある時点であきらめて、通常の非範囲リクエストを行うだけだった。

これは、標準の JellyBean MediaPlayer 実装が行っていることではなく、Android のメディア フレームワークの奇抜さと一般的な断片化の例にすぎません。ただし、上記の状況の解決策は、JellyBean の問題の解決策でもありました。つまり、次のとおりです。

ローカル プロキシがチャンク エンコーディングで応答するようにする

これにより、Content-Length ヘッダーが Tranfer-Encoding: チャンク ヘッダーに置き換えられます。これは、要求しているクライアントがリソースの全長を知らないため、範囲要求を行うことができず、受信したチャンクを処理する必要があることを意味します。

私が言ったように、これはハックですが、うまくいきます。副作用がないわけではありません: メディア プレーヤーのバッファの進行状況は、オーディオの長さ (オーディオの長さの 1 つ) がわからないため、正しくありません。これを回避するには、独自のバッファリング計算を使用する必要があります (ストリーミングしている)。どこかからプロキシを介して MediaPlayer に送信されますよね? したがって、全長がわかります)。

于 2012-09-27T16:37:03.177 に答える
7

シークまたはスキップするか、接続が失われ、MediaPlayer がプロキシ サーバーに再接続し続ける場合、クライアントから要求と範囲 (int) を取得した後、ステータス 206でこの応答を送信する必要があります。

String headers += "HTTP/1.0 206 OK\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";
于 2013-01-21T02:28:27.320 に答える