【问题标题】:Android MediaExtractor and mp3 streamAndroid MediaExtractor 和 mp3 流
【发布时间】:2014-03-10 15:05:49
【问题描述】:

我正在尝试使用 MediaExtractor/MediaCodec 播放 mp3 流。由于延迟和长缓冲区大小,MediaPlayer 是不可能的。

我找到的唯一示例代码是:http://dpsm.wordpress.com/category/android/

代码示例只是部分 (?) 并使用文件而不是流。

我一直在尝试调整此示例以播放音频流,但我无法理解它应该如何工作。像往常一样的 Android 文档没有帮助。

我知道首先我们获取有关流的信息,大概使用此信息设置 AudioTrack(代码示例确实包括 AudioTrack 初始化?),然后打开输入缓冲区和输出缓冲区。

我已经为此重新创建了代码,我猜可能是缺少的部分,但没有音频输出。

有人能指出正确的方向来理解这应该如何工作吗?

public final String LOG_TAG = "mediadecoderexample";
private static int TIMEOUT_US = -1;
MediaCodec codec;
MediaExtractor extractor;

MediaFormat format;
ByteBuffer[] codecInputBuffers;
ByteBuffer[] codecOutputBuffers;
Boolean sawInputEOS = false;
Boolean sawOutputEOS = false;
AudioTrack mAudioTrack;
BufferInfo info;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    String url = "http://82.201.100.9:8000/RADIO538_WEB_MP3";
    extractor = new MediaExtractor();

    try {
        extractor.setDataSource(url);
    } catch (IOException e) {
    }

    format = extractor.getTrackFormat(0);
    String mime = format.getString(MediaFormat.KEY_MIME);
    int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);

    Log.i(LOG_TAG, "===========================");
    Log.i(LOG_TAG, "url "+url);
    Log.i(LOG_TAG, "mime type : "+mime);
    Log.i(LOG_TAG, "sample rate : "+sampleRate);
    Log.i(LOG_TAG, "===========================");

    codec = MediaCodec.createDecoderByType(mime);
    codec.configure(format, null , null , 0);
    codec.start();

    codecInputBuffers = codec.getInputBuffers();
    codecOutputBuffers = codec.getOutputBuffers();

    extractor.selectTrack(0); 

    mAudioTrack = new AudioTrack(
            AudioManager.STREAM_MUSIC, 
            sampleRate, 
            AudioFormat.CHANNEL_OUT_STEREO, 
            AudioFormat.ENCODING_PCM_16BIT, 
            AudioTrack.getMinBufferSize (
                    sampleRate, 
                    AudioFormat.CHANNEL_OUT_STEREO, 
                    AudioFormat.ENCODING_PCM_16BIT
                    ), 
            AudioTrack.MODE_STREAM
            );

    info = new BufferInfo();


    input();
    output();


}

private void output()
{
    final int res = codec.dequeueOutputBuffer(info, TIMEOUT_US);
    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

        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(LOG_TAG, "Output format has changed to " + oformat);
        mAudioTrack.setPlaybackRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
    }

}

private void input()
{
    Log.i(LOG_TAG, "inputLoop()");
    int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_US);
    Log.i(LOG_TAG, "inputBufIndex : "+inputBufIndex);

    if (inputBufIndex >= 0) {   
        ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];

        int sampleSize = extractor.readSampleData(dstBuf, 0);
        Log.i(LOG_TAG, "sampleSize : "+sampleSize);
        long presentationTimeUs = 0;
        if (sampleSize < 0) {
            Log.i(LOG_TAG, "Saw input end of stream!");
            sawInputEOS = true;
            sampleSize = 0;
        } else {
            presentationTimeUs = extractor.getSampleTime();
            Log.i(LOG_TAG, "presentationTimeUs "+presentationTimeUs);
        }

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

        }
     }

}
}

编辑:添加 logcat 输出以获得额外的想法。

