Java通信原理(5)——视频通信

一、概述

        在实现视频通信前我们先了解一下UDP通信协议,UDP即用户数据报协议,UDP 只在 IP 的数据报服务之上增加了很少一点的功能,这就是复用分用的功能以及差错检测的功能。有区别与TCP/IP。UDP通信特点如下:

(1)、无连接性,即收发数据时不需要建立连接,由此可以大幅减少数据收发的延迟

(2)、UDP 使用尽最大努力交付。即不保证可靠交付,因此主机不需要维护复杂的连接状态表

(3)、UDP 没有拥塞控制。因此网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的。很多的实时应用要求源主机以恒定的速率发送数据,并且允许在网络出现拥塞时丢失一部分数据,但却不允许数据有太大的时延。UDP 协议正好适合这种要求。

二、为何使用UDP

        上述提到UDP通信协议虽然不可靠,但数据收发的延迟低,同时无拥塞控制。对于视频这种流媒体来说,用户体验时比较靠前的因素,即尽可能保持视频不会产生卡顿。因此UDP通信协议是视频传输首选的协议。

三、实现步骤

        1、启动摄像头

                对于Java视频通信来说,首先是需要捕捉图像,因此需要使用Java调用电脑自带的摄像头。对于Java调用摄像头有很多驱动,如openCV等,这里主要使用webcam包进行讲解。启动摄像头时我们需要一个窗口面板来展示画面。

        2、图像编码

                若要将图像发送出去,需要将图像转化为数据流,并通过UDP协议发送。因此需要将图像进行编码。

                简单来说一个30帧的视频在每秒钟会刷新30张图片,而视频通信只需要将每秒中的30张图片编码并发送即可,因此视频编码就可以简化为图片编码,再通过线程休眠进行速率的控制,即可完成对视频编码。

                图片编码即需要获取整张图片所有的像素值,通过BufferImage缓冲后将图片的编码信息一次性发送出去。实现代码如下。

import com.github.sarxos.webcam.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

//创建视频通话界面,并实现webapp中提供的接口
public class CamStart extends JFrame implements Runnable, WebcamListener, WindowListener, Thread.UncaughtExceptionHandler, ItemListener, WebcamDiscoveryListener {
    JFrame videoFrame;
    WebcamPanel northPanel;//创建WebcamPanel即需要通过一个面板显示画面
    JPanel southPanel;
    static Webcam webcam = null;//创建Webcam对象
    WebcamPanel panel = null;
    WebcamPicker picker = null;//创建摄像头选择器
    String windowTitle;
  

    public CamStart(String windowTitle){
        this.windowTitle = windowTitle;
    }

 

