12

パブリック 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)。

私がやろうとしたことは、クライアント側で疑似ストリーミングを実装することです:

  1. MP3 への入力ストリームを開きます。
  2. JLayer を使用して着信バイトをデコードします。このリンクでデコードを見つけました。
  3. デコードされた配列バイトを、すでに再生可能な 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 に追加すると、再生がスムーズになりません)。

まとめると:

  1. この JellyBean の問題に遭遇した場合、どのように解決しましたか?
  2. 誰かが私のアプローチを試した場合、上記のコードで何が間違っていますか? これがあなたが使用した解決策である場合、残りのコードを公開できます。

ありがとう!

4

2 に答える 2

2

It looks like you are trying to develop your own streaming type. This can get blocky or interrupted playback because you have to attempt continuous information piping w/out running out of bytes to read from.

Basically, you will have to account for all the situations that a normal streaming client takes care of. For instance, sometimes some blocks may be dropped or lost in transmission; sometimes the audio playback may catch up to the download; the cpu starts lagging which affects playback; etc. etc.

Something to research if you want to continue down this path would be Sliding Window implementation, it is essentially an abstract technique to try to keep the network connectivity always active and fluid. You should be able to find several examples through google, here is a place to start: http://en.wikipedia.org/wiki/Sliding_window_protocol

Edit: One workaround that may help you until this is fixed would be to include the source code for MediaPlayer.java and AudioManager.java from SDK <16 into your project and see if that resolves the problem. If you do not have the source code you can download it with the SDK Manager.

于 2013-01-23T01:51:29.310 に答える
1

AudioTrack は本質的に docs( Will block until all data has been written to the audio mixer.) をブロックしています。ファイルから読み取り、同じスレッドで AudioTrack に書き込みを行っているかどうかはわかりません。もしそうなら、AudioTrack のスレッドを作成することをお勧めします。

于 2013-03-03T01:28:13.217 に答える