パブリック URL で入手できる MP3 ファイルを再生するアプリケーションがあります。残念ながら、サーバーはストリーミングをサポートしていませんが、Android はユーザー エクスペリエンスを非常に受け入れやすいものにしています。
JellyBean を除くすべてのプラットフォームで問題なく動作します。MP3 を要求するとき、JB は Range-Header を 10 回要求します。10 回目の試行の後、古い動作に戻ったようです。これは既に報告された問題のようです。
推奨される解決策がTranfer-Encoding: chunked header を使用する別のSO スレッドを見つけました。しかし、そのすぐ下に、これは機能しないというコメントがあります。
現時点では、上記の応答ヘッダーを配信することはまったく制御できませんが、それができるようになるまで、クライアント側で代替手段を探すことを考えました. (それでも、0 から Content-Length - 1 までのインデックスを含む Content-Range のみを返すことができます。例: Content-Range: バイト 0-3123456/3123457)。
私がやろうとしたことは、クライアント側で疑似ストリーミングを実装することです:
- MP3 への入力ストリームを開きます。
- JLayer を使用して着信バイトをデコードします。このリンクでデコードを見つけました。
- デコードされた配列バイトを、すでに再生可能な stream_mode AudioTrack に送信します。
デコードを行うコードはそこにあります。InputStream を受け取るように変更しただけです。
public byte[] decode(InputStream inputStream, int startMs, int maxMs) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
float totalMs = 0;
boolean seeking = true;
try {
Bitstream bitstream = new Bitstream(inputStream);
Decoder decoder = new Decoder();
boolean done = false;
while (!done) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
done = true;
} else {
totalMs += frameHeader.ms_per_frame();
if (totalMs >= startMs) {
seeking = false;
}
if (!seeking) {
// logger.debug("Handling header: " + frameHeader.layer_string());
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
if (output.getSampleFrequency() != 44100 || output.getChannelCount() != 2) {
throw new IllegalArgumentException("mono or non-44100 MP3 not supported");
}
short[] pcm = output.getBuffer();
for (short s : pcm) {
outStream.write(s & 0xff);
outStream.write((s >> 8) & 0xff);
}
}
if (totalMs >= (startMs + maxMs)) {
done = true;
}
}
bitstream.closeFrame();
}
return outStream.toByteArray();
} catch (BitstreamException e) {
throw new IOException("Bitstream error: " + e);
} catch (DecoderException e) {
throw new IOException("Decoder error: " + e);
}
}
デコードされたバイトを時間チャンクでリクエストしています: (0, 5000) から始まるので、最初は再生する配列が大きくなり、次に 1 秒にまたがる次のバイト配列をリクエストしています: (5000, 1000), ( 6000、1000)、(7000、1000)など
デコードは十分に高速で、別のスレッドで行われます。デコードされたバイト配列が利用可能になったら、ブロッキング キューを使用して、別のスレッドで再生されている AudioTrack に書き込みます。
問題は、チャンクがトラック内で連続していないため、再生がスムーズではないことです (各チャンクは連続していますが、AudioTrack に追加すると、再生がスムーズになりません)。
まとめると:
- この JellyBean の問題に遭遇した場合、どのように解決しましたか?
- 誰かが私のアプローチを試した場合、上記のコードで何が間違っていますか? これがあなたが使用した解決策である場合、残りのコードを公開できます。
ありがとう!