MediaExtractor 不理解音频/AACP 流 [英] MediaExtractor does not understand audio/aacp streams

查看:43
本文介绍了MediaExtractor 不理解音频/AACP 流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有自己的MediaDataSource:

<代码>类 MyDataSource 扩展 MediaDataSource {private static final String TAG = "MyDataSource";私有 HttpURLConnection 连接;私有 BufferedInputStream 输入流;MyDataSource(@NonNull URL streamURL) 抛出 Throwable {this.connection = (HttpURLConnection) streamURL.openConnection();this.connection.setRequestMethod("GET");this.connection.addRequestProperty("Icy-Metadata", "0");this.connection.connect();int responseCode = this.connection.getResponseCode();如果(响应代码!= 200)throw new IOException("http 响应代码" + responseCode);for (Map.Entry> header: this.connection.getHeaderFields().entrySet()) {for (String headerValue : header.getValue())Log.v(TAG, "responseHeader(" + header.getKey() + ") = "" + headerValue + """);}this.inputStream = new BufferedInputStream(connection.getInputStream());}@覆盖公共长 getSize() {返回-1;}@覆盖public int readAt(long position, @NonNull byte[] buffer, int offset, int size) 抛出 IOException {int 字节读取;int bytesReadTotal = 0;做 {bytesRead = this.inputStream.read(buffer, offset + bytesReadTotal, size - bytesReadTotal);bytesReadTotal += bytesRead;} while(bytesRead != 0 && bytesReadTotal < size);返回 bytesReadTotal;}@覆盖公共无效关闭(){尝试 {如果(输入流!= null){inputStream.close();输入流 = 空;}如果(连接!= null){connection.disconnect();连接 = 空;}} catch(IOException e) {Log.e(TAG, "close", e);}}}

当我尝试播放 MP3 流时(例如 A.0.0.00无线电):

<代码>MyDataSource dataSource = new MyDataSource(new URL("http://streaming.shoutcast.com/80sPlanet"));MediaExtractor mediaExtractor = new MediaExtractor();mediaExtractor.setDataSource();MediaFormat mediaFormat = mediaExtractor.getTrackFormat(0);String mime = mediaFormat.getString(MediaFormat.KEY_MIME);Log.v("播放器", "mime:" + mime);mediaExtractor.selectTrack(0);MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);mediaCodec.configure(this.mediaFormat, null, null, 0);int sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);音轨音轨 = 新音轨(AudioManager.STREAM_MUSIC,采样率,AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT,AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT),AudioTrack.MODE_STREAM);mediaCodec.setCallback(new MyCodecCallback());mediaCodec.start();audioTrack.play();

我看到以下 Logcat trace:

<前>07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(null) = "HTTP/1.1 200 OK"07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Access-Control-Allow-Headers) = "Origin, Accept, X-Requested-With, Content-Type"07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Access-Control-Allow-Methods) = "GET, OPTIONS, HEAD"07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Access-Control-Allow-Origin) = "*"07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Cache-Control) =无缓存,无存储"07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Connection) = "close"07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Content-Type) = "audio/mpeg"07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Date) = "Mon, 24 Jul 2017 18:11:59 GMT"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Expires) = "Mon, 26 Jul 1997 05:00:00 GMT"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-br) = "128"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-genre) = "Decades,80s"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-name) = "A.0.0.00Radio:All 80s All Time"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice1) = "<BR>此流需要 <a href="http://www.winamp.com">Winamp</a><BR>"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice2) = "SHOUTcast DNAS/posix(linux x64) v2.5.1.725
"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-pub) = "1"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-sr) = "44100"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-url) = "http://a.0.00radio.com/80s/"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Pragma) =无缓存"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Server) = "Icecast 2.3.3-kh8"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Received-Millis) = "1500919909958"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Response-Source) = "NETWORK 200"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Selected-Protocol) = "http/1.1"07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Sent-Millis) = "1500919909816"07-24 18:11:49.961 7408-7671/com.sample.sandbox E/WVMExtractor:无法打开 libwvm.so:dlopen 失败:未找到库libwvm.so"07-24 18:11:49.962 7408-7671/com.sample.sandbox V/Player: mime: 音频/mpeg07-24 18:11:49.964 7408-7681/com.sample.sandbox I/OMXClient:使用客户端 OMX 多路复用器.07-24 18:11:50.170 7408-7681/com.sample.sandbox I/MediaCodec:MediaCodec 将以异步模式运行[...]

