0

The code given below works fine on the emulator, but not the device. I found the following lines that looked suspicious to me:

V/MediaExtractor(5030): Autodetected media content as 'audio/mpeg' with confidence 0.20 V/ChromiumHTTPDataSource(5030): mContentSize is undefined or network might be disconnected V/ChromiumHTTPDataSource(5030): mContentSize is undefined or network might be disconnected D/com.example.mediacodectest(5030): MIME TYPE: audio/mpeg

I am looking for hints/suggestions. Thanks in advance...

private class PlayerThread extends Thread {

    @Override
    public void run() {
        MediaExtractor extractor;
        MediaCodec codec;
        ByteBuffer[] codecInputBuffers;
        ByteBuffer[] codecOutputBuffers;

        AudioTrack mAudioTrack;

        mAudioTrack = new AudioTrack(
                AudioManager.STREAM_MUSIC, 
                44100, 
                AudioFormat.CHANNEL_OUT_STEREO, 
                AudioFormat.ENCODING_PCM_16BIT,
                8192 * 2, 
                AudioTrack.MODE_STREAM);

        extractor = new MediaExtractor();
        try 
        {
            extractor.setDataSource("http://anmp3streamingsource.com/stream");
            MediaFormat format = extractor.getTrackFormat(0);
            String mime = format.getString(MediaFormat.KEY_MIME);
            Log.d(TAG, String.format("MIME TYPE: %s", mime));

            codec = MediaCodec.createDecoderByType(mime);
            codec.configure(
                    format, 
                    null /* surface */, 
                    null /* crypto */, 
                    0 /* flags */ );
            codec.start();
            codecInputBuffers = codec.getInputBuffers();
            codecOutputBuffers = codec.getOutputBuffers();

            extractor.selectTrack(0); // <= You must select a track. You will read samples from the media from this track!

            boolean sawInputEOS = false;
            boolean sawOutputEOS = false;               

            for (;;) {
                int inputBufIndex = codec.dequeueInputBuffer(-1);
                if (inputBufIndex >= 0) {
                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];

                    int sampleSize = extractor.readSampleData(dstBuf, 0);
                    long presentationTimeUs = 0;
                    if (sampleSize < 0) {
                        sawInputEOS = true;
                        sampleSize = 0;
                    } else {
                        presentationTimeUs = extractor.getSampleTime();
                    }

                    codec.queueInputBuffer(inputBufIndex,
                                           0, //offset
                                           sampleSize,
                                           presentationTimeUs,
                                           sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
                    if (!sawInputEOS) {
                        extractor.advance();
                    }

                    MediaCodec.BufferInfo info = new BufferInfo();
                    final int res = codec.dequeueOutputBuffer(info, -1);
                    if (res >= 0) {
                        int outputBufIndex = res;
                        ByteBuffer buf = codecOutputBuffers[outputBufIndex];

                        final byte[] chunk = new byte[info.size];
                        buf.get(chunk); // Read the buffer all at once
                        buf.clear(); // ** MUST DO!!! OTHERWISE THE NEXT TIME YOU GET THIS SAME BUFFER BAD THINGS WILL HAPPEN

                        mAudioTrack.play();

                        if (chunk.length > 0) {
                            mAudioTrack.write(chunk, 0, chunk.length);
                        }
                        codec.releaseOutputBuffer(outputBufIndex, false /* render */);

                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                            sawOutputEOS = true;
                        }
                    } 
                    else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) 
                    {
                        codecOutputBuffers = codec.getOutputBuffers();
                    } 
                    else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) 
                    {
                        final MediaFormat oformat = codec.getOutputFormat();
                        Log.d(TAG, "Output format has changed to " + oformat);
                        mAudioTrack.setPlaybackRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
                    }
                 }
            }

        }                
        catch (IOException e) 
        {
            Log.e(TAG, e.getMessage());
        }
    }
}
burakk
  • 1,231
  • 2
  • 22
  • 45
  • Your output handling isn't quite right; I've added FAQ item #11 at http://bigflake.com/mediacodec/#q11 . The log lines you show don't seem too suspicious. Can you more clearly define how the code is failing to work on the device? – fadden Feb 18 '14 at 20:52
  • Thanks for the reply. The code works fine on an API level 17 emulator, but not on the actual device with the API level 18. I could debug until the line: final int res = codec.dequeueOutputBuffer(info, -1); but no further... – burakk Feb 18 '14 at 22:34

1 Answers1

1

I haven't worked with audio, but I think I may see the problem. You're hanging in dequeueOutputBuffer() because the codec is waiting for more input.

Some of the video codecs want ~4 buffers of input before they'll even finish initialization (for example). I expect some audio codecs may behave the same way. Codec implementations vary from device to device, so it's not surprising that what runs on the emulator behaves much differently.

Change the timeouts from -1 (wait forever) to something modest (say, 1000 microseconds).

Community
  • 1
  • 1
fadden
  • 51,356
  • 5
  • 116
  • 166
  • Thank you, that worked. When would you set the timeout -1 then? – burakk Feb 19 '14 at 10:49
  • I wouldn't use it as a constant value. If you are waiting for the very first input buffer, or you've sent input EOS and are just waiting for the output to drain, there's no point looping around. So you could set a variable to -1, do the first wait for input, then set the variable to 1000 and use that until you run out of input, at which point you set it back to -1. (I've been meaning to do something like this in Grafika ever since the question about startup latency came up, but I haven't got around to it yet.) – fadden Feb 19 '14 at 15:39