版权声明:eguid新博客地址:https://blog.eguid.cc;温馨提示:本博客所有原创文章均采用知识共享署名-相同方式共享 3.0 中国大陆许可协议进行许可。如有转载请注明出处和作者名! https://blog.csdn.net/eguid_1/article/details/51725970
1、本地:采用javaCV(安卓和java平台推荐javaCV)、ffmpeg、openCV或者jmf可以很方便的获取到本地摄像头流媒体
javaCV系列文章:
javacv开发详解之1:调用本机摄像头视频
javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG、javaCV-openCV)
javaCV开发详解之3:收流器实现,录制流媒体服务器的rtsp/rtmp视频文件(基于javaCV-FFMPEG)
javaCV开发详解之4:转流器实现(也可作为本地收流器、推流器,新增添加图片及文字水印,视频图像帧保存),实现rtsp/rtmp/本地文件转发到rtmp流媒体服务器(基于javaCV-FFMPEG)
javaCV开发详解之5:录制音频(录制麦克风)到本地文件/流媒体服务器(基于javax.sound、javaCV-FFMPEG)
javaCV开发详解之6:本地音频(话筒设备)和视频(摄像头)抓取、混合并推送(录制)到服务器(本地)
javaCV开发详解之7:让音频转换更加简单,实现通用音频编码格式转换、重采样等音频参数的转换功能(以pcm16le编码的wav转mp3为例)
补充篇:
音视频编解码问题:javaCV如何快速进行音频预处理和解复用编解码(基于javaCV-FFMPEG)
音视频编解码问题:16/24/32位位音频byte[]转换为小端序short[],int[],以byte[]转short[]为例
实现给图片增加图片水印或者文字水印(也支持视频图像帧添加水印)
2、监控(第三方摄像头):通过设备sdk或者rtsp直播流获取流媒体源
直播:通过ffmpeg(推荐),live555将接收rtsp或者字节码流并转为flv格式发布到rtmp流媒体服务器(流媒体服务器必须先建好)
hls原理同上
注意:rtmp只支持flv格式封装的视频流
ffmpeg服务实现方式实例请参考:
http://blog.csdn.net/eguid_1/article/details/51777716
http://blog.csdn.net/eguid_1/article/details/51787646
也可以参考javaCV的转流器实现:javaCV开发详解之4:转流器实现,实现rtsp/rtmp/本地文件转发到rtmp服务器
java封装FFmpeg命令,支持原生ffmpeg全部命令,实现FFmpeg多进程处理与多线程输出控制(开启、关闭、查询),rtsp/rtmp推流、拉流
目前主流的流媒体服务器有:fms,nginx-rtmp,red5(java),flazr
本地视频:直接通过流媒体服务器解码并推送视频流
直播流:通过开启udp/rtp/rtsp/rtmp/hls等等流媒体服务,从ffmpeg/live555获取推送过来的实时视频流并发布到rtmp/hls直播流并推送(可以边直播边保存)
rtmp和hls这两种是web领域主流的流媒体协议。使用rtp或rtsp协议的一般都是监控。
流媒体协议选择:rtmp基于tcp协议,rtmp能够保持3秒左右延迟。hls是基于http协议,所以实时性特别差,想要用hls保持实时性的就别想了,hls延迟基本超过10秒。
实时性要求特高的,建议使用基于udp协议的一些流媒体协议。
基于tcp和udp两种流媒体协议区别就是tcp会强制同步,udp是数据发出去就不管了。
所以最终的方案就是:强同步但是实时性要求不高用基于tcp协议的,强实时性弱同步就udp。
补充:nginx-rtmp流媒体服务器搭建实例:http://blog.csdn.net/eguid_1/article/details/51749830
直播:通过flex(flash)播放器或者第三方播放器(videoJS,ckplayer,VideoLAN 等...)调用流媒体服务器的流媒体源解码并播放,如果不需要兼容低版本IE,可以采用HTML5的webSocket播放器,videoJS是flash/html5双核播放器。
视频:通过html自带播放器、flex(flash)播放器或者第三方播放器(videoJS,ckplayer,VideoLAN 等...)进行播放
videoJS/ckplayer播放器二次开发支持rtmp直播、hls直播及普通视频播放:http://blog.csdn.net/eguid_1/article/details/51898912
一般使用videoLAN播放器作为测试工具,用于测试音视频流发布状况
补充:
1、如果是采用nginx服务器,它提供的rtmp模块可以发布rtmp直播、录播及hls,nginx可以把ffmpeg整合进去方流媒体后期处理(加水印等)。
2、java是可以调用ffmpeg的,通过jni的方式有两种方法:
2.1、javaCV1.2支持通过javacpp调用ffmpeg,javaCV目前整合了8种流媒体处理框架,是安卓和javaEE平台不可或缺的强大流媒体处理利器
2.2、javaAV(目前最新0.7,release最新0.5)提供了对java调用ffmpeg的支持,当前已停止更新
补充:为什么没有基于原生java(或者说自带GC的语言)的流媒体框架,原因来自GC,也就是java引以为豪的自动垃圾回收机制(真的是成也萧何,败也萧何)
为什么呢?
大家知道,直播(顾名思义,实时视频转发),这种实时性项目会产生大量的对象,这样会导致两种情况:
1、产生大量对象后占据的内存资源得不到及时释放,于是虚拟机内存溢出。
2、产生大量对象导致GC满负荷运行进行资源回收,会严重占用系统资源,导致系统运行迟滞,影响系统运行性能和实时性等等。
遇到的问题:
1.一定要先了解清楚整体流程:
资料参考:谢谢各位大神的帮助
实时监控、直播流、流媒体、视频网站开发方案设计简要:简要:https://blog.csdn.net/eguid_1/article/details/51725970
IntelliJ IDEA-nginx里配置https访问:访问:https://blog.csdn.net/qq_33101675/article/details/80308679
Nginx Linux详细安装部署教程:https://www.cnblogs.com/taiyonghai/p/6728707.html
javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG、javaCV-openCV):V):https://blog.csdn.net/eguid_1/article/details/52678775
还需要安插件:OpenCV学习笔记(一)安装及运行第一个OpenCV程序:http://www.cnblogs.com/teafree/p/4095887.html
OpenCV教程:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/tutorials.html
OpenCV教程:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/tutorials.html
opencv:http://groups.google.com /组/ javacv
搭建流流媒体服务器:https://blog.csdn.net/eguid_1/article/details/51725970
IntelliJ IDEA-nginx里配置https访问:https://blog.csdn.net/qq_33101675/article/details/80308679
IntelliJ IDEA-nginx里配置https访问:https://blog.csdn.net/qq_33101675/article/details/80308679
1.nginx需要加nginx-rtmp模块;
2.nginx的路径一定一定一定不要用中文。
3.附带查询端口被占用的命令
下载vlc media player
vue 播放rtmp格式的视频
:https://blog.csdn.net/qq_42842709/article/details/81772259
package test; import org.bytedeco.javacpp.Loader; import org.bytedeco.javacpp.avcodec; import org.bytedeco.javacpp.opencv_core; import org.bytedeco.javacpp.opencv_objdetect; import org.bytedeco.javacv.*; /** * 转流器 */ import javax.swing.*; public class recordPush { public static void main(String[] args) throws Exception { // String inputFile = "rtsp://admin:[email protected]:37779/cam/realmonitor?channel=1&subtype=0"; //香港卫视:rtmp://live.hkstv.hk.lxdns.com/live/hks String inputFile = "rtsp://admin:[email protected]:554/Streaming/Channels/102"; //String outputFile = "rtmp://192.168.1.71:1935/hls/mystream";//叶刚电脑的推流地址 String outputFile = "rtmp://192.168.1.81:1935/live/mystream";//本地配置的推流地址 recordPush(inputFile, outputFile, 25); } /** * 转流器 * @param inputFile * @param outputFile * @throws Exception * @throws org.bytedeco.javacv.FrameRecorder.Exception * @throws InterruptedException */ public static void recordPush(String inputFile,String outputFile,int v_rs) throws Exception, org.bytedeco.javacv.FrameRecorder.Exception, InterruptedException{ Loader.load(opencv_objdetect.class); long startTime=0; FrameGrabber grabber =FFmpegFrameGrabber.createDefault(inputFile); System.out.println("开始获取FrameGrabber grabber:"+grabber); try { System.out.println("获取 grabber的start:"+grabber); grabber.setImageHeight(480); grabber.setImageWidth(860); grabber.setOption("rtsp_transport", "tcp"); // 使用tcp的方式,不然会丢包很严重 grabber.start(); } catch (Exception e) { try { System.out.println("获取 grabber的restart:"+grabber); grabber.restart(); } catch (Exception e1) { System.out.println("出错了:"+grabber); throw e; } } System.out.println("结束获取FrameGrabber grabber:"+grabber); OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage(); System.out.println("开始获取OpenCVFrameConverter.ToIplImage:"+converter); Frame grabframe =grabber.grab(); opencv_core.IplImage grabbedImage =null; if(grabframe!=null){ System.out.println("取到第一帧"); grabbedImage = converter.convert(grabframe); }else{ System.out.println("没有取到第一帧"); } System.out.println("结束获取converter:"+converter); //如果想要保存图片,可以使用 opencv_imgcodecs.cvSaveImage("hello.jpg", grabbedImage);来保存图片 FrameRecorder recorder; try { recorder = FrameRecorder.createDefault(outputFile, 300, 300); } catch (org.bytedeco.javacv.FrameRecorder.Exception e) { throw e; } recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // avcodec.AV_CODEC_ID_H264 recorder.setFormat("flv"); recorder.setFrameRate(v_rs); recorder.setGopSize(v_rs); System.out.println("准备开始推流..."); try { recorder.start(); } catch (org.bytedeco.javacv.FrameRecorder.Exception e) { try { System.out.println("录制器启动失败,正在重新启动..."); if(recorder!=null) { System.out.println("尝试关闭录制器"); recorder.stop(); System.out.println("尝试重新开启录制器"); recorder.start(); } } catch (org.bytedeco.javacv.FrameRecorder.Exception e1) { throw e; } } System.out.println("开始推流"); //获取每一帧不展示 // CanvasFrame frame = new CanvasFrame("camera", CanvasFrame.getDefaultGamma() / grabber.getGamma()); // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //frame.setAlwaysOnTop(true); while ((grabframe=grabber.grab()) != null) { System.out.println("推流..."); // frame.showImage(grabframe); grabbedImage = converter.convert(grabframe); Frame rotatedFrame = converter.convert(grabbedImage); if (startTime == 0) { startTime = System.currentTimeMillis(); } recorder.setTimestamp(1000 * (System.currentTimeMillis() - startTime));//时间戳 if(rotatedFrame!=null && rotatedFrame.imageHeight > 0 && rotatedFrame.imageWidth > 0){ recorder.record(rotatedFrame); } //Thread.sleep(40); } // frame.dispose(); recorder.stop(); recorder.release(); grabber.stop(); System.exit(2); } }
随笔,时间紧急,还有任务,小小记录下曾经踩过的坑。
一定要先配置好rtmp的流媒体服务器。
#user nobody;
# multiple workers works !
worker_processes 2;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 8192;
# max value 32768, nginx recycling connections+registry optimization =
# this.value * 20 = max concurrent connections currently tested with one worker
# C1000K should be possible depending there is enough ram/cpu power
# multi_accept on;
}
#配置流rtmp
rtmp{
server{
listen 1935;
application live{
live on;
record off;
}
application hls{
live on;
hls on;
hls_path D:\ruanjian\nginx-rmpt\html\hls;
hls_cleanup off;
}
}
}
http {
#include /nginx/conf/naxsi_core.rules;
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr:$remote_port - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
# # loadbalancing PHP
# upstream myLoadBalancer {
# server 127.0.0.1:9001 weight=1 fail_timeout=5;
# server 127.0.0.1:9002 weight=1 fail_timeout=5;
# server 127.0.0.1:9003 weight=1 fail_timeout=5;
# server 127.0.0.1:9004 weight=1 fail_timeout=5;
# server 127.0.0.1:9005 weight=1 fail_timeout=5;
# server 127.0.0.1:9006 weight=1 fail_timeout=5;
# server 127.0.0.1:9007 weight=1 fail_timeout=5;
# server 127.0.0.1:9008 weight=1 fail_timeout=5;
# server 127.0.0.1:9009 weight=1 fail_timeout=5;
# server 127.0.0.1:9010 weight=1 fail_timeout=5;
# least_conn;
# }
sendfile off;
#tcp_nopush on;
server_names_hash_bucket_size 128;
## Start: Timeouts ##
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 30;
send_timeout 10;
keepalive_requests 10;
## End: Timeouts ##
#gzip on;
server {
listen 8077;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
## Caching Static Files, put before first location
#location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
# expires 14d;
# add_header Vary Accept-Encoding;
#}
# For Naxsi remove the single # line for learn mode, or the ## lines for full WAF mode
location / {
#include /nginx/conf/mysite.rules; # see also http block naxsi include line
##SecRulesEnabled;
##DeniedUrl "/RequestDenied";
##CheckRule "$SQL >= 8" BLOCK;
##CheckRule "$RFI >= 8" BLOCK;
##CheckRule "$TRAVERSAL >= 4" BLOCK;
##CheckRule "$XSS >= 8" BLOCK;
root html;
index index.html index.htm;
}
# For Naxsi remove the ## lines for full WAF mode, redirect location block used by naxsi
##location /RequestDenied {
## return 412;
##}
## Lua examples !
# location /robots.txt {
# rewrite_by_lua '
# if ngx.var.http_host ~= "localhost" then
# return ngx.exec("/robots_disallow.txt");
# end
# ';
# }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000; # single backend process
# fastcgi_pass myLoadBalancer; # or multiple, see example above
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl spdy;
# server_name localhost;
# ssl on;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_timeout 5m;
# ssl_prefer_server_ciphers On;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:ECDH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!eNULL:!MD5:!DSS:!EXP:!ADH:!LOW:!MEDIUM;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}