首页 新闻 会员 周边

海康设备回放,从回调函数取流,JavaCV推流,播放时间只有5s

0
悬赏园豆:100 [已解决问题] 解决于 2024-07-17 13:10
package com.ruoyi.hcnetsdk.utils;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;

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

@Slf4j
public class VideoTransformer {

    public PipedOutputStream writer;
    public PipedInputStream reader;
    public FFmpegFrameGrabber grabber;
    public FFmpegFrameRecorder recorder;
    public volatile boolean running = false;

    public VideoTransformer() {


        writer = new PipedOutputStream();

        reader = new PipedInputStream();

        try {
            writer.connect(reader);
        } catch (IOException e) {
            log.error("writer connect pipe error", e);
        }
        grabber = new FFmpegFrameGrabber(reader);
    }

    public void start(int hash) {
        try {
            grabber.start();
            recorder = new FFmpegFrameRecorder("rtmp://localhost:1935/hash/" + hash, grabber.getImageWidth(),
                    grabber.getImageHeight(), grabber.getAudioChannels());
            recorder.setFormat("flv");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
            recorder.start();
            running = true;

            // 开启线程处理回放回调函数的视频流
            new Thread(this::processStream).start();
        } catch (FFmpegFrameGrabber.Exception e) {
            log.error("start grabber error", e);
        } catch (FFmpegFrameRecorder.Exception e) {
            log.error("start recorder error", e);
        }
    }

    private void processStream() {
        try {
            Frame frame;
            while (running && ((frame = grabber.grab()) != null)) {
                recorder.record(frame);
                Thread.sleep(40);
            }
        } catch (IOException e) {
            log.error("record frame error", e);
        } catch (InterruptedException e) {
            log.error("Thread sleep error", e);
            Thread.currentThread().interrupt();
        } finally {
            this.cleanup();
        }
    }


    /**
     * stop the transformer
     */
    public void stop() {
        running = false;
        try {
            if (writer != null) writer.close();
        } catch (IOException e) {
            log.error("writer close error", e);
        }
    }

    /**
     * write bytes to the writer
     *
     * @param bytes the bytes to write
     */
    public void write(byte[] bytes) {
        try {
            writer.write(bytes);
        } catch (IOException e) {
            log.error("writer write error", e);
        }
    }

    /**
     * release all resources, except for the writer
     */
    private void cleanup() {
        try {
            if (recorder != null) {
                recorder.stop();
                recorder.release();
            }
        } catch (FFmpegFrameRecorder.Exception e) {
            log.error("recorder release error", e);
        }

        try {
            if (grabber != null) {
                grabber.stop();
                grabber.close();
                grabber.release();
            }
        } catch (FFmpegFrameGrabber.Exception e) {
            log.error("grabber release error", e);
        } catch (FrameGrabber.Exception e) {
            log.error(grabber.getClass().getSimpleName() + " release error", e);
        }
        
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            log.error("reader close error", e);
        }
    }
}

异常信息:



剩下的都一样了

我想知道这个问题怎么解决,只要输入流Reset,就剩下全是异常了。

我想通过把bytes数据写入队列,然后再写入OutputStream,来平衡InputStream和OutputStream速度,但是grabber.start()会从InputStream取流失败。

echo_lovely的主页 echo_lovely | 小虾三级 | 园豆:1436
提问于:2024-07-16 14:13
< >
分享
最佳答案
0

我从ai搜到的用javacv 获取视频流怎么只有短短一段函数啊

public static void main(String[] args) throws Exception {
        // 视频流地址
        String streamURL = "your_video_stream_url_here";

        // 创建 FrameGrabber 对象
        FrameGrabber grabber = FrameGrabber.createDefault(streamURL);
        grabber.start();

        // 创建一个窗口来显示视频帧
        CanvasFrame canvasFrame = new CanvasFrame("Video Stream");

        // 读取和显示视频帧
        Frame frame;
        while ((frame = grabber.grab()) != null) {
            // 在窗口中显示当前帧
            canvasFrame.showImage(frame);

            // 等待 30 毫秒,如果用户关闭窗口则退出循环
            if (canvasFrame.isResizable()) {
                canvasFrame.waitKey(30);
            } else {
                break;
            }
        }

        // 释放 FrameGrabber 和关闭窗口
        canvasFrame.dispose();
        grabber.stop();
    }
收获园豆:100
鼻嘎塞你嘴里 | 小虾三级 |园豆:573 | 2024-07-16 14:47

