Android MediaCodec解码Tello无人机流时持续输出INFO_TRY_AGAIN_LATER
问题:MediaCodec解码Tello H.264流时持续返回INFO_TRY_AGAIN_LATER,SurfaceView黑屏
我正在开发一款Android应用,使用MediaCodec API解码Tello无人机的H.264视频流,编写了H264Decoder类处理解码流程,初始化时传入SurfaceHolder用于渲染解码后的帧。但目前遇到问题:持续收到"INFO_TRY_AGAIN_LATER"提示,SurfaceView始终黑屏,无帧渲染。
H264Decoder类代码
public class H264Decoder { private MediaCodec codec; private SurfaceHolder surfaceHolder; private long timeoutUs = 20000; // 20 milliseconds timeout private long presentationTimeUs = 0; private static final String TAG = "H264Decoder"; public byte[] csd0 = {0, 0, 0, 1, 103, 66, 0, 42, (byte) 149, (byte) 168, 30, 0, (byte) 137, (byte) 249, 102, (byte) 224, 32, 32, 32, 64}; public byte[] csd1 = {0, 0, 0, 1, 104, (byte) 206, 60, (byte) 128, 0, 0, 0, 1, 6, (byte) 229, 1, (byte) 151, (byte) 128}; public H264Decoder(SurfaceHolder surfaceHolder) { this.surfaceHolder = surfaceHolder; Log.d(TAG, "H264Decoder constructor called"); } public void init() { Log.d(TAG, "Initializing H264Decoder"); try { codec = MediaCodec.createDecoderByType("video/avc"); MediaFormat format = MediaFormat.createVideoFormat("video/avc", 960, 720); format.setByteBuffer("csd-0", ByteBuffer.wrap(csd0)); format.setByteBuffer("csd-1", ByteBuffer.wrap(csd1)); codec.configure(format, surfaceHolder.getSurface(), null, 0); codec.start(); Log.d(TAG, "Codec started"); } catch (IOException e) { Log.e(TAG, "Error initializing codec: " + e.getMessage(), e); } } public void decode(byte[] input) { Log.d(TAG, "Decoding input of length: " + input.length); try { int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs); Log.d(TAG, "Input buffer index: " + inputBufferIndex); if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferIndex); if (inputBuffer != null) { inputBuffer.clear(); inputBuffer.put(input); codec.queueInputBuffer(inputBufferIndex, 0, input.length, presentationTimeUs, 0); Log.d(TAG, "Input buffer queued"); presentationTimeUs += 1000000 / 25; // Assuming a frame rate of 25fps } else { Log.w(TAG, "Input buffer is null"); } } MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, timeoutUs); while (outputBufferIndex >= 0) { codec.releaseOutputBuffer(outputBufferIndex, true); outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, timeoutUs); } if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { Log.w(TAG, "No output buffer available, try again later"); } } catch (Exception e) { Log.e(TAG, "Error decoding input: " + e.getMessage(), e); } } public void release() { Log.d(TAG, "Releasing H264Decoder"); try { codec.stop(); codec.release(); Log.d(TAG, "Codec released"); } catch (Exception e) { Log.e(TAG, "Error releasing codec: " + e.getMessage(), e); } } }
UDPReceiver类代码
public class UDPReceiver { private DatagramSocket socket; private byte[] buffer = new byte[2048]; private static final String TAG = "UDPReceiver"; public UDPReceiver(int port) { Log.d(TAG, "Attempting to create DatagramSocket on port " + port); try { socket = new DatagramSocket(port); Log.d(TAG, "DatagramSocket created successfully on port " + port); } catch (SocketException e) { Log.e(TAG, "Error creating socket on port " + port + ": " + e.getMessage(), e); } } public byte[] receive() throws IOException { Log.d(TAG, "Waiting to receive packet..."); DatagramPacket packet = new DatagramPacket(buffer, buffer.length); try { socket.receive(packet); Log.d(TAG, "Packet received. Length: " + packet.getLength() + " bytes from " + packet.getAddress().toString()); } catch (IOException e) { Log.e(TAG, "Error receiving packet: " + e.getMessage(), e); throw e; } return packet.getData(); } public void close() { Log.d(TAG, "Closing DatagramSocket..."); socket.close(); Log.d(TAG, "DatagramSocket closed."); } }
我已验证传入decode方法的是有效的H.264数据,但仍不断收到该警告,SurfaceView无画面。请问是什么原因导致的?如何确保解码后的帧能正常渲染到SurfaceView?
解决方案
问题根源在于Tello无人机的视频流传输机制:它不会直接发送完整的H.264帧,而是将每帧拆分为多个UDP分片发送——大部分分片长度为1460字节,最后一个分片长度较小,作为帧结束的标识。
之前的代码直接将单个UDP分片传入解码器,解码器无法处理不完整的帧,因此持续返回INFO_TRY_AGAIN_LATER,无法输出可渲染的帧。
解决方法是在UDP接收环节增加帧组装逻辑:收集所有属于同一帧的UDP分片,拼接成完整的H.264帧后,再传入decode方法进行解码。这样解码器就能正常处理完整帧,将画面渲染到SurfaceView上。
内容的提问来源于stack exchange,提问作者darukun
相关产品推荐
相关产品推荐

