java实现一个录像大师

java实现一个录像大师

javacv从入门到入土系列,发现了个好玩的东西,视频处理,于是我想搞了个屏幕录屏大师,这里我使用javafx进行页面显示。
在这里插入图片描述
依赖

        <!-- 需要注意,javacv主要是一组API为主,还需要加入对应的实现 -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.5.6</version>
        </dependency>

        <!-- 用到了 ffmpeg 需要把 ffmpeg 的平台实现依赖引入 -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg-platform</artifactId>
            <version>4.4-1.5.6</version>
        </dependency>

        <!--所有平台实现,依赖非常大,几百MB吧-->
        <!--<dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.6</version>
        </dependency>-->

实现

package top.lingkang.test.gui;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;

import javax.sound.sampled.*;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author lingkang
 * Created by 2022/5/10
 */
public class DesktopRecording extends Application {
    private static final int frameRate = 10;// 录制的帧率
    private static boolean isStop = false;

    private static TargetDataLine line;
    private static File audioFile, videoFile;
    private static ImageView imageVideo;
    private static boolean isStart;

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("lingkang-桌面录屏emm...");
        imageVideo = new ImageView();
        imageVideo.setFitWidth(800);
        imageVideo.setFitHeight(600);
        Button button = new Button("停止录制");
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                isStop = true;
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setTitle("info");
                alert.setHeaderText("已经停止录制");
                alert.setOnCloseRequest(event1 -> alert.hide());
                alert.showAndWait();
            }
        });

        VBox box = new VBox();
        box.getChildren().addAll(button, imageVideo);
        primaryStage.setScene(new Scene(box));
        primaryStage.setHeight(600);
        primaryStage.setWidth(800);
        primaryStage.show();
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                isStop = true;
                System.exit(0);
            }
        });

        // window 建议使用 FFmpegFrameGrabber("desktop") 进行屏幕捕捉
        FrameGrabber grabber = new FFmpegFrameGrabber("desktop");
        grabber.setFormat("gdigrab");
        grabber.setFrameRate(frameRate);
        // 捕获指定区域,不设置则为全屏
        grabber.setImageHeight(600);
        grabber.setImageWidth(800);
        //grabber.setOption("offset_x", "200");
        grabber.setOption("offset_y", "200");//必须设置了大小才能指定区域起点,参数可参考 FFmpeg 入参
        grabber.start();

        videoFile = File.createTempFile("lingkang", ".avi");
        // 用于存储视频 , 先调用stop,在释放,就会在指定位置输出文件,,这里我保存到D盘
        FFmpegFrameRecorder recorder = FFmpegFrameRecorder.createDefault(videoFile, grabber.getImageWidth(), grabber.getImageHeight());
        recorder.setVideoOption("tune", "zerolatency");
        recorder.setFrameRate(frameRate);
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);// 编码,使用编码能让视频占用内存更小,根据实际自行选择
        recorder.start();

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (isStop) {
                    try {
                        // 停止
                        recorder.stop();
                        grabber.stop();
                        // 释放内存,我们都知道c/c++需要手动释放资源
                        recorder.release();
                        grabber.release();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                composeVideo();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
                    timer.cancel();
                    return;
                }
                if (!isStart) {
                    isStart = true;
                    // 录制声音
                    startAudio();
                }
                try {
                    // 获取屏幕捕捉的一帧
                    Frame frame = grabber.grabFrame();
                    // 将这帧放到记录
                    recorder.record(frame);

                    // 观察可以怎么实现的
                    Image convert = new JavaFXFrameConverter().convert(frame);
                    // 更新
                    imageVideo.setImage(convert);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 2000, 1000 / frameRate);
    }

    /**
     * 音视频合并为一个视频
     */
    private void composeVideo() throws Exception {
        // 合成视频
        FFmpegFrameGrabber imageGrabber = new FFmpegFrameGrabber(videoFile);
        imageGrabber.start();
        FFmpegFrameGrabber audioGrabber = new FFmpegFrameGrabber(audioFile);
        audioGrabber.start();
        File file = new File("D://video666666.avi");
        if (file.exists())
            file.delete();
        // 流媒体输出地址,分辨率(长,高),是否录制音频(0:单声道/1:立体声)
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(file, imageGrabber.getImageWidth(),
                imageGrabber.getImageHeight(), 1);
        recorder.setInterleaved(true);
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        recorder.start(imageGrabber.getFormatContext());
        long videoTime = imageGrabber.getLengthInTime();
        AVPacket packet = null;

        for (; (packet = imageGrabber.grabPacket()) != null; ) {
            recorder.recordPacket(packet);
        }

        Frame sampleFrame = null;
        while ((sampleFrame = audioGrabber.grabSamples()) != null) {
            recorder.record(sampleFrame);
            if (audioGrabber.getTimestamp() >= videoTime) {
                break;
            }
        }
        recorder.close();
        audioGrabber.close();
        imageGrabber.close();
        System.out.println("视频合成完毕!");
    }

    private void startAudio() {
        // 录音
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
                    DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
                    // 可以捕捉不同声道
                    line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
                    line.open(audioFormat);
                    line.start();

                    audioFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), ".wav");
                    AudioInputStream ais = new AudioInputStream(line);
                    // start recording
                    AudioSystem.write(ais, AudioFileFormat.Type.WAVE, audioFile);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

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

posted @ 2022-09-16 00:08  凌康  阅读(127)  评论(0编辑  收藏  举报