You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 09:00:55