java代码实现ffmpeg转直播流

这里继续交代背景啊。
首先我们前端推拉rtmp流,服务端用nginx+rtmp服务器,粗略的实现视频直播已经完成的。然后毕竟最终目标是多人视频,所以这个实测1人直播几个人同时看倒是没问题。甚至两个人推拉实现两个人视频通话也是没问题。
但是!!!!问题来了,超过三个人也就是当四个人一起视频就出问题了,卡不卡的不说,实现的逻辑是每个人推自己的视频流,同时拉另外三个人的视频流。咱也不知道是服务器不行还是咋的,反正每个人拉其余三个人的视频就会出现某个视频是拉不出来的,也就是黑屏状态。
这个和我们老板九个人视频聊天的想法差的有点远,所以只能从头更改策略了啊。然后我们领导发话了,rtsp延迟低,虽然会丢帧但是小问题啊,所以换吧。
于是乎辛辛苦苦查阅rtsp,找了个已有的框架easyDarwin,按照官网的教程下载了个包,直接双击.exe文件启动(我是windows系统),然后服务端就这么搭起来了啊。


easyDarwin可视化控制台

搭起服务端第一件事就是测试,所以我先用ffmpeg随便推了个视频试了下,没问题的啊。


如上推流命令

推流后可视化控制台显示有一个正在推流

这简单的三步操作确定了我平台搭建完毕。然后本来我以为这就是结束了,谁想到意外一个接一个来。
首先我们前端是app,而且不是原生写的,用的uniapp、所以不支持推rtsp流。也就是只能退rtmp流啊。所以第一个问题:这里需要前端推流到rtmp服务器,然后我这边从rtmp服务器上获取这个流再转流成rtsp然后再推到easyDarwin上(我也不知道为什么要这么sb的操作,只能说领导最大,说什么是什么)
然后开始转流吧,我当时做rtmp就涉及到了这个问题。用的javacv的工具包,我记得是可以实现的,于是乎百度。。百度,,,百度!!!
说真的,一点不夸张,百度前十页的差不多都看了,人家都是rtsp转rtmp,像我们这样反过来的一个demo都没有。简直气笑了。
然后也试图用差不多的原理来实现,但是rtmp转rtmp实现了,rtsp转rtmp实现了。rtmp转rtsp就是一直报错。现在回想起来也不确定是FFmpegFrameGrabber不支持还是哪里细节处理有问题,反正就是没实现用工具类直接转流。
emmmmm...一条路不通换下一个,再查查有没有别的办法。
最后发现一个不错的思路,直接用命令行总不会出问题了吧?所以又用了java中process进程类,直接到ffmpeg的控制台去打命令行。下面是实现代码:

public class RTSPTool extends Thread {
    String line = "";
    private String params;
    private Process process;
    // ffmpeg位置,最好写在配置文件中
    String ffmpegPath = "D:/demo/ffmpeg/bin/";


    public RTSPTool(String params) {
        super();
        this.params = params;
    }

    @Override
    public void run() {
        try {
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // cmd命令拼接,注意命令中存在空格
            String command = ffmpegPath; // ffmpeg位置
            command += "ffmpeg"; // ffmpeg开头,-re代表按照帧率发送,在推流时必须有
            command += " -i rtmp://192.168.10.189:1935/live/" + params;
//          command += " -threads 2 -rtsp_transport tcp -vcodec libx264 -max_muxing_queue_size 9999 -s 320x240";
            command += " -threads 2 -vcodec libx264 -max_muxing_queue_size 9999 -s 320x240";
            command += " -f rtsp rtsp://192.168.10.189/" + params; // 指定推送服务器,-f:指定格式
            System.out.println("ffmpeg推流命令:" + command);

            // 运行cmd命令,获取其进程
            process = Runtime.getRuntime().exec(command);
            // 输出ffmpeg推流日志         
            BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));

            while((line = br.readLine()) != null) {
                System.out.println("视频推流信息[" + line + "]");
            }           
            if(process!=null)process.destroy();
            System.err.println("<<<<<<<<<<<<<<<<<<<<销毁" + params + "的转流进程!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

首先一点一点说:
这个command的命令是一点点完善的,获取rtmp流,然后转化成rtsp流,中间threads 2 -vcodec libx264 -max_muxing_queue_size 9999 -s 320x240这一排的参数,都是一点点在百度找到的。如果不加libx264编码,前端拉倒流无法解析。不弱不加容量9999,会转流一会儿自动失败,失败原因管道破裂。如果不加threads 2 会报什么2 faram的错,反正简单的几个命令是一次次尝试,搜索出来的结果。
然后确定命令是对的,于是放心大胆的跑起来了,联调。
结果发现第一次跑就出了问题,没有正常一帧一帧转码,而且只有一个开头就卡住了,天地良心,这两周是我第一次接触视频流之类的,一脸懵逼的去百度,去群里问人,中间多波折不说,最后得出结论,不继续往下走可能是因为视频源(rtmp拉流)有问题,emmmm..再专门测试一下发现确实,大多数这个时候vlc拉流也拉不到。


转流命令不继续

咱也不知道是因为推流需要时间,还是网速问题,反正大多数都说卡在这是视频源的问题,但是视频源有啥问题咱也不知道,所以这也不好解决。
最坑的是这样不属于报错,所以上面代码中的输出控制台打印语句的 while((line = br.readLine()) != null) 是一直阻塞的,因为他还等着控制台往里面写东西呢,所以这个程序也就卡死在这了,所以就没有所以了,程序上说这个线程就此死着了,不可能结束了,系统上说这个子进程也是一直后台就这么着了,最主要的是转流这个行为也就彻彻底底的失败了。(哪怕后来rtmp可以拉倒流了这个进程也不会继续走了)

最特么神奇的一点是这一块的内容百度都几乎没有结果!可能我们这么沙雕处理的没有吧。然后搜索类似的,发散思维,最后又是我们领导拍板:用线程监控(这之前还有个问题,uniapp不支持netty-socketio)!然后我说好,线程监控,怎么判断没正常转流呢?控制台输出语句不刷新了。于是乎又开始写进程。隔五s获取一下line的值。如果五秒不变说明卡死了,那么就销毁之前的进程,重新再开一个(目前能想到的唯一办法)。苦哈哈的把监控线程加好了,如下代码:

public class RTSPTool extends Thread {
    String line = "";
    private String params;
    private Process process;
    // ffmpeg位置,最好写在配置文件中
    String ffmpegPath = "D:/demo/ffmpeg/bin/";


    public RTSPTool(String params) {
        super();
        this.params = params;
    }

    @Override
    public void run() {
        try {
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // cmd命令拼接,注意命令中存在空格
            String command = ffmpegPath; // ffmpeg位置
            command += "ffmpeg"; // ffmpeg开头,-re代表按照帧率发送,在推流时必须有
            command += " -i rtmp://192.168.10.201:1935/live/" + params;
//          command += " -threads 2 -rtsp_transport tcp -vcodec libx264 -max_muxing_queue_size 9999 -s 320x240";
            command += " -threads 2 -vcodec libx264 -max_muxing_queue_size 9999 -s 320x240";
            command += " -f rtsp rtsp://192.168.10.201/" + params; // 指定推送服务器,-f:指定格式
            System.out.println("ffmpeg推流命令:" + command);

            // 运行cmd命令,获取其进程
            process = Runtime.getRuntime().exec(command);
            // 输出ffmpeg推流日志         
            BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //这个控制台没被注销则检查。
                    while(true) {
                        String string = line;
                        try {
                            Thread.sleep(7000);
                        } catch (InterruptedException e) {
                        }
                        //10s一判断
                        if(line.equals(string)){
                            process.destroy();
                            if(!LiveApplication.urls.contains(params)) {
                                System.out.println(params+"已退出登录!转流结束!");
                                break;
                            }
                            System.err.println(params+"转流失败,重新尝试转流!");                          
                            new RTSPTool(params).start();
                            break;
                        }
                    }
                }
            }).start();
            while((line = br.readLine()) != null) {
                System.out.println("视频推流信息[" + line + "]");
            }           
            if(process!=null)process.destroy();
            System.err.println("<<<<<<<<<<<<<<<<<<<<销毁" + params + "的转流进程!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

中间涉及到的业务逻辑很少,因为本来就是demo。反正就是控制台不继续转流则判断是用户退出了还是出错了!如果是用户退出了则结束这个进程。如果是转流失败了则结束这个进程并且重试。
至此,我这里能保证的就是最终能转流。但是中间时间不定。但是我目前能做的也就这样了。
你以为我们这个demo就这么结束了?不不不,下面的问题就是我这边实现了最终转流成功,但是前端是在用户加入房间就开始拉流的,这里有个问题,如果前端开始拉流的时候不知道有没有流,这个时候去拉,如果没流的话,哪怕后来有流了也不会再拉一遍的。其实和vlc一个道理,这里理解不了的建议去实际操作一下啊。
反正目前项目是卡在这里了,说实话我觉得rtsp实现九个人视频聊天不卡也是不太可能啊~反正不管怎么样我暂时就记录到这里了。
这篇笔记其实我吐槽比较多,但是知识点也不少啊,毕竟都是血泪的教训,如果稍微帮到你了记得点个喜欢点个关注,这方面的知识这几天狂补,如果有想交流或者指教的亲可以留言或者私信啊。顺便祝大家工作顺顺利利,生活健健康康!如果事件有后续我会续写的啊~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容