使用Android进行AAC音频流传输

23

据我所知,仅当AAC格式的音频编码为MPEG-4或3GPP时,Android 才能播放它。

当应用程序本地存在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类似,但可以包含其他类型的编码音频。请参见此处http://developer.android.com/guide/appendix/media-formats.html以及此处http://en.wikipedia.org/wiki/M4a。 - greg7gkb
是的,我应该将3GPP和M4A描述为AAC音频的“容器”。就我所知,M4A可能仅用于静态文件,而3GPP只能使用RTSP进行流式传输,而不能使用HTTP?这会对HTTP流式传输造成问题。如果我错了,请纠正我... - bdls
5个回答

7
这个解决方案可以让你播放来自网络上的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();
    }
}

6
好主意,但需要先完整下载文件才能播放。这在某些情况下可能很有用,但显然不适合作为流媒体解决方案。 - greg7gkb
没错,虽然这段代码可以构建一个流式解决方案,使用本地文件或文件作为临时循环缓冲区。不幸的是,MediaPlayer无法从InputStream接收数据,否则您将能够以标准方式执行循环缓冲。 - bdls

1

我也尝试了,但是找不到解决方案!

在最近的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 - 我可以播放任何流媒体(视频和音频)!

如果需要实现帮助,请告诉我!:)


0

这只是一个猜测,但我曾经在Flash播放器中看到过类似的行为,它实际上忽略文件名,并仅依赖于服务器发送的MIME类型。你知道从 example.com 发送了哪些头信息吗?你可以尝试将blah.m4a包装在一个页面中,该页面可以设置标头并流式传输二进制数据。尝试使用以下类型,社区会感谢您确认哪种有效:

audio/mpeg audio/mp4a audio/mp4a-latm audio/aac audio/x-aac


一个好的建议 - 谢谢。不幸的是,我尝试了各种可能的内容类型,但没有运气。为参考,以下是未能起作用的列表。仍然有可能与HTTP头有关 - 或者其他什么 - 可能它非常挑剔,只接受特定格式的输入。 audio/m4a audio/aac audio/aacp audio/x-aac audio/mpeg audio/mp4a audio/mp4a-latm audio/mp4 - bdls
1
我确实遇到了这个问题。对于我们的流媒体目的,MediaPlayer必须正确设置Http header content-type才能播放流文件。有趣的是,内容长度并不需要。 - greg7gkb

0

我发现如果你在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:

<audio id="audioMediaControl" controls src="yourfile.m4a"> 您的浏览器不支持音频元素。</audio>


-3

尝试--

1)MP.prepareAsync()

2)onPrepared() {mp.start()}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接