javaCV推rtsp到rtmp資料整理

版權聲明: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開發詳解之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

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;
    #    }
    #}

}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章