javaCV推rtsp到rtmp资料整理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_36810906/article/details/82050944

版权声明: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;
    #    }
    #}

}
 

猜你喜欢

转载自blog.csdn.net/weixin_36810906/article/details/82050944
今日推荐