MediaExtractor 不理解音频/AACP 流 [英] MediaExtractor does not understand audio/aacp streams
问题描述
我有自己的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.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"));[...]
我的主要错误是忽略了 I have my own And when I'm trying to play I see the following It seems that everything is OK (the stream is actually playing through the device). the My main mistake was to ignore the 这篇关于MediaExtractor 不理解音频/AACP 流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!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.
有人知道是什么问题吗?问题绝对不在于流本身 - 它是完全有效的.MediaDataSource::readAt()
方法的 position
参数.
结果证明 MediaExtractor
在文件中执行了很多跳过(向前和向后! - 这在您流式传输 HLS 时非常烦人).
我已经注意到这些跳过的数量(及其范围)取决于内容类型以及特定的编解码器.我在这个问题中的主要观点是MediaExtractor
不理解 aacp
",因为 AAC 编解码器强烈要求跳过,但 MP3 编解码器不需要.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);
}
}
}
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();
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
[...]
But if I try to open AAC
stream (ex. COOLfahrenheit 93):
MyDataSource dataSource = new MyDataSource(new URL("http://111.223.51.8:8005"));
[...]
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.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.