openCV和FFmpeg,录制显示器推流,支持窗口切换等
  shQYcBLwpqea 2023年12月06日 19 0

问题1:

FFmpeg采集窗口,放入Frame中缩小显示,会失真,需要等比例压缩每一帧图片。

问题2:

窗口放大缩小时,推的视频流会出现卡半屏现象,需要检测窗口变化,重新定义变化后的新采集器,覆盖变化前旧采集器,进行窗口切换推流。

或不多说,上代码:

/**
 * 添加直播素材--#窗口捕捉
 */
public class WindowSnap {

    private static String pushPath = "";
    private static Boolean ifLive = false;
    private static int frameRate = 25;// 录制的帧率
    public void setOutputPath(Boolean ifLive) {
        this.ifLive = ifLive;
    }
  //  static FFmpegFrameGrabber grabber = null;
  //  static FrameRecorder recorder;
    static JPanel contentPanel;
    static ExecutorTab executorTab;
    static int tabItemIndex=0;
    // 获取屏幕大小
    static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    static int width = screenSize.width;
    static int height = screenSize.height;
    static int i = 1;
    public void setI(int i) {
        this.i = i;
    }

    /**
     * 录制指定窗口到视频窗口展示
     * 动态捕捉指定窗口画面,现在可以动态捕捉指定窗口所在位置画面,相当于动态局部录屏;后期须改进优化
     * @param wText  捕捉窗口名称
     */
    public void doCaptureWindow(String wText, JPanel contentPanel,String pushPath,int tabItemIndex, ExecutorTab executorTab) {
        this.pushPath = pushPath;
        this.tabItemIndex = tabItemIndex;
        this.executorTab = executorTab;
        this.contentPanel = contentPanel;
        CanvasFrame canvasFrame = new CanvasFrame("11111");// javacv提供的图像展现窗口
        //内嵌到 内部窗口
        canvasFrame.setVisible(false);
        Component canvas = canvasFrame.getCanvas();
        contentPanel.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                // 获取窗口的新大小
                Dimension newSize = e.getComponent().getSize();
                //调整录制窗口展示与窗口等比例变化
                int roundWidth = (int) Math.round(newSize.getWidth());
                int roundHeight = (int) Math.round(newSize.getHeight());
                canvasFrame.setCanvasSize(roundWidth, roundHeight); // 设置窗口大小
            }
        });
        contentPanel.setLayout(null);
        contentPanel.add(canvas);

        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                startWindowCapture(canvasFrame,wText);
            }
        });
        executorService.shutdown();

    }

    /**
     * 采集窗口
     * @param canvasFrame
     * @param wText
     */
    static void startWindowCapture(CanvasFrame canvasFrame, String wText) throws Exception {

        ExecutorEntity executorWindow = executorTab.getTabList().get(tabItemIndex).getExecutorWindow();
        executorWindow.setExecutor(Executors.newSingleThreadExecutor());
        executorWindow.getExecutor().execute(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                //创建采集器
                executorWindow.setGrabber(new FFmpegFrameGrabber("title="+wText));
                executorWindow.getGrabber().setFormat("gdigrab");
                executorWindow.getGrabber().setFrameRate(frameRate);// 帧获取间隔
                //开启采集器
                try {
                    executorWindow.getGrabber().start();
                } catch (Exception e) {
                    try {
                        executorWindow.getGrabber().restart();  //一次重启尝试
                    } catch (Exception e2) {
                        throw e;
                    }
                }
                executorWindow.setRecorder(new FFmpegFrameRecorder(pushPath, width, height));  //输出路径,画面高,画面宽
                executorWindow.getRecorder().setVideoCodec(avcodec.AV_CODEC_ID_H264);  //设置编码格式
                executorWindow.getRecorder().setFormat("flv");
                executorWindow.getRecorder().setFrameRate(frameRate);
                Frame frame = null;
                Java2DFrameConverter converter = new Java2DFrameConverter();
                //检测窗口变化
                WinDef.HWND parentWindow = User32.INSTANCE.FindWindow(null, wText);
                WinDef.RECT rect = new WinDef.RECT();
                //设置初始值
                int rectWidth = -1;
                int rectHeight = -1;
                while ((frame = executorWindow.getGrabber().grab()) != null) {
                    User32.INSTANCE.GetWindowRect(parentWindow, rect);
                    //窗口宽
                    int nowRectWidth = rect.right- rect.left;
                    //窗口高
                    int nowRHeight = rect.bottom- rect.top;
                    if (rectWidth != -1){//已经赋值过了
                        if (rectWidth != nowRectWidth || rectHeight != nowRHeight){//窗口改变,新建采集器,关闭老采集器,使用新采集器采集
                            System.out.println("新老采集器开始交替!");
                            FFmpegFrameGrabber grabber2 = new FFmpegFrameGrabber("title="+wText);
                            grabber2.setFormat("gdigrab");
                            grabber2.setFrameRate(frameRate);// 帧获取间隔
                            // 设置比特率
                            grabber2.start();
                            //重新赋值
                            frame = grabber2.grabFrame();
                            executorWindow.getGrabber().close();
                            executorWindow.setGrabber(grabber2);
                            System.out.println("新老采集器交替完成!");
                        }
                    }
                    //重新赋值,宽高
                    rectWidth = nowRectWidth;
                    rectHeight = nowRHeight;
                    BufferedImage bufferedImage = converter.getBufferedImage(frame);
                    //等比例压缩图片
                    BufferedImage newBufferedImage = disposeImage(bufferedImage, (int)canvasFrame.getCanvasSize().getWidth(), (int)canvasFrame.getCanvasSize().getHeight());
                    //预览
                    canvasFrame.showImage(newBufferedImage);
                    if (ifLive) {
                        if(i==1){
                            i--;
                            //开启一次录制器
                            try {
                                executorWindow.getRecorder().start();
                            } catch (java.lang.Exception e) {
                                try {
                                    if (executorWindow.getRecorder() != null) {  //尝试重启录制器
                                        executorWindow.getRecorder().stop();
                                        executorWindow.getRecorder().start();
                                    }
                                } catch (java.lang.Exception e1) {
                                    e.printStackTrace();
                                }
                            }

                        }
                        Frame rotatedFrame = converter.convert(bufferedImage);
                        executorWindow.getRecorder().record(rotatedFrame);
                    }
                    Thread.sleep(1000/frameRate);  //30毫秒/帧
                }
                //关闭采集器
                executorWindow.getGrabber().close();
            }
        });
        //   canvasFrame.dispose();
    }



    public void stopWindow(int tabItemIndex) throws Exception {
        try {
            ExecutorEntity executorWindow = executorTab.getTabList().get(tabItemIndex).getExecutorWindow();
            ifLive = false;
            if (executorWindow.getExecutor() != null){
                executorWindow.getExecutor().shutdown();
            }
            executorWindow.getGrabber().close();
            if(executorWindow.getRecorder() != null){
                executorWindow.getRecorder().close();
            }
        } catch (FrameGrabber.Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *BufferedImage 转换 IplImage
     * @param bufferedImage
     * @return
     */
    public static IplImage bufferedImageToIplImage(BufferedImage bufferedImage) {
        Java2DFrameConverter converter = new Java2DFrameConverter();
        Frame frame = converter.convert(bufferedImage);
        OpenCVFrameConverter.ToIplImage iplConverter = new OpenCVFrameConverter.ToIplImage();
        return iplConverter.convert(frame);
    }

    /**
     *
     * @param image
     * @return
     */
    private static Frame convertBufferedImageToFrame(BufferedImage image) {
        Java2DFrameConverter converter = new Java2DFrameConverter() {
            public Frame convert(BufferedImage image, Frame frame) {
                if (frame == null) {
                    frame = new Frame(image.getWidth(), image.getHeight(), Frame.DEPTH_UBYTE, 3);
                }
                super.convert(image);
                frame.imageDepth = Frame.DEPTH_UBYTE;
                frame.imageChannels = 3;
                frame.imageStride = frame.imageWidth * 3;
                return frame;
            }
        };
        return converter.convert(image);
    }

    private static InputStream bufferedImageToInputStream(BufferedImage image) throws Exception {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        return new ByteArrayInputStream(os.toByteArray());
    }


    public void stopWindowRecord() {
    }

    private static BufferedImage disposeImage(BufferedImage src, int new_w, int new_h) {
        // 得到图片
        int old_w = src.getWidth();
        // 得到源图宽
        int old_h = src.getHeight();
        // 得到源图长
        BufferedImage newImg = null;
        // 判断输入图片的类型
        switch (src.getType()) {
            case 13:
                // png,gifnewImg = new BufferedImage(new_w, new_h,
                // BufferedImage.TYPE_4BYTE_ABGR);
                break;
            default:
                newImg = new BufferedImage(new_w, new_h, BufferedImage.TYPE_INT_RGB);
                break;
        }
        Graphics2D g = newImg.createGraphics();
        // 从原图上取颜色绘制新图
        g.drawImage(src, 0, 0, old_w, old_h, null);
        g.dispose();
        // 根据图片尺寸压缩比得到新图的尺寸
        newImg.getGraphics().drawImage(
                src.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0, 0,
                null);
        // 调用方法输出图片文件
        return newImg;
    }
}
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月06日 0

暂无评论

推荐阅读
shQYcBLwpqea