我正在开发一个通过RTP接收H264编码数据的应用程序,但我无法让Android的MediaCodec输出任何内容。我正在按照https://stackoverflow.com/a/7668578/10788248对RTP数据包进行解包
在编码帧被重新组装后,我将它们输入到出列的输入缓冲区中。
当我对输入缓冲区进行排队时,我不会得到任何错误,但是解码器的回调从来不会调用onOutputBufferAvailable方法。
我能够调用它的唯一方法是通过传递流结束标志,然后输出大小为0。
LinkedList<DatagramPacket> packets = new LinkedList<>();
Thread socketReader = new Thread(() -> {
try {
DatagramSocket socket = new DatagramSocket(videoPort);
socket.connect(InetAddress.getByName(remoteAddress),remotePort);
byte[] b;
while (true){
b = new byte[1500];
DatagramPacket p = new DatagramPacket(b,1500);
socket.receive(p);
//Log.d(TAG,"RTP: "+bytesToHex(p.getData()));
packets.add(p);
}
} catch (Exception e) {
e.printStackTrace();
}
});
@SuppressLint({"NewApi", "LocalSuppress"}) Thread packetHandler = new Thread(() -> {
try {
MediaCodec decoder = MediaCodec.createDecoderByType(MIME_TYPE);
MediaFormat decoderFormat = MediaFormat.createVideoFormat(MIME_TYPE, 1280, 720);
LinkedList<ByteBuffer> decoderInputs = new LinkedList<>();
LinkedList<Integer> decoderIndices = new LinkedList<>();
decoder.setCallback(new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
decoderInputs.add(codec.getInputBuffer(index));
decoderIndices.add(new Integer(index));
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
Log.d(TAG,"OUTPUT AVAILABLE!!!!");
Log.d(TAG, String.valueOf(info.size));
}
@Override
public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
Log.e(TAG,e.toString());
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
Log.d(TAG,"FORMAT CHANGED");
}
});
decoder.configure(decoderFormat, null,null,0);
decoder.start();
RTPFrame frame = new RTPFrame();
boolean sps = false;
boolean pps = false;
while (true){
if(decoderIndices.peek()!=null){
DatagramPacket packet = packets.poll();
if(packet!=null){
byte[] data = packet.getData();
if(frame==null||(!frame.isInitialized())){
frame = new RTPFrame(data);
}
else{
if(frame.isComplete()){
Integer index = null;
ByteBuffer decoderInput = null;
try {
decoderInput = decoderInputs.poll().put(frame.getFrame());
index = decoderIndices.poll();
int size = frame.getFrameSize();
if (frame.SPS) {
Log.d(TAG,"Depacketized at "+Integer.toUnsignedString(frame.getTime())+" length = "+frame.getFrameSize()+": " + bytesToHex(frame.getFrame().array()));
decoder.queueInputBuffer(index, 0, size, frame.getTime(), MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
sps = true;
pps = false;
} else if (frame.PPS) {
if(sps){
Log.d(TAG,"Depacketized at "+Integer.toUnsignedString(frame.getTime())+" length = "+frame.getFrameSize()+": " + bytesToHex(frame.getFrame().array()));
decoder.queueInputBuffer(index, 0, size, frame.getTime(), MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
pps = true;
}
} else if (!frame.badFrame) {
if(sps&&pps){
Log.d(TAG,"Depacketized at "+Integer.toUnsignedString(frame.getTime())+" length = "+frame.getFrameSize()+": " + bytesToHex(frame.getFrame().array()));
decoder.queueInputBuffer(index, 0, size, frame.getTime(), 0);
}
}
else{
throw new RuntimeException();
}
}
catch(Exception e){
e.printStackTrace();
if(index!=null){
decoderIndices.push(index);
}
decoderInput.clear();
decoderInputs.push(decoderInput);
}
frame = new RTPFrame();
}
else{
frame.addNALUnit(data);
}
}
if(frame.isComplete()){
Integer index = null;
ByteBuffer decoderInput = null;
try {
decoderInput = decoderInputs.poll().put(frame.getFrame());
index = decoderIndices.poll();
int size = frame.getFrameSize();
if (frame.SPS) {
Log.d(TAG,"Depacketized at "+Integer.toUnsignedString(frame.getTime())+" length = "+frame.getFrameSize()+": " + bytesToHex(frame.getFrame().array()));
decoder.queueInputBuffer(index, 0, size, frame.getTime(), MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
sps = true;
pps = false;
} else if (frame.PPS) {
if(sps){
Log.d(TAG,"Depacketized at "+Integer.toUnsignedString(frame.getTime())+" length = "+frame.getFrameSize()+": " + bytesToHex(frame.getFrame().array()));
decoder.queueInputBuffer(index, 0, size, frame.getTime(), MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
pps = true;
}
} else if (!frame.badFrame) {
if(sps&&pps){
Log.d(TAG,"Depacketized at "+Integer.toUnsignedString(frame.getTime())+" length = "+frame.getFrameSize()+": " + bytesToHex(frame.getFrame().array()));
decoder.queueInputBuffer(index, 0, size, frame.getTime(), 0);
}
}
else{
throw new Exception("bad frame");
}
}
catch(Exception e){
e.printStackTrace();
if(index!=null){
decoderIndices.push(index);
}
decoderInput.clear();
decoderInputs.push(decoderInput);
}
frame = new RTPFrame();
}
}
}
}
} catch (Exception e) {e.printStackTrace();}
});
这里是我收到的一帧的所有NAL单元的示例,以及我是如何将它们解包的。
RTP:80 63 00 2D 27 0E 64 30 66 B4 BA 42 3C 81 E0 00 80 6F...
RTP:80 63 00 2E 27 0E 64 30 66 B4 BA 42 3C 01 F3 0F E0 3F...
RTP:80 63 00 31 27 0E 64 30 66 B4 BA 42 3C 01 FA D8 A9 FF...
RTP:80 63 00 32 27 0E 64 30 66 B4 BA 42 3C 01 1B C5 BC C0...
RTP:80 63 00 33 27 0E 64 30 66 B4 BA 42 3C 01 0F F4 9A DE...
d/rtpreader:在2985639104解包长度=12611:00 00 00 01 21 E0 00 80 6F F0 B4 24 CD 5F 45 80 79 6E 0C...
下面是解包前后的SPS和PPS示例
RTP:80 63 00 00 5F DF A1 70 4F 2F 8A 3E 27 42 00 1F 8D 68 05 00 5B A1 00 00 03 00 01 00 00 03 00 1E 0F 10 7A 80
解包于1608491376长度=7:00 00 01 28 CE 32 48
RTP:80 63 00 01 5F DF A1 70 4F 2F 8A 3E 28 CE 32 48
我要感谢@greeble31和@chrisbe,感谢他们帮我弄清楚这个问题。问题确实出在我的RTPFrame类上。特别是getFrame()
方法,该方法遍历包含NAL单元数据的ByteBuffer列表,并使用put(ByteBuffer)
将它们添加到BB
。这会增加bb
的位置
直到它等于limit
。
public ByteBuffer getFrame() {
size = 4;
int nalCount = nalUnits.size();
for (int i = 0;i<nalCount;i++){
size+=nalUnits.get(i).data.length;
}
ByteBuffer bb = ByteBuffer.allocate(size);
bb.put(new byte[]{0,0,0,1});
for(NALUnit unit:nalUnits){
bb.put(unit.data);
}
return bb;
}
将返回BB;
更改为返回(ByteBuffer)BB.Position(0);
修复了此问题。
我正在解码从Android上的wifi摄像头接收到的原始h264。 这是解码时产生的视频的一个例子,除了底部部分看起来很好。 我还注意到一些奇怪的事情,当我移动摄像机时,饲料似乎运行几乎完全流畅(底部没有垃圾),一旦我把它放下,垃圾视频返回(我会以为它是相反的方式...) 我正在将h264数据解析成以澳元开头的块,每个块以澳元开头,当另一个开始时结束。 我的理解是,每个解析的“块”(以AUD开头)
我正在使用对三星S6上的h264流进行解码,发现mediacodec的输入缓冲区必须以“0001”开头(并且不需要设置pps、sps),否则ACodec将报告错误。 我也尝试使用mediaextractor播放一个mp4文件,它工作良好,但缓冲区到mediacodec不是以“0001”开始。 我不知道为什么decodec一个h264流有这样的限制,目前我需要从socket分析流,并将数据切割成小包
我正在尝试用android低级媒体API实时解码h264 nals。 每个nal都包含一个完整的帧,所以我希望在用我的nal提供输入并调用之后,它会“立即”(当然有一个litle延迟)显示我的帧,但它没有显示。我看到了第一个帧,出列器返回第一个缓冲区,只有在将第二个缓冲区馈送给解码器之后才返回第一个缓冲区,此时该缓冲区应该呈现第二个帧。帧编码时预置为x264,因此没有B帧等。 我想可能有一种方法可
我从服务器接收到h264数据,我想在Android上使用mediacodec和texture view对该流进行解码。我从服务器获取数据,解析它得到SPS、PPS和视频帧数据,然后我将该数据传递给mediacodec,但函数dequeueOutputBuffer(info,100000)总是返回-1,并且我得到dequeueOutputBuffer超时。 请帮忙,我三周来一直在忙这个问题。 这是用
我之前也尝试使用此方法创建Mediacodec 但视频质量与YouTube上分享的视频一样。
我可以看到视频播放在我的TextureView,但它是相当腐败。我已经验证了我正在以正确的顺序接收完整的数据包。我已经能够正确解析RTP头。我相信我的问题与SPS和PPS以及MediaCodec有关。 正如您所看到的,我没有从我的视频流中为MediaFormat提供SPS和PPS,而是使用了一个internet示例中的硬编码集。我试图找到解释如何从数据包中提取SPS和PPS的源,但没有找到任何东西