Java从网络摄像头获取图像并通过套接字发送

时间:2016-06-24 03:30:32

标签: java sockets memory memory-management webcam

我正在使用sarxos http://webcam-capture.sarxos.pl/

Webcam capture API

我编写此代码以不断从网络摄像头获取图像并通过套接字将其发送到服务器。

客户端代码

public static void main(String[] args) throws IOException
{
    Socket socket = new Socket("127.0.0.1", 54339);
    ObjectOutputStream sender = new ObjectOutputStream(socket.getOutputStream());
    Webcam wCam = Webcam.getDefault();
    wCam.setViewSize(WebcamResolution.VGA.getSize());
    wCam.open();
    while (true)
    {
        sender.writeObject(new ImageIcon(wCam.getImage()));
    }
}

服务器代码

public static void main(String[] args) throws IOException 
{
    ServerSocket server = new ServerSocket(54339);
    Socket socket = server.accept();

    ObjectInputStream rcv = new ObjectInputStream(socket.getInputStream());
    while (true)
    {
        rcv.readObject();
        System.out.println("receive");
    }
}

但是大约2到3分钟之后,我的客户端内存不足而卡住了,因为它需要太多内存。

我认为这是因为这一行new ImageIcon(wCam.getImage())但我不知道如何修复它。

我已经尝试了sender.flush(),但这不起作用

1 个答案:

答案 0 :(得分:1)

基本上ObjectOutputStream.writeObject()ObjectInputStream.readObject()会为他们发送/接收的对象维护一个 hard references的表格。当ObjectOutputStream对象重新发送时,仅发送对象的句柄,而ObjectInputStream将接收到的句柄转换为先前接收的对象的引用。此功能可以减少带宽和内存使用量,但仅限于定期重新发送对象的程序。由于ObjectInputStream/ObjectOutputStream硬引用这些对象,Garbage Collector无法收集它们,最终这些对象成为内存泄漏。

为了避免这种情况,Java提供ObjectOutputStream.writeUnshared()ObjectInputStream.readUnshared()方法,但这些方法也有已知的内存泄漏问题,请查看JDK-6525563 : Memory leak in ObjectOutputStream以获取有关该方法的更多信息问题。

所以你有两个选择;

  1. 使用read/writeUnshared()代替read/writeObject(),并且每隔一段时间调用ObjectOutputStream的{​​{1}}方法,以释放reset()泄露的内存,
  2. 避免使用ObjectOutputStream。您可以像这样ObjectInput/OutputStream读/写BufferedImage;
  3. 写作:

    DataInput/OutputStream

    读:

    try (DataOutputStream sender = new DataOutputStream(new BufferedOutputStream(new Socket("127.0.0.1", 54339).getOutputStream()))) //never use DataStreams without buffering, too slow
    {
        while (true)
        {
            BufferedImage frame = wCam.getImage(); //get frame from webcam
            int frameWidth = frame.getWidth();
            int frameHeight = frame.getHeight();
    
            sender.writeInt(frameWidth); //write image with
            sender.writeInt(frameHeight); //write image height
    
            int[] pixelData = new int[frameWidth * frameHeight];
            frame.getRGB(0, 0, frameWidth, frameHeight, pixelData, 0, frameWidth);
    
            for (int i = 0; i < pixelData.length; i++)
            {
                sender.writeInt(pixelData[i]); //write pixel data
            }
        }
    }
    

    我的方法2的实现,在服务器上显示接收到的图像;

    try (DataInputStream rcv = new DataInputStream(new BufferedInputStream(socket.getInputStream()))) //never use DataStreams without buffering, too slow { while (true) { int frameWidth = rcv.readInt(); //read image with int frameHeight = rcv.readInt(); //read image height int[] pixelData = new int[frameWidth * frameHeight]; for (int i = 0; i < pixelData.length; i++) { pixelData[i] = rcv.readInt(); //read pixel data } BufferedImage frame = new BufferedImage(frameWidth, frameHeight, BufferedImage.TYPE_INT_RGB); //create immage frame.setRGB(0, 0, frameWidth, frameHeight, pixelData, 0, frameWidth); //set pixel data //do whatever you want with frame } }

    Server.java

    public class Server { public static void main (String[] args) throws IOException, ClassNotFoundException { ServerSocket server = new ServerSocket(54339); Socket socket = server.accept(); JFrame jframe = new JFrame(); jframe.setSize(800, 600); jframe.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); jframe.setLayout(new BorderLayout()); ImageDisplayPanel imageDisplayPanel = new ImageDisplayPanel(); jframe.add(imageDisplayPanel, BorderLayout.CENTER); jframe.setVisible(true); try (DataInputStream rcv = new DataInputStream(new BufferedInputStream(socket.getInputStream()))) { while (true) { int frameWidth = rcv.readInt(); int frameHeight = rcv.readInt(); int[] pixelData = new int[frameWidth * frameHeight]; for (int i = 0; i < pixelData.length; i++) { pixelData[i] = rcv.readInt(); } BufferedImage frame = new BufferedImage(frameWidth, frameHeight, BufferedImage.TYPE_INT_RGB); frame.setRGB(0, 0, frameWidth, frameHeight, pixelData, 0, frameWidth); imageDisplayPanel.setBackground(frame); } } } private static class ImageDisplayPanel extends JPanel { private static final Object BACKGROUND_LOCK = new Object(); private BufferedImage background = null; public ImageDisplayPanel () throws HeadlessException { this.setDoubleBuffered(true); //to avoid flicker } public void setBackground (Image newBackground) { synchronized (BACKGROUND_LOCK) { if (background == null) { background = new BufferedImage(newBackground.getWidth(null), newBackground.getHeight(null), BufferedImage.TYPE_INT_RGB); } else if (background.getWidth() != newBackground.getWidth(null) || background.getHeight() != newBackground.getHeight(null)) { background.flush();//flush old resources first background = new BufferedImage(newBackground.getWidth(null), newBackground.getHeight(null), BufferedImage.TYPE_INT_RGB); } Graphics graphics = background.createGraphics(); graphics.drawImage(newBackground, 0, 0, null); } repaint(); } @Override public void paint (Graphics g) { super.paint(g); synchronized (BACKGROUND_LOCK) { if (background != null) { g.drawImage(background, 0, 0, getWidth(), getHeight(), null); } } } } }

    Client.java