使用 Android 流式传输 AAC 音频

Posted

技术标签:

【中文标题】使用 Android 流式传输 AAC 音频【英文标题】:Streaming AAC audio with Android 【发布时间】:2010-12-11 16:20:21 【问题描述】:

据我了解,android 只会播放编码为 MPEG-4 或 3GPP 的 AAC 格式音频。

当它在应用程序本地时,我能够播放编码为 M4A 的 AAC 音频,但从服务器获取它时失败。

以下工作,因为 m4a 文件保存在本地 res/raw 目录中。

MediaPlayer mp = MediaPlayer.create(this, R.raw.*file*);
mp.start();

以下不起作用。 (但对 MP3 有影响)。

Uri uri = Uri.parse("http://*example.com*/blah.m4a");
MediaPlayer mp = MediaPlayer.create(this, uri);
mp.start();

当 m4a 音频文件不是本地文件时,谁能解释为什么它会失败?

这是(部分)错误...

ERROR/PlayerDriver(542): Command PLAYER_INIT completed with an error or info UNKNOWN PVMFStatus
ERROR/MediaPlayer(769): error (200, -32)  
WARN/PlayerDriver(542): PVMFInfoErrorHandlingComplete  
DEBUG/MediaPlayer(769): create failed:  
DEBUG/MediaPlayer(769): java.io.IOException: Prepare failed.: status=0xC8  
DEBUG/MediaPlayer(769):     at android.media.MediaPlayer.prepare(Native Method)  
DEBUG/MediaPlayer(769):     at android.media.MediaPlayer.create(MediaPlayer.java:530)  
DEBUG/MediaPlayer(769):     at android.media.MediaPlayer.create(MediaPlayer.java:507)   
...

我的目标是 SDK 1.6。

【问题讨论】:

您将编码与文件格式混淆了。 AAC 是一种音频编码格式。 M4A 是一种文件类型或包,位于 AAC 数据周围。 3GPP 类似,但可以包含其他类型的编码音频。请参阅此处developer.android.com/guide/appendix/media-formats.html 和此处en.wikipedia.org/wiki/M4a。 没错,我应该将 3GPP 和 M4A 描述为 AAC 音频的“容器”。在我的脑海中,我认为 M4A 只能用于静态文件,而 3GPP 只能使用 RTSP 而不是 HTTP 进行流式传输?这会导致 HTTP 流式传输出现问题。如果我错了,请纠正我... 【参考方案1】:

解决方法允许您播放来自网络的 M4A 文件(以及其他容器中的 AAC 文件,例如 MP4 和 3GP)。它只是下载文件并从缓存中播放。

private File mediaFile;

private void playAudio(String mediaUrl) 
    try 
        URLConnection cn = new URL(mediaUrl).openConnection();
        InputStream is = cn.getInputStream();

        // create file to store audio
        mediaFile = new File(this.getCacheDir(),"mediafile");
        FileOutputStream fos = new FileOutputStream(mediaFile);   
        byte buf[] = new byte[16 * 1024];
        Log.i("FileOutputStream", "Download");

        // write to file until complete
        do 
            int numread = is.read(buf);   
            if (numread <= 0)  
                break;
            fos.write(buf, 0, numread);
         while (true);
        fos.flush();
        fos.close();
        Log.i("FileOutputStream", "Saved");
        MediaPlayer mp = new MediaPlayer();

        // create listener to tidy up after playback complete
        MediaPlayer.OnCompletionListener listener = new MediaPlayer.OnCompletionListener() 
            public void onCompletion(MediaPlayer mp) 
                // free up media player
                mp.release();
                Log.i("MediaPlayer.OnCompletionListener", "MediaPlayer Released");
            
        ;
        mp.setOnCompletionListener(listener);

        FileInputStream fis = new FileInputStream(mediaFile);
        // set mediaplayer data source to file descriptor of input stream
        mp.setDataSource(fis.getFD());
        mp.prepare();
        Log.i("MediaPlayer", "Start Player");
        mp.start();
     catch (Exception e) 
        e.printStackTrace();
    

