介绍

javacv目前不能像ffmpeg那样 直接加载字体文件到视频 参考这里

所以实现流程为:提取帧 -> 转图片 -> 编辑图片增加文字 -> 转回帧 -> 输出视频

上代码

/**
* 添加字幕
*
* @param videoFile 原始视频文件
* @param outputFile 输出视频文件
* @param subtitleFrames 字幕帧数组,帧是要有序的 根据时间由小到大;
*/
public static void addSubtitle(File videoFile, File outputFile, List<SubtitleFrame> subtitleFrames) {
Font font;
// 加载字体文件,防止中文乱码
try (InputStream fontStream = CvUtil.class.getResourceAsStream("/fonts/msyh.ttc")) {
if (fontStream == null) {
log.error("字体文件加载失败");
throw new BusinessException(ErrorCodeEnum.Unknow);
}
font = Font.createFont(Font.TRUETYPE_FONT, fontStream).deriveFont(Font.BOLD, 24);
} catch (Exception e) {
log.error("字体文件加载失败", e);
throw new BusinessException(e);
} try (FFmpegFrameGrabber grabberVideo = new FFmpegFrameGrabber(videoFile)) {
grabberVideo.start(); try (FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, grabberVideo.getImageWidth(), grabberVideo.getImageHeight())) {
// 视频参数
recorder.setFrameRate(grabberVideo.getFrameRate());
recorder.setVideoCodec(grabberVideo.getVideoCodec());
recorder.setFormat("mp4");
recorder.setVideoBitrate(grabberVideo.getVideoBitrate());
// 音频参数
recorder.setAudioCodec(grabberVideo.getAudioCodec()); // 音频编码
recorder.setAudioBitrate(grabberVideo.getAudioBitrate());
recorder.setSampleRate(grabberVideo.getSampleRate()); // 采样率
recorder.setAudioChannels(grabberVideo.getAudioChannels()); // 必须大于0,如立体声为2
recorder.start(); Frame frame;
Iterator<SubtitleFrame> subtitleFrameIterator = subtitleFrames.iterator();
SubtitleFrame subtitleFrame = subtitleFrameIterator.next();
// 是否使用过当前字幕帧,只有只用过才可以迭代下一个帧; 这个标志可以帮助实现这种 0-2秒 2-4秒 7-10秒 这种时间不连续的字幕
boolean useSubtitleFrame = false; while ((frame = grabberVideo.grabImage()) != null) {
// 转换为秒
long time = frame.timestamp / 1000;
String subtitle = null; if (time >= subtitleFrame.sta && time < subtitleFrame.end) {
subtitle = subtitleFrame.text;
useSubtitleFrame = true;
} else if (useSubtitleFrame && subtitleFrameIterator.hasNext()) {
// 当前的字幕帧使用过 才可以切换下一个
subtitleFrame = subtitleFrameIterator.next();
useSubtitleFrame = false; // 新字幕帧标志为未使用
} if (subtitle != null) {
BufferedImage image = Java2DFrameUtils.toBufferedImage(frame);
Graphics2D g2 = image.createGraphics();
g2.setFont(font);
// 计算字体宽度,方便剧中
FontMetrics fm = g2.getFontMetrics();
int textWidth = fm.stringWidth(subtitleFrame.text);
// 字幕位置
int[] subtitlePosition = calcSubtitlePosition(grabberVideo.getImageWidth(), grabberVideo.getImageHeight(), textWidth);
g2.drawString(subtitle, subtitlePosition[0], subtitlePosition[1]);
g2.dispose();
recorder.record(Java2DFrameUtils.toFrame(image));
} else {
recorder.record(frame);
}
}
} } catch (Exception e) {
throw new BusinessException(e);
}
} /**
* 计算字幕位置
* 字体高度 10% 横向剧中
*
* @return int[]{x,y}
*/
public static int[] calcSubtitlePosition(int videoWidth, int videoHeight, int textWidth) {
int y = videoHeight - videoHeight / 10;
// 24号字体约等于 32px
int x = videoWidth / 2 - textWidth / 2; return new int[]{x, y};
} /**
* 字幕帧
*/
public static class SubtitleFrame { public SubtitleFrame() {
} public SubtitleFrame(long sta, long end, String text) {
this.sta = sta;
this.end = end;
this.text = text;
} // 起始时间 毫秒
public long sta; // 结束时间 毫秒
public long end; // 字幕文本
public String text;
}

