java录屏直播推流

JAVACV实现直播推流

最小依赖

 
        1.5.5
        windows-x86_64
    


 
        
            org.bytedeco
            javacv
            ${javacv.version}
        
        
            org.bytedeco
            javacpp-platform
            ${javacv.version}
        
        
        
            org.bytedeco
            ffmpeg
            4.3.2-${javacv.version}
            ${system.windowsx64}
        
        
        
            org.bytedeco
            javacv
            ${javacv.version}
        
        
            org.bytedeco
            javacpp-platform
            ${javacv.version}
        
        
        
            org.bytedeco
            ffmpeg
            4.3.2-${javacv.version}
            ${system.windowsx64}
        
        
        
            org.bytedeco
            javacv
            ${javacv.version}
        
        
            org.bytedeco
            javacpp-platform
            ${javacv.version}
        
        
        
            org.bytedeco
            opencv
            4.5.1-${javacv.version}
            ${system.windowsx64}
        
        
            org.bytedeco
            openblas
            0.3.13-${javacv.version}
            ${system.windowsx64}
        
        
            org.bytedeco
            flycapture
            2.13.3.31-${javacv.version}
            ${system.windowsx64}
        

视频采集源可以使用摄像头或者其他,我这里用了桌面录像
也排除了声音的采集

package com.test.utiles;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;

/**
 * @author yudy
 * Created by 2022/9/10
 */
@Slf4j
public class MyLiveTest {

    private static int frameRate = 16;// 录制的帧率
    static Frame imgFrame = null;
    static String url = "";
    static boolean isStop = false;

    public static void start() throws Exception {
        // 帧记录
        // window 建议使用 FFmpegFrameGrabber("desktop") 进行屏幕捕捉
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("desktop");
        grabber.setFormat("gdigrab");
        grabber.setFrameRate(frameRate);// 帧获取间隔

        grabber.setOption("framerate", "25");
        grabber.setOption("draw_mouse", "0");
        // 捕获指定区域,不设置则为全屏
//        grabber.setImageHeight(600);
//        grabber.setImageWidth(800);
        // grabber.setOption("offset_x", "200");
        // grabber.setOption("offset_y", "200");//必须设置了大小才能指定区域起点,参数可参考 FFmpeg 入参
        grabber.start();
        //初始化默认帧,用于锁屏状态下无法或者图形的问题导致异常
//        if (imgFrame == null){
//            InputStream in = this.getClass().getClassLoader().getResourceAsStream("img.jpeg");
//            BufferedImage image = ImageIO.read(in);
//            Java2DFrameConverter java2dConverter = new Java2DFrameConverter();
//            imgFrame = java2dConverter.convert(image);
//        }

        if (StringUtils.isEmpty(url)) {
            url = "rtmp://192.168.0.25:1935/live/livestream/aa";
        }
        // 直播推流
        //推送固定大小分辨率的图像
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(
                url,
                1024, 768, 0);
        //推送实际小打分辨率的图像
//        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(
//                url,
//                grabber.getImageWidth(), grabber.getImageHeight(), 0);
        recorder.setMaxDelay(5000);

        // 用于存储视频 , 调用stop后,需要释放,就会在指定位置输出文件,,这里我保存到D盘
        //FFmpegFrameRecorder recorder = FFmpegFrameRecorder.createDefault(file, grabber.getImageWidth(), grabber.getImageHeight());
        recorder.setInterleaved(true);
        // https://trac.ffmpeg.org/wiki/StreamingGuide
        recorder.setVideoOption("tune", "zerolatency");// 加速  零延迟
        //recorder.setVideoOption("flvflags", "no_duration_filesize");// 加速
        // https://trac.ffmpeg.org/wiki/Encode/H.264
        recorder.setVideoOption("preset", "ultrafast");
        recorder.setFrameRate(frameRate);// 设置帧率,重要!
        // Key frame interval, in our case every 2 seconds -> 30 (fps) * 2 = 60
        recorder.setGopSize(frameRate * 2);
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);// 编码,使用编码能让视频占用内存更小,根据实际自行选择
//        recorder.profile(AVCodecContext.FF_PROFILE_H264_CONSTRAINED_BASELINE);
        // https://trac.ffmpeg.org/wiki/Encode/H.264
        recorder.setVideoOption("crf", "28");
        // 2000 kb/s  720P
        recorder.setVideoBitrate(1000000);
        recorder.setFormat("flv");


        recorder.start();
        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    // 获取屏幕捕捉的一帧
                    // 屏幕录制,由于已经对音频进行了记录,需要对记录时间进行调整即可
                    // 即上面调用了 recorder.recordSamples 需要重新分配时间,否则视频输出时长等于实际 的2倍
//                    while ((frame = grabber.grab()) != null) {
                    boolean isGdi = false;
                    int screenW = grabber.getImageWidth();
                    int screenH = grabber.getImageHeight();
                    while (true) {
                        if (isStop) {
                            try {
                                // 停止
                                recorder.stop();
                                grabber.stop();
                                // 释放内存,我们都知道c/c++需要手动释放资源
                                recorder.release();
                                grabber.release();
                                log.info("停止直播完成");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        }
                        Frame frame = grabber.grabFrame();
                        if (frame == null) {
                            isGdi = true;
                            frame = imgFrame;
                        } else if (isGdi) {
                            //先判断分辨率
                            int screenWt = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
                            int screenHt = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
                            if (screenWt == screenW && screenHt == screenH) {
                                log.info("分辨率没有变化,无需重置");
                                isGdi = false;
                            } else {
                                log.info("分辨率有变化,需要重置,马上停止");
                                isStop = true;
                            }
                        }
                        recorder.record(frame);
                    }
                } catch (Exception e) {
                    isStop = true;
                    e.printStackTrace();
                }
            }

        }).start();
    }

    public static void main(String[] args) throws Exception {
        start();
    }

}

可以用docker起一个srs进行推流播放。

# 先启动
docker run -p 1935:1935 -p 1985:1985 -p 8080:8080 \
    ccr.ccs.tencentyun.com/ossrs/srs:4

java录屏直播推流_第1张图片

再启动推流

效果图:

本示例使用的是SRS,也可以更新为其他的平台服务!启动后SRS控制台上就可以看见当前的直播流了

java录屏直播推流_第2张图片
也可以进行预览,预览效果图图下:

java录屏直播推流_第3张图片

你可能感兴趣的:(java,ffmpeg,开发语言)