You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

复制Payload流时出现异常,Camera预览帧传输问题求助

解决「Exception Copying Stream from Payload」异常的排查思路

我之前做Android实时视频传输的时候也碰到过类似的流复制异常,结合你描述的Camera预览帧传输场景,大概率是帧数据处理或流操作环节出了问题,咱们一步步来排查:

  • 首先排查预览帧的格式是否匹配传输要求
    Camera的onPreviewFrame返回的字节数组默认是YUV格式(多数设备是NV21),如果你的sendFramesOfImages方法期望的是压缩后的图像流(比如JPEG),直接把原始YUV字节转成InputStream传输会导致对方无法解析,进而抛出流复制异常。
    解决方法是先把YUV数据压缩成JPEG再生成流:

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        Camera.Parameters params = camera.getParameters();
        Camera.Size size = params.getPreviewSize();
        // 将NV21格式的YUV数据转成JPEG字节数组
        YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            // 压缩质量可根据需求调整(0-100)
            yuvImage.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, baos);
            byte[] jpegBytes = baos.toByteArray();
            // 用try-with-resources自动关闭流,避免资源泄漏
            try (ByteArrayInputStream is = new ByteArrayInputStream(jpegBytes)) {
                sendFramesOfImages(is);
            }
        } catch (IOException e) {
            Log.e("CameraPreview", "Frame processing error", e);
        }
    }
    
  • 检查流的创建与复用是否正确
    如果你复用了同一个InputStream对象,或者没有在每次回调时创建新的流,会导致流的读取指针已经到末尾,后续读取时抛出异常。务必保证每次onPreviewFrame回调都创建全新的ByteArrayInputStream,并且用try-with-resources语法自动关闭流,避免资源泄漏。

  • 确认线程安全问题
    onPreviewFrame是在Camera的内部回调线程执行的,如果sendFramesOfImages方法是在主线程或者其他线程中处理流,可能出现并发读写流的情况,导致复制过程中出错。
    可以用Handler把流处理任务切换到指定线程,比如:

    private final Handler mWorkHandler = new Handler(Looper.getMainLooper()); // 或者自定义线程的Looper
    
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        // 先在回调线程处理YUV转JPEG
        Camera.Parameters params = camera.getParameters();
        Camera.Size size = params.getPreviewSize();
        YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        yuvImage.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, baos);
        byte[] jpegBytes = baos.toByteArray();
        
        // 切换到目标线程执行传输
        mWorkHandler.post(() -> {
            try (ByteArrayInputStream is = new ByteArrayInputStream(jpegBytes)) {
                sendFramesOfImages(is);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
    
  • 检查sendFramesOfImages内部的流复制逻辑
    异常提示是复制流时出错,可能是这个方法内部的读取逻辑有问题,比如误用了available()方法来判断总长度(available()仅返回当前可读取的字节数,不是流的总长度),或者没有正确处理流的结束标记(read()返回-1)。
    正确的流复制逻辑应该是这样的:

    public void sendFramesOfImages(InputStream is) throws IOException {
        byte[] buffer = new byte[4096]; // 用合适大小的缓冲区
        int bytesRead;
        while ((bytesRead = is.read(buffer)) != -1) {
            // 这里替换成你的发送逻辑,比如写入Socket输出流
            yourOutputStream.write(buffer, 0, bytesRead);
        }
        yourOutputStream.flush(); // 确保数据全部发送
    }
    

内容的提问来源于stack exchange,提问作者NullPointerExcepzion

火山引擎 最新活动