似乎一切正常(流实际上是通过设备播放的).
但是,如果我尝试打开 AAC 流(例如 COOLfahrenheit 93):

<代码>MyDataSource dataSource = new MyDataSource(new URL("http://111.223.51.8:8005"));[...]

MediaExtractor 发疯了:
<前>07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(null) = "HTTP/1.0 200 OK"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(content-type) = "audio/aacp"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-br) = "128"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-genre) = "Easy Listening, Pop"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-name) = "COOLfahrenheit 93 - (4)"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice1) = "<BR>此流需要 <a href="http://www.winamp.com">Winamp</a><BR>"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice2) = "SHOUTcast DNAS/posix(linux x64) v2.4.7.256
"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-pub) = "1"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-url) = "http://www.coolism.net"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Received-Millis) = "1500921523862"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Response-Source) = "NETWORK 200"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Selected-Protocol) = "http/1.0"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Sent-Millis) = "1500921523387"07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Clacks-Overhead) = "GNU Terry Pratchett"07-24 18:38:45.424 32284-32690/com.sample.sandbox E/WVMExtractor:无法打开 libwvm.so:dlopen 失败:未找到库libwvm.so"07-24 18:38:45.425 32284-32690/com.sample.sandbox E/PlayerThread:错误java.io.IOException:无法实例化提取器.在 android.media.MediaExtractor.setDataSource(本机方法)在 com.sample.sandbox.Player.open(Player.java:204)在 com.sample.sandbox.Player.(Player.java:231)在 com.sample.sandbox.PlayerThread.run(PlayerThread.java:28)


有人知道是什么问题吗?问题绝对不在于流本身 - 它是完全有效的.

解决方案

我的主要错误是忽略了 MediaDataSource::readAt() 方法的 position 参数.
结果证明 MediaExtractor 在文件中执行了很多跳过(向前和向后! - 这在您流式传输 HLS 时非常烦人).
我已经注意到这些跳过的数量(及其范围)取决于内容类型以及特定的编解码器.我在这个问题中的主要观点是MediaExtractor 不理解 aacp",因为 AAC 编解码器强烈要求跳过,但 MP3 编解码器不需要.

I have my own MediaDataSource:



    class MyDataSource extends MediaDataSource {
        private static final String TAG = "MyDataSource";
        private HttpURLConnection connection;
        private BufferedInputStream inputStream;

        MyDataSource(@NonNull URL streamURL) throws Throwable {
            this.connection = (HttpURLConnection) streamURL.openConnection();
            this.connection.setRequestMethod("GET");
            this.connection.addRequestProperty("Icy-Metadata", "0");
            this.connection.connect();
            int responseCode = this.connection.getResponseCode();
            if (responseCode != 200)
                throw new IOException("http response code " + responseCode);
            for (Map.Entry<String, List<String>> header: this.connection.getHeaderFields().entrySet()) {
                for (String headerValue : header.getValue())
                    Log.v(TAG, "responseHeader(" + header.getKey() + ") = "" + headerValue + """);
            }
            this.inputStream = new BufferedInputStream(connection.getInputStream());
        }

        @Override
        public long getSize() {
            return -1;
        }

        @Override
        public int readAt(long position, @NonNull byte[] buffer, int offset, int size) throws IOException {
            int bytesRead;
            int bytesReadTotal = 0;
            do {
                bytesRead = this.inputStream.read(buffer, offset + bytesReadTotal, size - bytesReadTotal);
                bytesReadTotal += bytesRead;
            } while(bytesRead != 0 && bytesReadTotal < size);
            return bytesReadTotal;
        }

        @Override
        public void close() {
            try {
                if (inputStream != null) {
                    inputStream.close();
                    inputStream = null;
                }
                if (connection != null) {
                    connection.disconnect();
                    connection = null;
                }
            } catch(IOException e) {
                Log.e(TAG, "close", e);
            }
        }
    }

And when I'm trying to play MP3 stream (ex. A.0.0.00Radio):



    MyDataSource dataSource = new MyDataSource(new URL("http://streaming.shoutcast.com/80sPlanet"));
    MediaExtractor mediaExtractor = new MediaExtractor();
    mediaExtractor.setDataSource();
    MediaFormat mediaFormat = mediaExtractor.getTrackFormat(0);
    String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
    Log.v("Player", "mime: " + mime);
    mediaExtractor.selectTrack(0);
    MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);
    mediaCodec.configure(this.mediaFormat, null, null, 0);
    int sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
    AudioTrack audioTrack = 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);
    mediaCodec.setCallback(new MyCodecCallback());
    mediaCodec.start();
    audioTrack.play();

