我的StreamPlayer有一个奇怪的问题,我需要任何帮助。
我需要实现的主要目标是StreamPlayer,它能够以尽可能小的延迟播放MPEG-2传输流。为此,我遵循这种方法:
流由基于Java的TS Parser解析。我已经实现了一个类似于MediaExtractor的TSExtractor,它工作正常。我可以像使用带
的MediaExtractor一样接收选定曲目的所有媒体样本extractor.readSampleData(...);
extractor.advance();
要解码我想要创建和配置MediaCodec实例的AAC数据。使用MediaExtractor类通常由
完成MediaFormat mediaFormat = extractor.getTrackFormat(i);
decoder = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));
decoder.configure(mediaFormat, null, null, 0);
由于我必须在TSExtractor.getTrackFormat(int track)方法中初始化MediaFormat,我使用
MediaFormat mf = MediaFormat.createAudioFormat ("audio/mp4a-latm", getSampleRate(), getChannelCount());
并且因为我的所有AAC样本都包括我做的ADTS
mediaFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);
阅读this帖后,我最后使用“csd-0”键添加ESDS帧
mediaFormat.setByteBuffer("csd-0", ByteBuffer.allocate(2).put(new byte[]{(byte) 0x11, (byte)0x90}));
其中从ADTS中提取值0x11和0x90。
当我现在要解码AAC样本时,解码器发布
AAC decoder returned error 4097, substituting silence
到日志。
为了验证我的TSExtractor正确提取样本我使用VLC将相同的流重新转换为mp4文件而不进行转码,因此原始流不变。现在我可以用记录的mp4文件初始化MediaExtractor,并比较我的TSExtractor和MediaExtractor创建的样本。使用跟踪和错误我发现了一个非常奇怪的行为:
当我使用MediaExtractor创建的mediaFormat配置MediaCodec时,MediaCodec会对我的TSExtractor返回的AAC样本进行解码而不会出现任何问题。比较MediaFormat,它基本上包装了由我的TSExtractor创建的HashMap和由MediaExtractor创建的那个,给出了这些差异:
由MediaExtractor创建:
mediaFormat:{max-input-size = 1212,durationUs = 77428875,is-adts = 1, channel-count = 2,mime = audio / mp4a-latm, CSD-0 = java.nio.ByteArrayBuffer [位置= 0,极限= 2,容量= 2], 采样率= 48000}
由TSExtractor创建:
mediaFormat:{is-adts = 1,channel-count = 2,mime = audio / mp4a-latm, CSD-0 = java.nio.ByteArrayBuffer [位置= 2,极限= 2,容量= 2], 采样率= 48000}
即使我采用TSExtractor创建的MediaFormat与MediaExtractor创建的MediaFormat类似,解码器也会使用self创建并使用另一个解码而没有任何问题进行解码。
任何帮助都会非常有用。
答案 0 :(得分:5)
我真的不知道为什么,但事实证明以这种方式初始化“csd-0”ByteBuffer
mediaFormat.setByteBuffer("csd-0", ByteBuffer.allocate(2).put(new byte[]{(byte) 0x11, (byte)0x90}));
不起作用,但是以这种方式初始化
byte[] bytes = new byte[]{(byte) 0x11, (byte)0x90};
ByteBuffer bb = ByteBuffer.wrap(bytes);
mediaFormat.setByteBuffer("csd-0", bb);
确实
BTW,使用
比较这两个byteBuffersbb1.equals(bb2);
返回true。
很奇怪!
答案 1 :(得分:3)
感谢上面的计算CSD的代码。不幸的是,这对我不起作用。我的解码器失败了上面的csd设置。最后我发现了这个问题。根据文档第一" 5位" CSD是对象类型(Profile)。以上代码配置文件仅添加到4位。所以更改下面的代码对我来说很好
int profile = (header[2] & 0xC0) >> 6;
int srate = (header[2] & 0x3C) >> 2;
int channel = ((header[2] & 0x01) << 2) | ((header.[3] & 0xC0) >> 6)
ByteBuffer csd = ByteBuffer.allocate(2);
csd.put(0, (byte)(profile << 3 | srate >> 1));
csd.put(1, (byte)((srate & 0x01) << 7 | channel << 3));
答案 2 :(得分:2)
在失败的情况下,你可能需要先调用ByteBuffer的倒带方法。如果你仔细看,你会发现MediaExtractor和TSExtractor之间的位置不同:
CSD-0 = java.nio.ByteArrayBuffer [<强>位置= 0 下,极限= 2,容量= 2]
VS
CSD-0 = java.nio.ByteArrayBuffer [<强>位置= 2 下,极限= 2,容量= 2]
ByteBuffer的equals仅比较位置之后的字节,直到不匹配为止;在你的情况下,一个缓冲区已经定位在最后,因此没有不匹配。
答案 3 :(得分:2)
csd-0中的值取决于ADTS标头。
ADTS标头长度最多为9个字节。要生成csd-0,您需要标题的第二个和第三个字节。
int profile = (header[2] & 0xC0) >> 6;
int srate = (header[2] & 0x3C) >> 2;
int channel = ((header[2] & 0x01) << 2) | ((header.[3] & 0xC0) >> 6)
ByteBuffer csd = ByteBuffer.allocate(2);
csd.put(0, (byte)( ((profile + 1) << 3) | srate >> 1 ) );
csd.put(1, (byte)( ((srate << 7) & 0x80) | channel << 3 ) );
现在你为这个aac音频流获得了有效的csd-0。