这个没有推流,

                    grabber.getImageHeight(), grabber.getAudioChannels());```是推流成rtmp浏览器播放的
echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 14:50

@echo_lovely: 推流的搜索变成这样了

  public static void main(String[] args) throws FrameGrabber.Exception, Exception {
        // 捕获摄像头视频
        OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);
        grabber.start();

        // 推流到 RTMP 服务器
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("rtmp://your_rtmp_server_url/live/stream_name", 640, 480);
        recorder.setFormat("flv");
        recorder.start();

        // 循环捕获视频帧并推流
        Frame frame;
        while ((frame = grabber.grab()) != null) {
            recorder.record(frame);
        }

        // 停止捕获和推流
        recorder.stop();
        grabber.stop();
    }
鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 16:07

@九亿BUG的噩梦: 不好使

echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 16:12

@echo_lovely: 解码器设置H264 ,不能拿过来直接用, 调整调整

 recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 设置视频编解码器为 H264
鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 16:15

@九亿BUG的噩梦: 写了,是264的

echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 16:17

@echo_lovely: 那就爱莫能助了,搜索、复制、粘贴三板斧用完咧!!!

鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 16:19

@九亿BUG的噩梦: 锤你!其实我感觉拉流和推流没有问题,问题在于数据从回调到拉流这里或者是回调数据够不够,我大概计算了下,回调数据基本上是够的,十几秒钟和真实的是差不多的,所以问题就是回调给的可能太快,需要控制下流速到拉流这里

echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 16:27

@echo_lovely: 我理解的是在循环里一直读取流里有没有内容,如果过了很久还读不到内容就关闭流,应该和流的速度关系不大,没实操过

鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 16:38

@九亿BUG的噩梦: 问题是,回调数据如果一边写,一边读只有5s就中断了,但是搞个队列,一边写入回调到队列,另一头写入输出流,他能写到回调数据结束。,但是grabber不会读

echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 16:40

@echo_lovely: 可能流不等于null就退出读取流那里得再加个逻辑, while循环里加一个等待时间,如果流为空,开始进入等待,等待时间内再各一定时间尝试读取流,超过等待时间,结束读取

鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 16:45

@九亿BUG的噩梦: 呜呜呜,似乎可以了,但是不知道为啥可以了,emmm,也不确定是否是真的可以了

echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 17:04

@echo_lovely: 什么都没改就可以了嘛 0.0

鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 17:16

@九亿BUG的噩梦: 现在整理后的代码

package com.ruoyi.hcnetsdk.utils;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;

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

@Slf4j
public class VideoTransformer {
    public PipedOutputStream writer;
    public PipedInputStream reader;
    public FFmpegFrameGrabber grabber;
    public FFmpegFrameRecorder recorder;
    public volatile boolean running = false;
    
    public VideoTransformer() {
        try {
            writer = new PipedOutputStream();
            reader = new PipedInputStream(writer);
            grabber = new FFmpegFrameGrabber(reader);
            grabber.setOption("analyzeduration", "15000000"); // 15s
            grabber.setOption("probesize", "15000000"); // 150 MB
        } catch (IOException e) {
            log.error("VideoTransformer init error:\n" + e);
        }
        FFmpegLogCallback.set();
    }

    public void start(int hash) {
        try {
            running = true;
            grabber.start();
            recorder = new FFmpegFrameRecorder("rtmp://localhost:1935/hash/" + hash, grabber.getImageWidth(),
                    grabber.getImageHeight(), grabber.getAudioChannels());
            recorder.setFormat("flv");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
            recorder.start();

            // 开启线程处理回放回调函数的视频流
            new Thread(this::processStream).start();
        } catch (FFmpegFrameGrabber.Exception e) {
            log.error("start grabber error:\t" + e.getMessage());
        } catch (FFmpegFrameRecorder.Exception e) {
            log.error("start recorder error:\t" + e.getMessage());
        }
    }

    private void processStream() {
        while (running) {
            try {
                Frame frame;
                if ((frame = grabber.grab()) != null) {
                    recorder.record(frame);
                } else {
                    Thread.sleep(40);
                }
            } catch (InterruptedException e) {
                log.error("processStream -> Thread.sleep error:\t" + e.getMessage());
            } catch (FFmpegFrameGrabber.Exception e) {
                log.error("Grabber error:\t" + e.getMessage());
            } catch (FFmpegFrameRecorder.Exception e) {
                log.error("Recorder error:\t" + e.getMessage());
            }
        }
        this.cleanup();
        log.info("VideoTransformer stopped");
    }


    /**
     * stop the transformer
     */
    public void stop() {
        running = false;
        try {
            if (writer != null) {
                writer.close();
            }
        } catch (IOException e) {
            log.error("停止播放回放视频:\t" + e.getMessage());
        }
    }

    /**
     * write bytes to the writer
     *
     * @param bytes the bytes to write
     */
    public void write(byte[] bytes) {
        try {
            writer.write(bytes);
        } catch (IOException e) {
            log.error("writer write error", e);
        }
        log.info("write bytes to writer:\t" + bytes.length + " 字节");
    }

    /**
     * release all resources, except for the writer
     */
    private void cleanup() {
        try {
            if (recorder != null) {
                recorder.stop();
                recorder.close();
                recorder.release();
            }
        } catch (FFmpegFrameRecorder.Exception e) {
            log.error("recorder release error" + e.getMessage());
        } catch (FrameRecorder.Exception e) {
            log.error("recorder close error" + e.getMessage());
        }

        try {
            if (grabber != null) {
                grabber.stop();
                grabber.close();
                grabber.release();
            }
        } catch (FFmpegFrameGrabber.Exception e) {
            log.error("grabber release error", e);
        } catch (FrameGrabber.Exception e) {
            log.error(grabber.getClass().getSimpleName() + " release error", e);
        }

        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            log.error("reader close error", e);
        }
    }
}

echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 17:30

@九亿BUG的噩梦: 你可以对比下,修改了哪些

echo_lovely | 园豆:1436 (小虾三级) | 2024-07-16 17:30

@echo_lovely: while循环那里更符合逻辑了,我们俩真强!

鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 17:37

@echo_lovely: 别忘了改下源码命名啥的,或者把这个提问删了,一般是不准粘公司源码到网上的

鼻嘎塞你嘴里 | 园豆:573 (小虾三级) | 2024-07-16 17:39
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册