I see the following Logcat trace:


    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(null) = "HTTP/1.1 200 OK"
    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Access-Control-Allow-Headers) = "Origin, Accept, X-Requested-With, Content-Type"
    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Access-Control-Allow-Methods) = "GET, OPTIONS, HEAD"
    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Access-Control-Allow-Origin) = "*"
    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Cache-Control) = "no-cache, no-store"
    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Connection) = "close"
    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Content-Type) = "audio/mpeg"
    07-24 18:11:49.958 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Date) = "Mon, 24 Jul 2017 18:11:59 GMT"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Expires) = "Mon, 26 Jul 1997 05:00:00 GMT"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-br) = "128"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-genre) = "Decades,80s"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-name) = "A.0.0.00Radio:All 80s All The Time"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice1) = "<BR>This stream requires <a href="http://www.winamp.com">Winamp</a><BR>"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice2) = "SHOUTcast DNAS/posix(linux x64) v2.5.1.725<BR>"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-pub) = "1"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-sr) = "44100"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(icy-url) = "http://a.0.00radio.com/80s/"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Pragma) = "no-cache"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(Server) = "Icecast 2.3.3-kh8"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Received-Millis) = "1500919909958"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Response-Source) = "NETWORK 200"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Selected-Protocol) = "http/1.1"
    07-24 18:11:49.959 7408-7671/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Sent-Millis) = "1500919909816"
    07-24 18:11:49.961 7408-7671/com.sample.sandbox E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library "libwvm.so" not found
    07-24 18:11:49.962 7408-7671/com.sample.sandbox V/Player: mime: audio/mpeg
    07-24 18:11:49.964 7408-7681/com.sample.sandbox I/OMXClient: Using client-side OMX mux.
    07-24 18:11:50.170 7408-7681/com.sample.sandbox I/MediaCodec: MediaCodec will operate in async mode
    [...]

It seems that everything is OK (the stream is actually playing through the device).
But if I try to open AAC stream (ex. COOLfahrenheit 93):



    MyDataSource dataSource = new MyDataSource(new URL("http://111.223.51.8:8005"));
    [...]

the MediaExtractor goes mad:

    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(null) = "HTTP/1.0 200 OK"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(content-type) = "audio/aacp"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-br) = "128"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-genre) = "Easy Listening, Pop"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-name) = "COOLfahrenheit 93 - (4)"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice1) = "<BR>This stream requires <a href="http://www.winamp.com">Winamp</a><BR>"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-notice2) = "SHOUTcast DNAS/posix(linux x64) v2.4.7.256<BR>"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-pub) = "1"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(icy-url) = "http://www.coolism.net"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Received-Millis) = "1500921523862"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Response-Source) = "NETWORK 200"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Selected-Protocol) = "http/1.0"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Android-Sent-Millis) = "1500921523387"
    07-24 18:38:43.863 32284-32690/com.sample.sandbox V/MyDataSource: responseHeader(X-Clacks-Overhead) = "GNU Terry Pratchett"
    07-24 18:38:45.424 32284-32690/com.sample.sandbox E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library "libwvm.so" not found
    07-24 18:38:45.425 32284-32690/com.sample.sandbox E/PlayerThread: error
                                                                              java.io.IOException: Failed to instantiate extractor.
                                                                                  at android.media.MediaExtractor.setDataSource(Native Method)
                                                                                  at com.sample.sandbox.Player.open(Player.java:204)
                                                                                  at com.sample.sandbox.Player.<init>(Player.java:231)
                                                                                  at com.sample.sandbox.PlayerThread.run(PlayerThread.java:28)


Does anybody know what is the issue? The issue is definitely is not in the stream itself - it is fully valid.

解决方案

My main mistake was to ignore the position argument of the MediaDataSource::readAt() method.
It turned out that MediaExtractor performs A LOT of skips through the file (forward as well as backward! - and this is very annoying when you are streaming HLS).
I've been noticed that amount of these skips (and its' range) depend on the content type as well as particular codec. And my main point in this question was "MediaExtractor does not understand aacp" because AAC codec strongly requires skipping, but MP3 codec does not.

这篇关于MediaExtractor 不理解音频/AACP 流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