    public void setWindow(){
        videoFrame = new JFrame(windowTitle);
        videoFrame.setSize(640 , 640);
        videoFrame.setResizable(false);
        videoFrame.addWindowListener(this);
        videoFrame.add(northPanel , BorderLayout.NORTH);

        southPanel = new JPanel();
        southPanel.setPreferredSize(new Dimension(640 , 120));
        JButton jb1 = new JButton("接通");
        JButton jb2 = new JButton("挂断");
        jb1.setBounds(30 , 100 , 70 , 30);
        jb2.setBounds(120 , 100 , 70 , 30);
        southPanel.add(jb1);
        southPanel.add(jb2);

        videoFrame.add(southPanel , BorderLayout.SOUTH);
        videoFrame.setLocationRelativeTo(null);
        videoFrame.setVisible(true);
        videoFrame.setDefaultCloseOperation(1);
        jb1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

            }
        });
        jb2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                shutDownTip();
            }
        });
    }

    public void getCam(){
        //查找本地机的摄像头并启动
        Webcam.addDiscoveryListener(this);
        picker = new WebcamPicker();
        webcam = picker.getSelectedWebcam();
        //未找到摄像头的情况
        if (webcam == null) {
            JFrame tip = new JFrame("提示");
            tip.setSize(300 , 200);
            tip.setLocationRelativeTo(null);
            JPanel panel1 = new JPanel();
            panel1.setPreferredSize(new Dimension(220 , 100));
            JLabel label = new JLabel("未在您的设备上找到摄像头,请检查连接情况");
            label.setSize(200 , 90);
            panel1.add(label);

            JPanel panel2 = new JPanel();
            panel2.setPreferredSize(new Dimension(220 , 35));
            JButton jb1 = new JButton("确定");
            jb1.setBounds(70 , 40 , 70 , 30);
            panel2.add(jb1);
            tip.add(panel1 , BorderLayout.NORTH);
            tip.add(panel2 , BorderLayout.SOUTH);
            tip.setDefaultCloseOperation(1);
            tip.setVisible(true);
            tip.setResizable(false);
            jb1.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    tip.dispose();
                    tip.setDefaultCloseOperation(1);
                }
            });
        }
        //设置画面尺寸
        webcam.setViewSize(WebcamResolution.VGA.getSize());
        webcam.addWebcamListener(CamStart.this);
        northPanel = new WebcamPanel(webcam , false);
        northPanel.setFPSDisplayed(true);
        Thread t = new Thread(){
            @Override
            public void run() {
                northPanel.start();
            }
        };
        t.setDaemon(true);
        t.setUncaughtExceptionHandler(this);
        t.start();
        new Thread(() -> {
            imageFlush();
        }).start();
    }
    
    //设置视频通信
    public void imageFlush(){
        while (true) {
            try {
                Thread.sleep(33);
                //缓冲图像,避免画面加载错误
                BufferedImage image = webcam.getImage();
                OutputStream out = socket.getOutputStream();
                DataOutputStream flue = new DataOutputStream(out);
                //将图像进行编码
                //获取图像宽高
                int imageWidth = image.getWidth();
                int imageHeight = image.getHeight();
                //将图像尺寸发送,以便于接收端解码
                flue.writeInt(imageWidth);
                flue.writeInt(imageHeight);
                //根据图像宽高逐个获取图像像素
                for (int i = 0; i < imageWidth; i++) {
                    for (int j = 0; j < imageHeight; j++) {
                        flue.writeInt(image.getRGB(i , j));
                    }
                }
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    public void init(){
        socketInit();
        getCam();
        setWindow();
    }


    public void shutDownTip(){
        JFrame tipWindow = new JFrame();
        tipWindow.setTitle("系统提示");
        tipWindow.setSize(300 , 200);
        tipWindow.setLocationRelativeTo(null);
        JPanel panel1 = new JPanel();
        panel1.setPreferredSize(new Dimension(220 , 100));
        JLabel label = new JLabel("是否结束当前视频通信?");
        label.setSize(200 , 90);
        panel1.add(label);

        JPanel panel2 = new JPanel();
        panel2.setPreferredSize(new Dimension(220 , 35));
        JButton jb1 = new JButton("是");
        jb1.setBounds(70 , 40 , 70 , 30);
        JButton jb2 = new JButton("否");
        jb1.setBounds(160 , 40 , 20 , 30);
        panel2.add(jb1);
        panel2.add(jb2);
        tipWindow.add(panel1 , BorderLayout.NORTH);
        tipWindow.add(panel2 , BorderLayout.SOUTH);
        tipWindow.setDefaultCloseOperation(1);
        tipWindow.setVisible(true);
        tipWindow.setResizable(false);
        jb1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                webcam.close();
                videoFrame.dispose();
                tipWindow.dispose();
                videoFrame.setDefaultCloseOperation(1);
                tipWindow.setDefaultCloseOperation(1);
            }
        });
        jb2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tipWindow.dispose();
                tipWindow.setDefaultCloseOperation(1);
            }
        });
    }


    @Override
    public void webcamFound(WebcamDiscoveryEvent webcamDiscoveryEvent) {

    }

    @Override
    public void webcamGone(WebcamDiscoveryEvent webcamDiscoveryEvent) {

    }

    @Override
    public void webcamOpen(WebcamEvent webcamEvent) {

    }

    @Override
    public void webcamClosed(WebcamEvent webcamEvent) {

    }

    @Override
    public void webcamDisposed(WebcamEvent webcamEvent) {

    }

    @Override
    public void webcamImageObtained(WebcamEvent webcamEvent) {

    }

    @Override
    public void itemStateChanged(ItemEvent e) {

    }

    @Override
    public void windowOpened(WindowEvent e) {

    }

    @Override
    public void windowClosing(WindowEvent e) {
        webcam.close();
        this.setDefaultCloseOperation(1);
    }

    @Override
    public void windowClosed(WindowEvent e) {

    }

    @Override
    public void windowIconified(WindowEvent e) {

    }

    @Override
    public void windowDeiconified(WindowEvent e) {

    }

    @Override
    public void windowActivated(WindowEvent e) {

    }

    @Override
    public void windowDeactivated(WindowEvent e) {

    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {

    }
}

四、总结

        本篇主要讲解UDP下的视频通信,目前项目还在改进中,博客将持续更新

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值