【讨论】:

好主意,但这当然需要先下载整个文件才能播放。在某些情况下可能有用,但显然不适合作为流式解决方案。 确实如此,尽管可以构建此代码以使用本地文件或文件作为临时循环缓冲区来创建流解决方案。不幸的是,MediaPlayer 无法从 InputStream 接收,或者您可以以标准方式执行循环缓冲区。【参考方案2】:

我也试过了,但没找到解决办法!

在上一次 Google I/O 上,我看到了一些对我帮助很大的东西。它是从 MediaPlayer 扩展而来并改进了很多东西!看看吧。

EXOPLAYER 可以帮到你很多

检查示例的这一部分:

private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 256;
...

// String with the url of the radio you want to play
String url = getRadioUrl();
Uri radioUri = Uri.parse(url);
// Settings for exoPlayer
Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
String userAgent = Util.getUserAgent(context, "ExoPlayerDemo");
DataSource dataSource = new DefaultUriDataSource(context, null, userAgent);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
radioUri, dataSource, allocator, BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT);
audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
// Prepare ExoPlayer
exoPlayer.prepare(audioRenderer);

EXOPLAYER - 我可以播放流媒体(视频和音频)中的任何内容!

如果您需要帮助来实施它,请告诉我! :)

【讨论】:

【参考方案3】:

这是在黑暗中的狂野拍摄,但我在 Flash 播放器中看到了类似的行为,它实际上忽略了文件名,只依赖于服务器发送的 MIME 类型。知道从 example.com 发送了哪些标头吗?您可能想尝试将 blah.m4a 包装在可以设置标题然后流式传输二进制数据的页面中。试一试这些类型,社区会很高兴确认什么是有效的:

音频/mpeg 音频/mp4a 音频/mp4a-latm 音频/aac 音频/x-aac

【讨论】:

一个好建议 - 谢谢。不幸的是,我没有运气尝试一系列可能的内容类型。作为参考,我在下面列出了那些不起作用的。它仍然可能与 HTTP 标头有关 - 或其他东西 - 它可能对可以提供的内容非常挑剔。 audio/m4a audio/aac audio/aacp audio/x-aac audio/mpeg audio/mp4a audio/mp4a-latm audio/mp4 我确实遇到了这个问题。出于我们的流媒体目的,除非正确设置了 Http 标头内容类型,否则 MediaPlayer 不会播放流媒体文件。有趣的是,内容长度不是必需的。【参考方案4】:

我发现,如果您使用以下属性在 Android 上录制音频文件,您就可以在您的服务器上播放它。它在 HTML 音频元素中也能很好地播放,但目前仅在 Firefox 上。这在未来可能会改变。

Android (JAVA):

mediaRecorder = new MediaRecorder();
mediaRecorder.setAudiosource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
mediaRecorder.setAudioSamplingRate(44100);
mediaRecorder.setAudioChannels(1);
mediaRecorder.setOutputFile(filePath);

HTML:

&lt;audio id="audioMediaControl" controls src="yourfile.m4a"&gt; Your browser does not support the audio element. &lt;/audio&gt;

【讨论】:

【参考方案5】:

试试 --

1) MP.prepareAsync()

2) onPrepared() mp.start()

【讨论】:

以上是关于使用 Android 流式传输 AAC 音频的主要内容,如果未能解决你的问题,请参考以下文章

Android - 在我自己的项目中包含原生 StageFright 功能

如何使用 mediaRecorder 流式传输 android 的内部音频+屏幕?

如何使用 Android MediaPlayer 通过 http 流使 AAC 可搜索?

Android 流式音频/视频无法流式传输 rtsp 文件?

将作品音频 rtp 流式传输到 android 设备

Android:使用 AudioTrack 和 Socket 手动有效地流式传输音频