当前位置: 首页 > 知识库问答 >
问题:

Android MediaExtractor和mp3流

弓宏茂
2023-03-14

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

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

代码示例只是局部的(?)并使用html" target="_blank">文件而不是流。

我一直在尝试调整这个例子来播放音频流,但我无法理解这应该是如何工作的。Android留档一如既往没有帮助。

我知道,首先我们得到关于流的信息,大概是用这些信息设置音轨(代码示例是否包括音轨初始化?)然后打开输入缓冲区和输出缓冲区。

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

有人能给我指出正确的方向,让我明白这是怎么回事吗?

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

共有3个答案

包承望
2023-03-14

上述代码有两个问题。首先,正如接受的答案所述,从输入流只进行一次读取。然而,第二,调用。在音轨上需要播放()

此修改修复了OPs代码:

mAudioTrack.play();

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

暨鹭洋
2023-03-14

onCreate()中的代码表明您对MediaCodec的工作方式有误解。您的代码当前为:

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

MediaCodec在接入单元上运行。对于视频,每次调用输入/输出都会得到一帧视频。我没有使用过音频,但我的理解是,它的行为类似。您无法将整个文件加载到输入缓冲区,它也无法为您播放流;你拿一小块文件,把它交给解码器,它就会把解码数据(例如YUV视频缓冲区或PCM音频数据)交回给你。然后你做任何必要的事情来播放这些数据。

所以你的例子充其量只能解码一秒钟的音频。您需要在一个循环中完成提交输入获取输出,并正确处理流的结尾。你可以在各种bigflake示例中看到视频中的这一点。看起来你的代码有必要的部分。

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

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

巫健柏
2023-03-14

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

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

 类似资料:
  • MP3 Diags 帮你查找 MP3 文件中的问题并试图修复大部分的问题。

  • 问题内容: 如何在Java应用程序中播放和文件?我正在使用。我尝试在互联网上查找类似以下示例的内容: 但是,这只会播放文件。 问题答案: 如何在Java应用程序中播放和文件?我正在使用。我尝试在互联网上查找类似以下示例的内容: 但是,这只会播放文件。

  • 我的使用案例是:自动对移动/固定电话号码进行语音通话,并在有人接听电话时播放mp3文件。基本上是一种电话号码验证服务。在Twilio我该怎么做

  • 以下是从mp3文件中提取内容和元数据的程序 - import java.io.File; import java.io.FileInputStream; import java.io.IOException; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; impor

  • 这是一个Eclipse RCP示例应用程序,它以MP3管理为例演示了非常多的Eclipse RCP特性。包括:如何使用Presentation API创建新的应用程序外观。如何实现视图与编辑器的松耦合。 如何使用Tree views,table views和virtual tree view。如何使用多页面编辑器。如何使用本地帮助系统。如何实现可定制的升级功能。如何实现属于自己的扩展点(exten

  • 这是一个在 Windows 7 下用 C# 开发的 MP3 简易播放器,为想开发音乐播放器的人提供一个参考,该项目演示了如何编写一些 Windows 7 下应用程序的新特性。