原理,利用FFmpegFrameGrabber 采集到视频帧,再利用Java2DFrameConverter 把采集的frame转换为Image,再利用ImageIO把image转发到outputStream。
客户端利用ImageIO收到inputstream 就是image格式的数据,直接showimage()就可显示了
采集放在socket客户端,也可放在serversocket 服务器端
本例采集放在socket端 (只能使用不压缩图片格式:bmp)
import org.bytedeco.javacv.*;
import org.bytedeco.javacv.Frame;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.Socket;
public class Khd1 {
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket=new Socket("192.168.43.187",3000);
OutputStream outputStream=socket.getOutputStream();
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("/dev/video0"); //读摄像头
grabber.start();
Java2DFrameConverter java2DFrameConverter = new Java2DFrameConverter();
while(true) {
Frame frame = grabber.grabFrame();
BufferedImage bufferedImage = java2DFrameConverter.getBufferedImage(frame);
ImageIO.write(bufferedImage, "bmp", outputStream); //bmp,png 验证,只能用这两种图片格式
}
}
}
显示播发服务器端:
import org.bytedeco.javacv.CanvasFrame;
import javax.imageio.ImageIO;
import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
ServerSocket serverSocket = new ServerSocket(3000);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
CanvasFrame canvas = new CanvasFrame("播发");//新建一个预览窗口
while(true) {
Image image = ImageIO.read(inputStream);
canvas.showImage(image);
}
}
}
下一步,利用前面写的利用公网ip,实现的点对点传输数据的程序,把上面程序加进去,做一个内网视频监控。
后面一查网络,不得了,十几分钟传了几百G的流量。这个不是实用程序,只说明原理,真实的情况应该是加入各种数据压缩后才能网络传输。
-------------------------------------------------------------------------------------------------------------------------------
FFmpegFrameGrabber FFmpegFrameRecorder 都可以从网络拉流和推流,采用stmp协议,问题是这俩只能做客户端,不能一个做客户端,一个做服务器组网。所以这两者要一个推流,一个做拉流,中间必须引入第三方stmp服务器,下面就是利用srs stmp服务器,FFmpegFrameGrabber 做拉流,FFrameFrameRecoder 做推流的网络摄像头程序,注意采用stmp协议,必须采用flv 格式。
import org.bytedeco.javacv.*;
import org.bytedeco.javacv.Frame;
import java.io.*;
import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264;
public class Khd1 {
public static void main(String[] args) throws IOException{
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("/dev/video0"); //读摄像头
grabber.start();
FFmpegFrameRecorder recorder=new FFmpegFrameRecorder("rtmp://127.0.0.1/live/livestream",200,150);
recorder.setVideoCodec(AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.start();
while(true) {
Frame frame = grabber.grabFrame();
recorder.record(frame);
}
}
}
import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import java.io.*;
import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264;
public class Test {
public static void main(String[] args) throws IOException{
FFmpegFrameGrabber grabber=new FFmpegFrameGrabber("rtmp://127.0.0.1/live/livestream");
grabber.setFormat("flv");
grabber.setVideoCodec(AV_CODEC_ID_H264);
grabber.start();
CanvasFrame canvas = new CanvasFrame("播发");//新建一个预览窗口
while(true) {
Frame frame=grabber.grab();
canvas.showImage(frame);
}
}
}
=========================================================================不使用ffmpegframerecorder 推流方式,ffmpegframegrabber采集后生成frame视频帧,再生成图片,再压缩图片。客户端与服务器之间直接传递含有图片数据的字节数组。
改进后可以使用jpg格式压缩的程序,使用此方法,感觉视频显示延时很短,明显优于利用srs 服务器中转。但网络传输带宽要大些。采用这种方式,可省去中间stmp 服务器,直接编写点对点程序。如果带宽太小,可以在采集视频时把宽度,高度取小些,在生成图片时把图片帧也取小。
后期看能不能在图片之间加入时间戳,最后生成可全段查询回放的视频。
现在想想,完全可以抛开什么stmp协议,自己完全可以定义传输协议,比如规定传送几帧图片后就传送时间标志,或者隔几帧图片就传送一段声音数据,最后再根据时间合成图片流,声音流。甚至图片帧之间还可传输控制命令。现在知识储备不够,慢慢理解,学习,最后实现。
import org.bytedeco.javacv.*;
import org.bytedeco.javacv.Frame;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.Socket;
public class Khd1 {
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket=new Socket("127.0.0.1",3000);
OutputStream outputStream=socket.getOutputStream();
ObjectOutputStream outputStream2=new ObjectOutputStream(outputStream);
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("/dev/video0"); //读摄像头
grabber.setImageWidth(720);
grabber.setImageHeight(540); //这两句严重影响带宽。
grabber.start();
Java2DFrameConverter java2DFrameConverter = new Java2DFrameConverter();
while(true) {
Frame frame = grabber.grabFrame();
BufferedImage bufferedImage = java2DFrameConverter.getBufferedImage(frame);
ByteArrayOutputStream outputStream1=new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "jpg", outputStream1); //支持bmp,png,jpg,前两种没压缩
byte[] b=outputStream1.toByteArray();
outputStream2.writeObject(b);
outputStream2.flush();
outputStream1=null;
Thread.sleep(1000/20); //图片帧率,这是大概数字,因为压缩还要占用时间
}
}
}
import org.bytedeco.javacv.CanvasFrame;
import javax.imageio.ImageIO;
import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket serverSocket = new ServerSocket(3000);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
ObjectInputStream inputStream1=new ObjectInputStream(inputStream);
CanvasFrame canvas = new CanvasFrame("播放");//新建一个预览窗口
while(true) {
byte[] b= (byte[]) inputStream1.readObject();
ByteArrayInputStream inputStream2=new ByteArrayInputStream(b);
Image image = ImageIO.read(inputStream2);
canvas.showImage(image);
}
}
}