javacv添加字幕 剧中显示的更多相关文章

  1. (原)使用ass字幕文件通过ffmpeg给视频添加字幕的一些研究

    使用ass字幕文件通过ffmpeg给视频添加字幕的一些研究 Author:lihaiping1603@aliyun.com Create:2019-09-04 最近对ffmpeg给视频文件添加字幕效果 ...

  2. css调用外部样式和css样式说明剧中显示

    <title>边走边乔</title><link href="css/style.css" rel="stylesheet" ty ...

  3. 添加QScintilla时显示无法解析的外部函数

    转载请注明出处:http://www.cnblogs.com/dachen408/p/7147165.html 问题:添加QScintilla时显示无法解析的外部函数 解决方案:去掉头文件qscisc ...

  4. C++通讯录管理系统(添加联系人,显示联系人,删除联系人,查找联系人,修改联系人,清空联系人,退出通讯录)

    1 /** 2 * ProjectNmae:通讯录管理系统 3 * 功能: 4 * 添加联系人:向通讯录添加新人 5 * 显示联系人:显示通讯录中的所有联系人信息 6 * 删除联系人:按照姓名进行删除 ...

  5. mac 终端中添加tree命令显示文件目录结构

      在Ubuntu下,通过 sudo apt-get install tree 可以使用tree命令,显示文件目录列表,如图所示: 在mac OS X系统下怎么使用呢? 在终端输入: cd $home ...

  6. 根据select不同的选项实现相应input框添加项的显示

    实现效果: @1.单击包时,显示包时的添加项 @2.单击包里程,显示包里程的添加项 二  代码实现: 给select添加change事件 获取当前select的value 根据value判断对象显示其 ...

  7. Jmeter+Jenkins的聚合报告中添加QPS栏目显示

    1.进入jmeter/extras目录,修改 jmeter-results-detail-report_21.xsl   2.打开文件修改 如上所示,在文件中添加6个地方关于QPS的显示即可, 然后替 ...

  8. video字幕无法显示,video视频在google中无法控制快进

    video字幕(track)无法显示: 直接用关闭同源策略的浏览器打开你的HTML文件可以请求到字幕文件并显示字幕: 从hbuilder中打开html文件,在从里面打开google浏览器去浏览HTML ...

  9. Rancher 添加主机无法显示、添加主机无效的解决办法

    在 Rancher UI 中,添加主机,在 Shell ssh 运行了,然后 点击 “关闭” 按钮,发现没有显示如何主机. 第一步,先去查看应用是否正常,就是 应用 - 全部应用 如果显示是 unhe ...

  10. VC++组合框——学习笔记1(组合框选项的添加和无法显示下拉选项)

    VC++控件 ---组合框  环境VC2003 1.组合框添加下拉菜单选项  现在有尝试了两个命令 (m_com为组合框控control类型的变量.) 方法一  m_com.AddString(&qu ...

随机推荐

  1. Ubuntu 下查看当前用户

    博客地址:https://www.cnblogs.com/zylyehuo/ 在终端执行以下命令 whoami

  2. 使用LLaMA-Factory训练LLM大模型并用ollama调用

    环境搭建 系统环境 需要Nvidia显卡,至少8G显存,且专用显存与共享显存之和大于20G 建议将非安装版的环境文件都放到非系统盘,方便重装或移植 以Windows11为例,非安装环境文件都放在 E ...

  3. Redis 通用命令

    KEYS 语法: KEYS pattern 功能: 返回所有匹配 pattern 的键 可以使用该命令的Redis版本: 1.0.0 时间复杂度: O(N) N指的是在数据库中的键的数量 不建议在生成 ...

  4. 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密

    引言 最近遇到了一个 ActiveMQ 消费端的问题:在没有消息时,日志频繁打印,每秒打印2000多条空消息,导致日志文件迅速膨胀,甚至影响系统性能.经过一番排查,最终定位到问题根源并成功解决.本文将 ...

  5. VsCode写Markdown使用snippet

      文件->首选项->用户片段   输入markdown   输入代码片段 Ctrl+P,输入settings.json   加入下面个这个选项 "[markdown]" ...

  6. 【Guava】IO工具

    引言 Guava 使用术语 流来表示可关闭的,并且在底层资源中有位置状态的 I/O 数据流.字节流对应的工具类为 ByteSterams,字符流对应的工具类为 CharStreams. Guava 中 ...

  7. RL · Exploration | 使用时序距离构造 intrinsic reward,鼓励 agent 探索

    论文标题:Episodic Novelty Through Temporal Distance. ICLR 2025,8 8 6 5 poster. arxiv:https://arxiv.org/a ...

  8. python之request请求后响应的数据从中获取指定值

    request请求后响应的数据为字典类型,从中获取指定值 如上图,需要获取Code的值,或者Msg的值 首先把response通过内置json解码器解码输出 response =  response. ...

  9. Java日期格式化中的“YYYY”陷阱:为什么跨年周会让你的年份突然+1?.md

    结论先行 在Java中使用 YYYY-MM-dd 格式化日期时,若日期所在的周跨年,年份可能会被错误计算为下一年(如2021年12月26日显示为2022年).而使用 yyyy-MM-dd 会始终返回正 ...

  10. 解决NET Core发布iis项目覆盖原有的项目时"另一个程序正在使用此文件,进程无法访问"

    解决NET Core发布iis项目覆盖原有的项目时"另一个程序正在使用此文件,进程无法访问" 现在net core运用的多了,一系列的问题接踵而来,更新项目发布到iis时就有一个坑 ...