版權聲明:eguid新博客地址:https://blog.eguid.cc;溫馨提示:本博客所有原創文章均採用知識共享署名-相同方式共享 3.0 中國大陸許可協議進行許可。如有轉載請註明出處和作者名! https://blog.csdn.net/eguid_1/article/details/51725970
- 歡迎大家積極開心的加入討論羣
羣號:371249677 (點擊這裏進羣)
一、本地推送端
1、本地:採用javaCV(安卓和java平臺推薦javaCV)、ffmpeg、openCV或者jmf可以很方便的獲取到本地攝像頭流媒體
javaCV系列文章:
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
nginx-rtmp配置指令詳細含義和用法:http://blog.csdn.net/eguid_1/article/details/51821297
四、播放端(收流端)
直播:通過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;
# }
#}
}