bytedeco/javacv

pull after push will show ‘no image’

Open

#2,045 opened on Jun 20, 2023

View on GitHub
 (7 comments) (0 reactions) (0 assignees)Java (1,583 forks)batch import
help wantedquestion

Repository metrics

Stars
 (6,985 stars)
PR merge metrics
 (No merged PRs in 30d)

Description

package com.zjxf.hsdji.utils;

import com.frank.live.FFmpegUtil; import com.zjxf.hsdji.MApplication;

import org.bytedeco.ffmpeg.avutil.AVFrame; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.FFmpegLogCallback; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.FrameRecorder; import org.bytedeco.javacv.OpenCVFrameConverter; import org.bytedeco.opencv.opencv_videoio.VideoWriter; import org.bytedeco.opencv.opencv_core.Size;

import java.io.File; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream;

import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264; import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_MPEG1VIDEO; import static org.bytedeco.ffmpeg.global.avutil.AV_LOG_PRINT_LEVEL; import static org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_BGR24; import static org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_RGB48; import static org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_YUV420P; import static org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_YUV420P10; import static org.bytedeco.ffmpeg.global.avutil.av_log_set_level;

/**

  • 推流到rtsp、rtmp服务器 */ public class Pusher extends Thread {

    private final PipedInputStream pis = new PipedInputStream(); private final PipedOutputStream pos = new PipedOutputStream(); private final MyLog log; private volatile boolean running = true; private boolean flag = false;

    public Pusher() { log = new MyLog(); try { // 使用管道流实现线程间通信 pos.connect(pis); av_log_set_level(AV_LOG_PRINT_LEVEL); FFmpegLogCallback.set(); } catch (IOException e) { log.info("pos connect pis error.{}" + e.getMessage()); } }

    private int num = 0;

    /**

    • 将视频流写入管道 */ public void onMediaStream(byte[] data) { try { if (!flag) { log.info("receive data..."); flag = true; } pos.flush(); pos.write(data); log.info("write"); } catch (Exception e) { log.error("write video data error.{}" + e.getMessage()); running = false; try { pos.close(); } catch (IOException ex) { // log.error("pos close error.{}" + ex.getMessage()); } } }

    /**

    • 转流器, 指定format */ public void recordPushWithFormat() { long startTime = 0; long videoTS;

      try { log.info("grabber start ... {} > {}"); // 从管道流中读取视频流 // maximumSize 设置为0,不设置会阻塞 FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(pis, 0);

       //此配置以减少启动时间!若不设置,启动最起码半分钟;
       //类似一个缓冲区,用来检测流的正确性,值越小,启动越快
      

// grabber.setOption("probesize", "30"); // grabber.setOption("buffer_size", "10"); // grabber.setOption("threads", "1"); grabber.setOption("g", "25"); grabber.setVideoOption("vcodec", "copy"); // grabber.setOption("flags", "ignorecropped"); //// grabber.setOption("flags", "low_delay"); // grabber.setOption("discardcorrupted", "1"); // grabber.setOption("skip_loop_filter", "1"); // grabber.setVideoOption("vcodec", "h264"); //// //硬解码 //// grabber.setVideoOption("hwaccel", "mediacodec"); //// grabber.setVideoOption("allow_hwaccel", "1"); //// grabber.setVideoOption("hwaccel_device", "auto"); ////// //// grabber.setOption("preset", "slow"); // 使用slow编码预设 //// grabber.setOption("tune", "film"); // 使用film的tune //// grabber.setOption("crf", "36"); // 设置视频质量 //// // grabber.setFrameRate(30); // grabber.setVideoBitrate(5 * 1024 * 1024); grabber.setImageWidth(1920); grabber.setImageHeight(1080); // grabber.setPixelFormat(AV_PIX_FMT_BGR24); // grabber.setVideoCodec(AV_CODEC_ID_H264); // grabber.setAudioCodec(avcodec.AV_CODEC_ID_NONE); grabber.setFormat("h264"); // grabber.setAudioChannels(0); //阻塞式,直到通道有数据 grabber.start(); int pixelFormat = grabber.getPixelFormat(); int videoBitrate = grabber.getVideoBitrate(); log.info("grabber start suc. and start recorder ...pixelFormat: " + pixelFormat); log.info("grabber start suc. and start recorder ...videoBitrate: " + videoBitrate); log.info("grabber start suc. and start recorder ...ImageHeight: " + grabber.getImageHeight());

        double frameRate = grabber.getFrameRate();
        log.info("grabber start suc. and start recorder ...frameRate: " + frameRate);
        // 获取推流地址对应的recorder,用于录制视频
        // rtmp对应format是flv
        // rtsp对应format是rtsp
        FrameRecorder rtmpRecorder = getRecorder(frameRate);
        rtmpRecorder.start();
        log.info("VideoCodecName:" + rtmpRecorder.getVideoCodecName());
        Frame grabframe;
        // 创建 VideoWriter 对象,用于将帧写入视频文件
        Size size = new Size(1920, 1080);
        int fourcc = VideoWriter.fourcc((byte) 'M', (byte) 'J', (byte) 'P', (byte) 'G');
        OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
        File file = new File(MApplication.getInstance().getExternalCacheDir(), "output.avi");
        VideoWriter writer = new VideoWriter(file.getAbsolutePath(), fourcc, 30.0, size, true);

        // 假设一秒钟15帧,那么两帧间隔就是(1000/15)毫秒
        int interVal = (int) (1000 / frameRate);

// 发送完一帧后sleep的时间,不能完全等于(1000/frameRate),不然会卡顿, // 要更小一些,这里取八分之一 interVal /= 10;

        boolean isKey = true;
        int frameNum = 0;
        // 从视频流中捕获帧以录制视频
        while (running) {
            log.info("get frame");
            grabframe = grabber.grabImage();
            if (grabframe == null) {
                return;
            }
            int imageWidth = grabframe.imageWidth;
            int imageHeight = grabframe.imageHeight;
            log.info("get imageWidth" + imageWidth + "::imageHeight" + imageHeight);

            log.info("grabframe.image" + (grabframe.image == null) + "frame.samples " + (grabframe.samples == null));
            Object opaque = grabframe.opaque;
            if (opaque instanceof AVFrame) {
                AVFrame avFrame = (AVFrame) opaque;
                log.info("AVFrame format:" + avFrame.format());
            }
            if (startTime == 0) {
                startTime = System.currentTimeMillis();
            }
            try {

// Mat mat = converter.convert(grabframe); // writer.write(mat); } catch (Exception e) { log.error("writer error." + e.getMessage()); }

            videoTS = 1000 * (System.currentTimeMillis() - startTime);

            // 推流到rtmp server
            if (videoTS > rtmpRecorder.getTimestamp()) {
                rtmpRecorder.setTimestamp(videoTS);
            }
            log.info("grabframe is keyFrame " + grabframe.keyFrame);
            rtmpRecorder.record(grabframe);
            if (frameNum >= frameRate) {
                frameNum = 0;
            } else
                frameNum++;
            log.error("is keyFrame " + grabframe.keyFrame);

// Thread.sleep(interVal); } } catch (Exception e) { log.error("record push error." + e.getMessage()); } finally { try { pis.close(); } catch (IOException e) { log.error("pis close error.{}" + e.getMessage()); } }

}

private FrameRecorder getRecorder(double frameRate) {

    FrameRecorder recorder = new FFmpegFrameRecorder("rtmp://192.168.100.115:1935/live/video1", 1920, 1080, 0);

// recorder.setInterleaved(true); recorder.setVideoOption("tune", "zerolatency"); recorder.setVideoOption("preset", "ultrafast"); // recorder.setVideoOption("crf", "28"); // recorder.setImageHeight(1088); // recorder.setImageWidth(1920); recorder.setVideoBitrate(5 * 1024 * 1024);//码率 越大越清晰 recorder.setVideoCodec(AV_CODEC_ID_H264); // recorder.setPixelFormat(AV_PIX_FMT_YUV420P); // recorder.setMaxBFrames(0); recorder.setVideoQuality(30); recorder.setFormat("flv");//flv rtsp recorder.setFrameRate(30);//帧率 30fps 每秒包含的帧数 24-30越大越流畅 recorder.setGopSize((int) (302));//302 每60帧存在一个关键帧 // recorder.setVideoCodecName("libx264"); // recorder.setVideoOption("x264opts", "keyint=25:min-keyint=25"); // recorder.setVideoOption("x264-params", "keyint=25"); recorder.setOption("g", "10"); recorder.setAudioChannels(0); // recorder.setMaxBFrames(10);

// recorder.setOption("probesize", "1000000"); // recorder.setOption("buffer_size", "5000000"); // recorder.setSampleRate(44100); // recorder.setTrellis(0);

    return recorder;
}

public void stopPush() {
    this.running = false;
    this.interrupt();
}

public boolean isRunning() {
    return this.running;
}

@Override
public void run() {
    recordPushWithFormat();
}

}

Contributor guide