03-10 16:47:54.115: I/mediadecoderexample(24643): ===========================
03-10 16:47:54.115: I/mediadecoderexample(24643): url ....
03-10 16:47:54.115: I/mediadecoderexample(24643): mime type : audio/mpeg
03-10 16:47:54.115: I/mediadecoderexample(24643): sample rate : 32000
03-10 16:47:54.115: I/mediadecoderexample(24643): ===========================
03-10 16:47:54.120: I/OMXClient(24643): Using client-side OMX mux.
03-10 16:47:54.150: I/Reverb(24643):  getpid() 24643, IPCThreadState::self()->getCallingPid() 24643
03-10 16:47:54.150: I/mediadecoderexample(24643): inputLoop()
03-10 16:47:54.155: I/mediadecoderexample(24643): inputBufIndex : 0
03-10 16:47:54.155: I/mediadecoderexample(24643): sampleSize : 432
03-10 16:47:54.155: I/mediadecoderexample(24643): presentationTimeUs 0
03-10 16:47:54.155: I/mediadecoderexample(24643): extractor.advance()
03-10 16:47:59.085: D/HTTPBase(24643): [2] Network BandWidth = 187 Kbps
03-10 16:47:59.085: D/NuCachedSource2(24643): Remaining (64K), HighWaterThreshold (20480K)
03-10 16:48:04.635: D/HTTPBase(24643): [3] Network BandWidth = 141 Kbps
03-10 16:48:04.635: D/NuCachedSource2(24643): Remaining (128K), HighWaterThreshold (20480K)
03-10 16:48:09.930: D/HTTPBase(24643): [4] Network BandWidth = 127 Kbps
03-10 16:48:09.930: D/NuCachedSource2(24643): Remaining (192K), HighWaterThreshold (20480K)
03-10 16:48:15.255: D/HTTPBase(24643): [5] Network BandWidth = 120 Kbps
03-10 16:48:15.255: D/NuCachedSource2(24643): Remaining (256K), HighWaterThreshold (20480K)
03-10 16:48:20.775: D/HTTPBase(24643): [6] Network BandWidth = 115 Kbps
03-10 16:48:20.775: D/NuCachedSource2(24643): Remaining (320K), HighWaterThreshold (20480K)
03-10 16:48:26.510: D/HTTPBase(24643): [7] Network BandWidth = 111 Kbps
03-10 16:48:26.510: D/NuCachedSource2(24643): Remaining (384K), HighWaterThreshold (20480K)
03-10 16:48:31.740: D/HTTPBase(24643): [8] Network BandWidth = 109 Kbps
03-10 16:48:31.740: D/NuCachedSource2(24643): Remaining (448K), HighWaterThreshold (20480K)
03-10 16:48:37.260: D/HTTPBase(24643): [9] Network BandWidth = 107 Kbps
03-10 16:48:37.260: D/NuCachedSource2(24643): Remaining (512K), HighWaterThreshold (20480K)
03-10 16:48:42.620: D/HTTPBase(24643): [10] Network BandWidth = 106 Kbps
03-10 16:48:42.620: D/NuCachedSource2(24643): Remaining (576K), HighWaterThreshold (20480K)
03-10 16:48:48.295: D/HTTPBase(24643): [11] Network BandWidth = 105 Kbps
03-10 16:48:48.295: D/NuCachedSource2(24643): Remaining (640K), HighWaterThreshold (20480K)
03-10 16:48:53.735: D/HTTPBase(24643): [12] Network BandWidth = 104 Kbps
03-10 16:48:53.735: D/NuCachedSource2(24643): Remaining (704K), HighWaterThreshold (20480K)
03-10 16:48:59.115: D/HTTPBase(24643): [13] Network BandWidth = 103 Kbps
03-10 16:48:59.115: D/NuCachedSource2(24643): Remaining (768K), HighWaterThreshold (20480K)
03-10 16:49:04.480: D/HTTPBase(24643): [14] Network BandWidth = 103 Kbps
03-10 16:49:04.480: D/NuCachedSource2(24643): Remaining (832K), HighWaterThreshold (20480K)
03-10 16:49:09.955: D/HTTPBase(24643): [15] Network BandWidth = 102 Kbps

【问题讨论】:

  • FWIW,其他示例可以在 bigflake.com/mediacodec 找到,但它们主要用于视频,并且仅在 MediaCodec API 相同的情况下才真正有用。 DecoderTest 用于音频。你在 logcat 中看到了什么“有趣”的东西吗?
  • 我也看过这些例子,但我认为这个主题太复杂了,我无法弄清楚如何将它们“移植”到我的特定场景中。 logcat(将其添加到问题中)并没有什么太有趣的东西,而且手机似乎“挂起”,我无法使用侧面按钮调高或调低音量。该应用程序不会崩溃
  • 有用于音频的 openmxplayer,作为开源发布。
  • 我也有兴趣,你找到解决办法了吗?
  • @rraallvv 是的,改用 exoplayer

标签: android audio mp3 android-mediacodec mediaextractor


【解决方案1】:

对于仍在寻找可靠播放流音频问题的答案的任何人,您可能想看看这个项目(基于 MediaCodec API)

https://code.google.com/p/android-openmxplayer/

【讨论】:

  • 由于code.google.com/p/android/issues/detail?id=73715>,此代码也不能处理许多设备上的网络流。
【解决方案2】:

onCreate() 中的代码表明您对MediaCodec 的工作方式存在误解。您的代码目前是:

onCreate() {
    ...setup...
    input();
    output();
}

MediaCodec 在访问单元上运行。对于视频,每次调用输入/输出都会为您提供一帧视频。我没有使用过音频,但我的理解是它的行为类似。您不会将整个文件加载到输入缓冲区中,并且它不会为您播放流;你拿一小块文件,把它交给解码器,它把解码后的数据(例如 YUV 视频缓冲区或 PCM 音频数据)交还给解码器。然后,您可以执行任何必要的操作来播放该数据。

因此,您的示例最多只能解码几分之一秒的音频。您需要在循环中执行 submit-input-get-output 并正确处理流结束。您可以在各种 bigflake 示例中看到为视频完成的操作。看起来你的代码有必要的部分。

您正在使用 -1(无限)的超时,因此您将提供一个输入缓冲区并永远等待一个输出缓冲区。在视频中这是行不通的——我测试过的解码器在产生任何输出之前似乎需要大约四个输入缓冲区——但我又没有处理过音频,所以我不确定这是否预计工作。由于您的代码挂起,我猜它不是。将超时更改为(例如)10000 并查看挂起是否消失可能很有用。

我假设这是一个实验,你不会真的在onCreate() 中做这一切。 :-)

【讨论】:

  • 谢谢,是的,我很困惑。文档很少,示例通常适用于视频(不是音频)。这很有帮助。
  • 我尝试将 OP 代码修改为使用 do/while:do { input(); output(); } while (!sawInputEOS); 但我仍然没有声音...
  • 您好!我有类似的问题,但还是有区别stackoverflow.com/q/57956915/5709159,你能告诉我你的想法吗?
【解决方案3】:

上面的代码有两个问题。首先,正如接受的答案所述,只从输入流中读取一次。但是,其次,需要在AudioTrack 上调用.play()

此修改修复了 OP 代码:

mAudioTrack.play();

do {
    input();
    output();
} while (!sawInputEOS);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-02
    • 1970-01-01
    • 2019-10-19
    相关资源
    最近更新 更多