前言
突然发现抖音有有一些人发布电视剧的精彩片段,并且还获得了很多的点赞。突然发现,原来这样也可以这么多赞。
发现这种视频可以通过工具直接生成。花不多说,上代码。
一、视频剪辑
1.1 生成标题
通过java生成文字图片,加外描边会更加好看。
示例:
public static void createImage2(String str, Font font, File outFile,
Integer width, Integer height) throws Exception {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
BufferedImageGraphicsConfig config = BufferedImageGraphicsConfig.getConfig(bufferedImage);
bufferedImage =config.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
Graphics g = bufferedImage.getGraphics();
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
TextLayout tl = new TextLayout(str, font, frc);
//字在页面的位置
Shape sha = tl.getOutline(AffineTransform.getTranslateInstance(0,250));
//白边的范围
g2.setStroke(new BasicStroke(8));
g2.setColor(Color.RED);
((Graphics2D) g).draw(sha);
g2.setColor(Color.WHITE);
g2.fill(sha);
// 输出png图片
ImageIO.write(bufferedImage, "png", outFile);
}
public static void main(String[] args) throws Exception {
String name= "hello java";
createImage2(name, new Font("楷体", Font.BOLD, 150), new File(
"E:\\a.png"), 800, 600);
}
输出图片:
2. 文字和背景图叠加
D:\tools\ffmpeg\ffmpeg-N-99816-g3da35b7cc7-win64-gpl-shared-vulkan\bin\ffmpeg.exe
-y
-i E:\video\guanlangaoshou\外部封面3.jpg
-i E:\video\guanlangaoshou\bigFont\1-1.png
-filter_complex overlay=220:400
E:\video\guanlangaoshou\fontSize\1-1.png
通过ffmpeg工具即可很轻松的实现文字和背景图的叠加效果
-y
如果输出文件存在, 则覆盖。-i
后面跟源文件地址 两个源文件进行叠加。-filter_complex overlay
后面跟的数字是叠加时的位置。- 最后是输出文件的位置。
如此可实现:1-1
是java生成的,加一个自己喜欢的背景图。叠加即可。
3、视频切割
将视频按照自己的想法分段。
D:\tools\ffmpeg\ffmpeg-N-99816-g3da35b7cc7-win64-gpl-shared-vulkan\bin\ffmpeg.exe
-y
-ss 00:01:44 -i E:\video\guanlangaoshou\videodeal\13.mp4 -to 00:07:00 E:\video\guanlangaoshou\videosplit\13-1.mp4
-ss
开始时间-i
视频地址-to
截止长度,意为开始时间向后切7分钟。- 最后是输出位置。
4、视频和背景图叠加
D:\tools\ffmpeg\ffmpeg-N-99816-g3da35b7cc7-win64-gpl-shared-vulkan\bin\ffmpeg.exe -y -i E:\video\guanlangaoshou\videoimage\13-1.png -i E:\video\guanlangaoshou\videosplit\13-1.mp4 -filter_complex overlay=-80:500 E:\video\guanlangaoshou\success\13-1.mp4
视频和背景图叠加和图片叠加一样, 只需要自己控制叠加位置即可。
结果演示
最后即可生成这种按照视频分段后可以上传抖音的短视频。
java执行cmd指令
另外,使用ffmpeg工具的话,是通过cmd指令调用的。附加cmd调用工具类,直接复制调用即可。
CmdUtil.java:
public class CmdUtil {
public static String execCmd(String cmd){
BufferedReader bReader=null;
InputStreamReader sReader=null;
try
{
String str = BaseConstant.FFMPEG_PATH+" "+cmd;
System.out.println (str);
Process p = Runtime.getRuntime ().exec (str);//启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
//获取进程的标准输入流
final InputStream is1 = p.getInputStream();
//获取进城的错误流
final InputStream is2 = p.getErrorStream();
new Thread() {
public void run() {
BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
try {
String line1 = null;
while ((line1 = br1.readLine()) != null) {
if (line1 != null){}
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
is1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
try {
String line2 = null ;
while ((line2 = br2.readLine()) != null ) {
if (line2 != null){}
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
is2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
try
{
p.waitFor ();
p.destroy();
}
catch (InterruptedException e)
{
e.printStackTrace ();
}
return null;
}
catch (IOException e)
{
e.printStackTrace ();
}
return null;
}
}
更多ffmepg指令
FFmpegCmd.java:
public class FFmpegCmd {
/**
* 扩大分辨率
* -y 覆盖输入
* -i 视频文件地址
*/
private static final String EXPAND_BOUNDARY=" -y -i %s -vf scale=%s:%s %s";
private static final Integer AUTO=-2;
/**
* 设置封面
*
* -i %s -map 1 -map 0 -c copy -disposition:v:1 attached_pic -y %s
*
* ffmpeg -i 第一集-妖雾重回-1.mp4 -i E:\video\huluwa\cover3.jpg -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic E:\video\huluwa\success
*/
private static final String SETTING_COVER=" -i %s -i %s -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic %s";
private static final String VIDEO_INFO=" -i %s ";
/**
* 封面 font 输入路径
*/
private static final String Image_OverLay=" -y -i %s -i %s -filter_complex overlay=%s:%s %s";
/**
* 图片截取
* 文件源 宽:高:start:end 输出路径
*/
private static final String Image_SPLIT=" -y -i %s -vf crop=%s:%s:%s:%s %s";
/**
* 视频分段
* 开始时间00:00:00 视频路径 截取时长 输出路径
*/
private static final String Video_SPLIT="-y -ss %s -i %s -to %s %s";
/**
* 视频转换
*/
private static final String Video_COVERT=" -i %s -c:v copy -c:a copy %s";
/**
* image to video
*/
private static final String Image_TO_VIdeo=" -f image2 -r 1 -i %s -vcodec h264 %s";
private static final String Video_TS=" -i %s -c copy -bsf:v h264_mp4toannexb -f mpegts %s";
private static final String Concat=" -i \"concat:%s|%s\" -c copy -bsf:a aac_adtstoasc -movflags +faststart %s";
/**
* 修改分辨率
* @param videoPath 文件路径
* @param outPath 输出路径
* @param width 修改宽度
* @param height 修改高度
*/
public static void expandBoundaryMethod(String videoPath,String outPath,Integer width,Integer height){
width = width==null?AUTO:width;
height = height==null?AUTO:height;
String cmd = String.format (EXPAND_BOUNDARY,videoPath,width,height,outPath);
CmdUtil.execCmd (cmd);
}
/**
* 设置封面
* @param videoPath 文件路径
* @param imgPath 图片路径
* @param outPath 输出路径
*/
public static void settingCoverMethod(String videoPath,String imgPath,String outPath){
String cmd = String.format (SETTING_COVER,videoPath,imgPath,outPath);
String s = CmdUtil.execCmd (cmd);
System.out.println (s);
}
public static VideoEntity getVideoInfo(String videoPath){
String cmd = String.format (VIDEO_INFO,videoPath);
String s = CmdUtil.execCmd (cmd);
String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
Pattern pattern = Pattern.compile(regexDuration);
Matcher m = pattern.matcher(s.toString());
VideoEntity videoEntity = new VideoEntity (videoPath);
if (m.find()) {
int time = getTimelen(m.group(1));
System.out.println("视频时长:" + time + "s , 开始时间:" + m.group(2) + ", 比特率:" + m.group(3) + "kb/s");
videoEntity.setDuration (time);
}
return videoEntity;
}
private static int getTimelen(String timelen) {
int min = 0;
String strs[] = timelen.split(":");
if (strs[0].compareTo("0") > 0) {
// 秒
min += Integer.valueOf(strs[0]) * 60 * 60;
}
if (strs[1].compareTo("0") > 0) {
min += Integer.valueOf(strs[1]) * 60;
}
if (strs[2].compareTo("0") > 0) {
min += Math.round(Float.valueOf(strs[2]));
}
return min;
}
public static void imageOverlayMethod(String coverImage,String fontImage,Integer width,Integer height,String output){
String cmd = String.format (Image_OverLay,coverImage,fontImage,width,height,output);
CmdUtil.execCmd (cmd);
}
public static void ImageSplitMethod(String imagePath,Integer width,Integer height,Integer sw,Integer sh,String output ){
String cmd = String.format (Image_SPLIT,imagePath,width,height,sw,sh,output);
CmdUtil.execCmd (cmd);
}
public static void VideoSplitMethod(String videoPath,String start,String length,String output ){
String cmd = String.format (Video_SPLIT,start,videoPath,length,output);
CmdUtil.execCmd (cmd);
}
public static void videoConvert(String path,String outPath) {
String cmd = String.format(Video_COVERT,path,outPath);
CmdUtil.execCmd(cmd);
}
public static void ImageToVodeoMethod(String path, String outPutPath) {
String cmd = String.format(Image_TO_VIdeo,path,outPutPath);
CmdUtil.execCmd(cmd);
}
public static void toVideoTs(String path, String s) {
String cmd = String.format(Video_TS,path,s);
CmdUtil.execCmd(cmd);
}
public static void ConcatMethod(String imgPath, String vPath, String outPut) {
String cmd = String.format(Concat,imgPath,vPath,outPut);
CmdUtil.execCmd(cmd);
}
}