通过Java CV框架的FFmpegFrameGrabber读取本地一个视频流的帧,接着Java2DFrameConverter先把帧转换为图片,由Graphics2D进行文本刷到图片上后,Java2DFrameConverter再把合成的图片转回帧,最后由FFmpegFrameRecorder把帧写入输出视频流。
直接上代码:
package com.lxy.service; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.javacv.*; import org.bytedeco.javacv.Frame; import org.bytedeco.opencv.opencv_core.*; import sun.font.FontDesignMetrics; import java.awt.*; import java.awt.image.BufferedImage; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class LiveMixService { public static void main(String[] args) throws FrameGrabber.Exception, FrameRecorder.Exception { // 构造测试字幕 String[] test = { "世上无难事", "只怕有心人", "只要思想不滑坡", "办法总比困难多", "长江后浪推前浪", "前浪死在沙滩上" }; // 为连续的50帧设置同一个测试字幕 List<String> testStr = new ArrayList<>(); for (int i = 0; i < 300; i++) { testStr.add(test[i / 50]); } // 设置源视频、加字幕后的视频 FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault("E:\\BaiduNetdiskDownload\\testout.mkv"); FFmpegFrameRecorder recorder = FFmpegFrameRecorder.createDefault("E:\\BaiduNetdiskDownload\\outtest.mp4", 1280, 720); System.out.println("准备开始推流..."); grabber.start(); recorder.start(); Java2DFrameConverter converter = new Java2DFrameConverter(); Frame frame = null; int i = 0; while ((frame = grabber.grabImage()) != null) { // 从视频帧中获取图片 BufferedImage bufferedImage = converter.getBufferedImage(frame); bufferedImage = addSubtitle(bufferedImage, testStr.get(i++ % 300)); frame.image = converter.getFrame(bufferedImage).image; recorder.record(frame); } grabber.stop(); recorder.stop(); } /** * 图片添加文本 * @param bufImg * @param subTitleContent * @return */ private static BufferedImage addSubtitle(BufferedImage bufImg, String subTitleContent) { // 添加字幕时的时间 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Font font = new Font("微软雅黑", Font.BOLD, 32); String timeContent = sdf.format(new Date()); FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font); Graphics2D graphics = bufImg.createGraphics(); graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); //设置图片背景 graphics.drawImage(bufImg, 0, 0, bufImg.getWidth(), bufImg.getHeight(), null); //设置左上方时间显示 graphics.setColor(Color.orange); graphics.setFont(font); graphics.drawString(timeContent, 0, metrics.getAscent()); // 计算文字长度,计算居中的x点坐标 int textWidth = metrics.stringWidth(subTitleContent); int widthX = (bufImg.getWidth() - textWidth) / 2; graphics.setColor(Color.red); graphics.setFont(font); graphics.drawString(subTitleContent, widthX, bufImg.getHeight() - 50); graphics.dispose(); return bufImg; } }
看下效果: