【问题标题】:Android MediaCodec API - Music Plays on Emulator, Not on the DeviceAndroid MediaCodec API - 在模拟器上播放音乐,而不是在设备上
【发布时间】:2014-02-18 20:09:50
【问题描述】:

下面给出的代码在模拟器上运行良好,但在设备上运行良好。我发现以下几行对我来说很可疑:

V/MediaExtractor(5030):自动检测媒体内容为“音频/mpeg”,置信度为 0.20 V/ChromiumHTTPDataSource(5030): mContentSize 未定义或网络可能断开 V/ChromiumHTTPDataSource(5030): mContentSize 未定义或网络可能断开 D/com.example.mediacodectest(5030): MIME TYPE: audio/mpeg

我正在寻找提示/建议。提前谢谢...

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());
        }
    }
}

【问题讨论】:

  • 您的输出处理不太正确;我在 bigflake.com/mediacodec/#q11 添加了常见问题解答项目 #11。您显示的日志行似乎并不太可疑。您能否更清楚地定义代码在设备上无法运行的原因?
  • 感谢您的回复。该代码在 API 级别 17 模拟器上运行良好,但在 API 级别 18 的实际设备上运行良好。我可以调试到以下行: final int res = codec.dequeueOutputBuffer(info, -1);但没有进一步...

标签: android audio-streaming audiotrack android-mediacodec


【解决方案1】:

我没有处理过音频,但我想我可能会发现问题所在。你挂在dequeueOutputBuffer(),因为编解码器正在等待更多输入。

一些视频编解码器在完成初始化之前需要大约 4 个输入缓冲区 (for example)。我希望某些音频编解码器的行为方式相同。编解码器的实现因设备而异,因此在模拟器上运行的内容有很大不同也就不足为奇了。

将超时从 -1(永远等待)更改为适度的(例如,1000 微秒)。

【讨论】:

  • 谢谢你,成功了。那你什么时候设置超时-1呢?
  • 我不会将它用作常量值。如果您正在等待第一个输入缓冲区,或者您已经发送了输入 EOS 并且只是在等待输出耗尽,那么循环是没有意义的。因此,您可以将变量设置为 -1,首先等待输入,然后将变量设置为 1000 并使用它,直到输入用完,此时将其设置回 -1。 (自从关于启动延迟的问题出现以来,我一直想在 Grafika 中做这样的事情,但我还没有开始考虑。)
猜你喜欢
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 2023-03-22
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 2015-01-27
相关资源
最近更新 更多