研究了接近一个星期的流媒体技术,等好不容易有点成就(其实我是打算写个直播系统的= =。阿嘿)的时候又被领导派去学习spark分布式计算框架了…关于流媒体的技术,之前写了一篇使用JavaCV调用本机摄像头的文章,趁着工作之余,也更新下第二篇关于流媒体的博客。本篇文章介绍使用JavaCV和FFmpeg完成视频的推流及拉流。
啥是推流啥是拉流?==。直播听过吗?直播就是一个将推流及拉流运用到极致的一个产品。那么直播是如何表现出推流和拉流功能的?我还是简短的介绍一下吧,详细介绍请自行google。
- 1.推流:将录制视频进行采集、编码、美颜处理后推送给直播服务器进行处理。将视频推送到服务器的这一过程就叫做推流。
- 2.拉流:服务器对传送过来的视频进行处理例如转码、录制、截图、鉴黄后将视频分发给正在观看直播的客户(播放器)。服务器将视频分发给客户端的这一过程就叫做拉流咯。
下面我来谈谈如何实现上述功能,由于博主没有买服务器,所以在下面的代码中我就将服务器的地址写为我本机的文件目录路径咯,你使用的时候只需将我的本地路径修改为你的服务器地址即可。
1.介绍
1.准备jar包:1.javacv.jar。2.javacpp.jar。3.ffmpeg.jar。4.ffmpeg-系统平台.jar。5.opencv.jar。6.opencv-系统平台.jar。
为什么要这些jar包:因为ffmpeg-系统平台.jar中存放的是c/c++本地so/dll库,而ffmpeg.jar就是使用javacpp封装的对应本地库Java接口的实现,而javacpp就是基于jni的一个功能性封装包,方便实现jni,javacv.jar就是对9个视觉库进行了二次封装,但是实现的功能有限,所以建议新手先熟悉openCV和ffmpeg这两个C/C++库的API后再来看javaCV思路就会很清晰了。
2.开发工具:eclipse和IDEA都行啦。
3.功能介绍:
- 1.推流端:实现调用本机摄像头边播放边录制/推流,停止预览(关掉摄像头窗口)即停止录制/推流。
- 2.拉流端:实现将服务器端的视频资源拉到本地。
2.推流端的代码实现
这里我就直接上代码啦,每行代码具体意思见代码旁注释:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69package cn.codingxiaxw.test;
import javax.swing.JFrame;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacpp.opencv_objdetect;
import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.FrameRecorder;
import org.bytedeco.javacv.OpenCVFrameConverter;
public class RecordCameraPullTest2 {
public static void main(String[] args) throws InterruptedException, org.bytedeco.javacv.FrameRecorder.Exception, Exception
{
recordCamera("/Users/codingBoy/Desktop/picture/camera/output.mp4", 25);//这里将地址换成你的远程视频服务器地址例如recordCamera("rtmp://192.168.30.21/live/record1",25); 即可
}
/**
*
* @param outputFile 服务器地址,我这里直接将视频输送到我本机目录
* @param frameRate
* @throws Exception
* @throws InterruptedException
* @throws org.bytedeco.javacv.FrameRecorder.Exception
*/
public static void recordCamera(String outputFile, double frameRate)
throws Exception, InterruptedException, org.bytedeco.javacv.FrameRecorder.Exception {
Loader.load(opencv_objdetect.class);
FrameGrabber grabber = FrameGrabber.createDefault(0);//本机摄像头默认0,这里使用javacv的抓取器,至于使用的是ffmpeg还是opencv,请自行查看源码
grabber.start();//开启抓取器
OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();//转换器
IplImage grabbedImage = converter.convert(grabber.grab());//抓取一帧视频并将其转换为图像,至于用这个图像用来做什么?加水印,人脸识别等等自行添加
int width = grabbedImage.width();
int height = grabbedImage.height();
FrameRecorder recorder = FrameRecorder.createDefault(outputFile, width, height);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // avcodec.AV_CODEC_ID_H264,编码
recorder.setFormat("flv");//封装格式,如果是推送到rtmp就必须是flv封装格式
recorder.setFrameRate(frameRate);
recorder.start();//开启录制器
long startTime=0;
long videoTS=0;
CanvasFrame frame = new CanvasFrame("camera", CanvasFrame.getDefaultGamma() / grabber.getGamma());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setAlwaysOnTop(true);
Frame rotatedFrame=converter.convert(grabbedImage);//不知道为什么这里不做转换就不能推到rtmp
while (frame.isVisible() && (grabbedImage = converter.convert(grabber.grab())) != null) {
rotatedFrame = converter.convert(grabbedImage);
frame.showImage(rotatedFrame);
if (startTime == 0) {
startTime = System.currentTimeMillis();
}
videoTS = 1000 * (System.currentTimeMillis() - startTime);
recorder.setTimestamp(videoTS);
recorder.record(rotatedFrame);
Thread.sleep(40);
}
frame.dispose();
recorder.stop();
recorder.release();
grabber.stop();
}
}
运行程序,会自动启动你本机的摄像头开始录制,同时你会在本机目录(或是远程服务器上)看到一个新生成的.flv(其它格式你只要修改相应视频后缀即可)视频文件,关闭摄像头,便可查看这个.flv视频文件。这样,我们便实现了使用本机摄像头进行录制并将此视频推送到服务器(或者本机)上。接下来看看如何实现拉流功能。
3.拉流端代码实现
同样直接上代码,不懂就使劲代码,一遍不懂看两遍,两遍不懂…去撞墙吧:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51package cn.codingxiaxw.test;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.FrameRecorder;
public class RecordCollect3 {
public static void frameRecord(String inputFile, String outputFile, boolean AUDIO_ENABLED)
throws Exception, org.bytedeco.javacv.FrameRecorder.Exception {
// 是否录制音频
int audioChannel = AUDIO_ENABLED ? 1 : 0;
// 获取视频源
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
// 流媒体输出地址,分辨率(长,高),是否录制音频(0:不录制/1:录制)
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);
// 开始取视频源
recordByFrame(grabber, recorder, AUDIO_ENABLED);
}
private static void recordByFrame(FFmpegFrameGrabber grabber, FFmpegFrameRecorder recorder, Boolean status)
throws Exception, org.bytedeco.javacv.FrameRecorder.Exception {
try { //建议在线程中使用该方法
grabber.start();
recorder.start();
Frame frame = null;
while (status && (frame = grabber.grabFrame()) != null) {
recorder.record(frame);
}
recorder.stop();
grabber.stop();
} finally {
if (grabber != null) {
grabber.stop();
}
}
}
public static void main(String[] args)
throws Exception {
String inputFile = "/Users/codingBoy/Desktop/picture/camera/output.mp4";
// Decodes-encodes
String outputFile = "/Users/codingBoy/Desktop/picture/camera/recorde.mp4";
frameRecord(inputFile, outputFile,true);
}
}
代码中inputFile
传入你的远程服务器地址如rtsp://admin:admin@192.168.2.236:37779/cam/realmonitor?channel=1&subtype=0
即可。outputFile
表示你需要将远程服务器上的视频拉到你本地的哪个文件目录下。这样,我们便完成了拉流端代码的实现。
好了,只要你将上述的代码看懂(放心,我刚开始研究这东西,也是把别人的源码看了几十来遍吧才看懂的.),然后再去查点关于流媒体的资料,简书上有很多人有写实现直播功能的一系列教程,去看看,不久后你也能写出一个像样的直播系统。
关于流媒体这块内容我不打算再更新博客啦,除非以后突然有兴趣继续研究直播功能这块,所以大家要是想继续研究的话,点击参考链接看下这位大神写的关于JavaCV和FFmpeg的详细博客吧。
参考:javacv开发详解系列
最后,祝你好运。
2018.3.19更
欢迎加入我的Java交流1群:659957958。
2018.4.21更:如果群1已满或者无法加入,请加Java学习交流2群:305335626 。
4.联系
If you have some questions after you see this article,you can tell your doubts in the comments area or you can find some info by clicking these links.