JavaはFFmpegを使ってビデオファイルを処理する方法教程を使います。
前言
本論文では、Java+FFmpegを用いてビデオファイルに関する情報抽出、符号率圧縮、解像度変換などの機能を説明する。
前にインターネットで大きな輪Javaを見ましたが、FFmpegを使ってビデオを処理する文章は大体簡単です。ビルの主は実際の操作の過程でたくさんの穴を踏んで、たくさんの穴を埋めました。この詳しい踏み穴と記入マニュアルが皆さんの助けになることを願っています。
1.FFmpegとは何ですか
はい、わかりました
2.開発前準備
JavaでFFmpeg処理音ビデオを呼び出す前に、FFmpegをインストールする必要があります。インストール方法は二つに分けられます。は、FFmpegを実装したオープンソースフレーム を導入する。システムに手動でFFmpeg をインストールする。
2.1 FFmpegを実装したオープンソースフレームの導入
JAVE.jar(私はあなたを愛します)はFFmpegをカプセル化したJavaフレームであり、プロジェクトの中で直接にそのAPIを呼び出して音声ビデオファイルを処理することができる。
利点:使用が便利で、直接プロジェクトにJAVE.jarを導入すればメディアファイルを処理できます。そして開発が完了したら、プロジェクトと一緒に包装して発表できます。目標運行環境内で手動でFFmpeg関連のクラスライブラリをインストールする必要はありません。
短所:JAVE.jarの最終更新は2009年で、パッケージのFFmpegバージョンは09年かもっと前のバージョンで、比較的古いので、いくつかの新しい特性を使うことができません。
(もちろん、他の比較的新しいパッケージのフレームがあるかどうかを見てもいいです。)
Maven座標は以下の通りです
動作環境で手動でFFmpegをインストールするのはちょっと面倒です。Baidu windows/macにFFmpegというキーワードをインストールできます。インターネットのインストール教程によってFFmpegをシステムにインストールします。
怠け者リンク:Windowsインストール教程 Macセットアップ教程
利点:直接にFFmpegを呼び出すことができる関連API処理音ビデオ、FFmpegバージョンを制御することができます。
短所:手動でインストールするのは面倒です。開発環境と目標運行環境は先にFFmpegをインストールする必要があります。
3.FFmpegを使って音声ビデオを処理する
JAVE.jarを使った開発はFFmpegを直接使用して開発したコードとは少し違っていますが、ここでは直接FFmpegを使って開発したコードを説明します(開発環境MacOS)。JAVEのコードを使って、直接FFmpegを使うコードは文末に添付されますので、参考にしてください。)
MediaUtil.java類とその依存の種類を通じて、あなたは実現できます。は、ビデオフォーマット、時間長、符号率などを含むソースビデオの基本情報を解析する。 オーディオ、画像の基本情報を解析する。 は、ソースビデオを異なる解像度、異なる符号率、バンドまたはオーディオを持たない新しいビデオに変換する。 は、ソースビデオにおいて時間点を指定するフレーム画面を抽出して、静止画を生成する。 は、ソースビデオにおいて指定された時間帯のフレーム画面を抽出して、GIFダイナミック図を生成する。 はソースビデオの一部を切り取り、新しいビデオを形成する。 はソースビデオのオーディオ情報を抽出し、別のMP 3ファイルを生成する。 は、音声ビデオなどのメディアファイルに対してカスタムFFmpegコマンドを実行する。 3.1コード構造整理
MediaUtil.javaは全体の解析プログラムの中の核心の種類で、外部の呼び出しのために各種の常用の解析方法をカプセル化しました。
MetaInfo.javaはマルチメディアデータ共有のいくつかの属性を定義しています。Video Meta Info.java Music MetaInfo.javaはMetaInfo.javaから継承しています。それぞれビデオ、オーディオ、画像データに関するいくつかの属性を定義しています。
AnimedGifEncder.java LZWEncoder.java NeuQuannt.javaはビデオフレーム数を抽出して、GIF動画を作る時に使います。
CrfValue Enum.javaは、3つのよく使われるFFmpeg圧縮ビデオの時に使用されるcrf値を定義しています。Prest Vaule Enum.javaは、FFmpeg圧縮ビデオの時によく使われるいくつかの圧縮レート値を定義しています。
crf、presetについての拡張読解点は私にあります。
3.2 MediaUtil.javaメインプログラムクラスの解析
3.2.1使用前に注意すべき点
1、正しいFFmpegプログラムの実行経路を指定します。
MacOSがFFmpegをインストールしたら、コンソールでwhich ffmpegコマンドでFFmpegプログラムの実行経路を取得できます。MediaUtil.javaを呼び出す前に、まずset FFmpegPath()の方法でFFmpegプログラムのシステムでの実行経路を設定してから、FFmpegにスムーズに音声ビデオを解析することができます。
Windowsシステムでは、このパスは理論的に設定されています。FFmpeg実行可能なプログラムはシステムでの絶対パスです。
2、解析音ビデオ情報を指定する場合に必要な正規表現
プロジェクトの解析サフィックス形式は.MP 4.WMV.AACのビデオとオーディオファイルですので、JAVE.jarの下の階にFFmpegを呼び出した時の解析ロジックを調べた後、MediaUtil.javaにこの3つのフォーマットにマッチする正規表現を解析用に設定しました。
MKV.MP 3のようなメディアファイルを解析する必要がある場合、実際の状況に応じてdurationRegex videoStreamRegex music StreamRegexの3つの正規表現の値を変更する必要があります。
3、プログラムの多くのデフォルト値は実際の必要に応じて変更できます。たとえば、ビデオフレームの標準幅や高さ、時間などです。
3.2.2 MediaUtil.javaコード
1、LinuxなどのサーバーにJavaプログラムを展開してビデオ圧縮を行う時、アカウントを実行する権限の問題に注意してください。実行プログラムに十分なファイル操作権限がないため、圧縮プロセスが失敗する場合があります。
2、第一版のプログラムがオンラインになったら、たまにこのような問題が発生します。
MediaUtil.javaを呼び出してビデオ圧縮を行っていますが、プログラム全体が突然「引っ掛かり」になりました。バックグラウンドもログがありません。この時、圧縮プロセスはまだ完成していません。スレッドが突然ふさがりました。
複数の検索を経て、JavaがFFmpegを呼び出すと、実際にはJVMでサブプロセスを生成して圧縮プロセスを実行しています。このサブプロセスはJVMと3つのチャネルリンク(標準入力、標準出力、標準エラーフローを含む)を確立しています。圧縮過程では、実際に標準出力とエラーフローに情報を書き込み続けます。
標準出力とエラーフローに対してローカルシステムが提供するバッファサイズが限られているため、標準出力とエラーフローの情報を書き込みバッファ領域を満たした場合、圧縮を実行するプロセスはブロックされます。
したがって、圧縮プロセスにおいて、標準出力及びエラーフローの情報を単独で作成し、圧縮プロセス全体のブロックを防止する必要がある。MediaUtil.javaのexecuteCommand()方法におけるerrorStreamとinputStreamの2つの内部クラスの例の動作を参照してください。
3.3 CentOSサーバにFFmpegマニュアルをインストールする
プロジェクトが最終的にCentOSサーバに展開されているので、事前にFFmpegプログラムをサーバにインストールしておく必要があります。この過程でも多くのピットを踏んでいます。これに対して別の総括記事を書きました。ここ私を注文しますよを参照してください。
4.ソースのダウンロード
ここでは2つのバージョンのソースコードを提供します。は、FFmpegを実装したオープンソースフレームJave.jarのバージョンダウンロードを導入しました。 。システムに手動でFFmpegをインストールするバージョンダウンロード 締め括りをつける
以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に対して一定の参考となる学習価値を持っています。質問があれば、メッセージを書いて交流してください。ありがとうございます。
本論文では、Java+FFmpegを用いてビデオファイルに関する情報抽出、符号率圧縮、解像度変換などの機能を説明する。
前にインターネットで大きな輪Javaを見ましたが、FFmpegを使ってビデオを処理する文章は大体簡単です。ビルの主は実際の操作の過程でたくさんの穴を踏んで、たくさんの穴を埋めました。この詳しい踏み穴と記入マニュアルが皆さんの助けになることを願っています。
1.FFmpegとは何ですか
はい、わかりました
2.開発前準備
JavaでFFmpeg処理音ビデオを呼び出す前に、FFmpegをインストールする必要があります。インストール方法は二つに分けられます。
2.1 FFmpegを実装したオープンソースフレームの導入
JAVE.jar(私はあなたを愛します)はFFmpegをカプセル化したJavaフレームであり、プロジェクトの中で直接にそのAPIを呼び出して音声ビデオファイルを処理することができる。
利点:使用が便利で、直接プロジェクトにJAVE.jarを導入すればメディアファイルを処理できます。そして開発が完了したら、プロジェクトと一緒に包装して発表できます。目標運行環境内で手動でFFmpeg関連のクラスライブラリをインストールする必要はありません。
短所:JAVE.jarの最終更新は2009年で、パッケージのFFmpegバージョンは09年かもっと前のバージョンで、比較的古いので、いくつかの新しい特性を使うことができません。
(もちろん、他の比較的新しいパッケージのフレームがあるかどうかを見てもいいです。)
Maven座標は以下の通りです
<dependency>
<groupId>org.ffmpeg</groupId>
<artifactId>sdk</artifactId>
<version>1.0.2</version>
</dependency>
2.2システムに手動でFFmpegをインストールする動作環境で手動でFFmpegをインストールするのはちょっと面倒です。Baidu windows/macにFFmpegというキーワードをインストールできます。インターネットのインストール教程によってFFmpegをシステムにインストールします。
怠け者リンク:Windowsインストール教程 Macセットアップ教程
利点:直接にFFmpegを呼び出すことができる関連API処理音ビデオ、FFmpegバージョンを制御することができます。
短所:手動でインストールするのは面倒です。開発環境と目標運行環境は先にFFmpegをインストールする必要があります。
3.FFmpegを使って音声ビデオを処理する
JAVE.jarを使った開発はFFmpegを直接使用して開発したコードとは少し違っていますが、ここでは直接FFmpegを使って開発したコードを説明します(開発環境MacOS)。JAVEのコードを使って、直接FFmpegを使うコードは文末に添付されますので、参考にしてください。)
MediaUtil.java類とその依存の種類を通じて、あなたは実現できます。
MediaUtil.javaは全体の解析プログラムの中の核心の種類で、外部の呼び出しのために各種の常用の解析方法をカプセル化しました。
MetaInfo.javaはマルチメディアデータ共有のいくつかの属性を定義しています。Video Meta Info.java Music MetaInfo.javaはMetaInfo.javaから継承しています。それぞれビデオ、オーディオ、画像データに関するいくつかの属性を定義しています。
AnimedGifEncder.java LZWEncoder.java NeuQuannt.javaはビデオフレーム数を抽出して、GIF動画を作る時に使います。
CrfValue Enum.javaは、3つのよく使われるFFmpeg圧縮ビデオの時に使用されるcrf値を定義しています。Prest Vaule Enum.javaは、FFmpeg圧縮ビデオの時によく使われるいくつかの圧縮レート値を定義しています。
crf、presetについての拡張読解点は私にあります。
3.2 MediaUtil.javaメインプログラムクラスの解析
3.2.1使用前に注意すべき点
1、正しいFFmpegプログラムの実行経路を指定します。
MacOSがFFmpegをインストールしたら、コンソールでwhich ffmpegコマンドでFFmpegプログラムの実行経路を取得できます。MediaUtil.javaを呼び出す前に、まずset FFmpegPath()の方法でFFmpegプログラムのシステムでの実行経路を設定してから、FFmpegにスムーズに音声ビデオを解析することができます。
Windowsシステムでは、このパスは理論的に設定されています。FFmpeg実行可能なプログラムはシステムでの絶対パスです。
2、解析音ビデオ情報を指定する場合に必要な正規表現
プロジェクトの解析サフィックス形式は.MP 4.WMV.AACのビデオとオーディオファイルですので、JAVE.jarの下の階にFFmpegを呼び出した時の解析ロジックを調べた後、MediaUtil.javaにこの3つのフォーマットにマッチする正規表現を解析用に設定しました。
MKV.MP 3のようなメディアファイルを解析する必要がある場合、実際の状況に応じてdurationRegex videoStreamRegex music StreamRegexの3つの正規表現の値を変更する必要があります。
3、プログラムの多くのデフォルト値は実際の必要に応じて変更できます。たとえば、ビデオフレームの標準幅や高さ、時間などです。
3.2.2 MediaUtil.javaコード
package media;
import lombok.extern.slf4j.Slf4j;
import media.domain.ImageMetaInfo;
import media.domain.MusicMetaInfo;
import media.domain.VideoMetaInfo;
import media.domain.gif.AnimatedGifEncoder;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.sql.Time;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* FFmpeg ;
* FFmpeg , FFmpeg MediaUtil.java FFmpeg ;
*
* Author: dreamer-1
*
* version: 1.0
*
*/
@Slf4j
public class MediaUtil {
/**
*
*/
public final static String[] VIDEO_TYPE = { "MP4", "WMV" };
/**
*
*/
public final static String[] IMAGE_TYPE = { "JPG", "JPEG", "PNG", "GIF" };
/**
*
*/
public final static String[] AUDIO_TYPE = { "AAC" };
/**
* , 10s( )
* (Time :ms)
*/
private static final Time DEFAULT_TIME = new Time(0, 0, 10);
/**
* , :px
*/
private static int DEFAULT_WIDTH = 320;
/**
* , :s( )
*/
private static int DEFAULT_TIME_LENGTH = 10;
/**
* gif ,gif
*/
private static int DEFAULT_GIF_PLAYTIME = 110;
/**
* FFmpeg
* ffmpeg , ffmpeg
*/
private static String FFMPEG_PATH = "/usr/bin/ffmpeg"; // /usr/bin/ffmpeg
/**
*
* ;
*
* (.*?) : \r
0 ,
*
*/
private static String durationRegex = "Duration: (\\d*?):(\\d*?):(\\d*?)\\.(\\d*?), start: (.*?), bitrate: (\\d*) kb\\/s.*";
private static Pattern durationPattern;
/**
*
* ;
*/
private static String videoStreamRegex = "Stream #\\d:\\d[\\(]??\\S*[\\)]??: Video: (\\S*\\S$?)[^\\,]*, (.*?), (\\d*)x(\\d*)[^\\,]*, (\\d*) kb\\/s, (\\d*[\\.]??\\d*) fps";
private static Pattern videoStreamPattern;
/**
*
* ;
*/
private static String musicStreamRegex = "Stream #\\d:\\d[\\(]??\\S*[\\)]??: Audio: (\\S*\\S$?)(.*), (.*?) Hz, (.*?), (.*?), (\\d*) kb\\/s";;
private static Pattern musicStreamPattern;
/**
*
*/
static {
durationPattern = Pattern.compile(durationRegex);
videoStreamPattern = Pattern.compile(videoStreamRegex);
musicStreamPattern = Pattern.compile(musicStreamRegex);
}
/**
* ffmpeg
* @return
*/
public static String getFFmpegPath() {
return FFMPEG_PATH;
}
/**
* ffmpeg
* @param ffmpeg_path ffmpeg
* @return
*/
public static boolean setFFmpegPath(String ffmpeg_path) {
if (StringUtils.isBlank(ffmpeg_path)) {
log.error("--- ffmpeg , ffmpeg ! ---");
return false;
}
File ffmpegFile = new File(ffmpeg_path);
if (!ffmpegFile.exists()) {
log.error("--- ffmpeg , ffmpeg ffmpeg ! ---");
return false;
}
FFMPEG_PATH = ffmpeg_path;
log.info("--- ffmpeg --- ffmpeg : " + ffmpeg_path);
return true;
}
/**
*
* @return
*/
public static boolean isExecutable() {
File ffmpegFile = new File(FFMPEG_PATH);
if (!ffmpegFile.exists()) {
log.error("--- , ffmpeg ffmpeg ! ---");
return false;
}
List<String> cmds = new ArrayList<>(1);
cmds.add("-version");
String ffmpegVersionStr = executeCommand(cmds);
if (StringUtils.isBlank(ffmpegVersionStr)) {
log.error("--- , ffmpeg ! ---");
return false;
}
log.info("--- ---");
return true;
}
/**
* FFmpeg
* @param commonds FFmpeg
* @return FFmpeg , null
*/
public static String executeCommand(List<String> commonds) {
if (CollectionUtils.isEmpty(commonds)) {
log.error("--- , FFmpeg ! ---");
return null;
}
LinkedList<String> ffmpegCmds = new LinkedList<>(commonds);
ffmpegCmds.addFirst(FFMPEG_PATH); // ffmpeg
log.info("--- FFmpeg :---" + ffmpegCmds);
Runtime runtime = Runtime.getRuntime();
Process ffmpeg = null;
try {
// ffmpeg
ProcessBuilder builder = new ProcessBuilder();
builder.command(ffmpegCmds);
ffmpeg = builder.start();
log.info("--- FFmpeg :--- :" + builder.toString());
//
// : ffmpeg , jvm ,
PrintStream errorStream = new PrintStream(ffmpeg.getErrorStream());
PrintStream inputStream = new PrintStream(ffmpeg.getInputStream());
errorStream.start();
inputStream.start();
// ffmpeg
ffmpeg.waitFor();
//
String result = errorStream.stringBuffer.append(inputStream.stringBuffer).toString();
//
String cmdStr = Arrays.toString(ffmpegCmds.toArray()).replace(",", "");
String resultStr = StringUtils.isBlank(result) ? "【 】" : " ";
log.info("--- FFmepg : ---" + cmdStr + " , : " + resultStr);
return result;
} catch (Exception e) {
log.error("--- FFmpeg ! --- : " + e.getMessage());
return null;
} finally {
if (null != ffmpeg) {
ProcessKiller ffmpegKiller = new ProcessKiller(ffmpeg);
// JVM , FFmepg
runtime.addShutdownHook(ffmpegKiller);
}
}
}
/**
*
*
* , ;
*
* @param fileInput
* @param fileOutPut
* @param withAudio ;true- ,false-
* @param crf ( , , ; 0-51, ), :CrfValueEnum.code
* @param preset ( ), :PresetVauleEnum.presetValue
* @param width ;
* @param height ;
*/
public static void convertVideo(File fileInput, File fileOutPut, boolean withAudio, Integer crf, String preset, Integer width, Integer height) {
if (null == fileInput || !fileInput.exists()) {
throw new RuntimeException(" , ");
}
if (null == fileOutPut) {
throw new RuntimeException(" , ");
}
if (!fileOutPut.exists()) {
try {
fileOutPut.createNewFile();
} catch (IOException e) {
log.error(" ");
}
}
String format = getFormat(fileInput);
if (!isLegalFormat(format, VIDEO_TYPE)) {
throw new RuntimeException(" :" + format);
}
List<String> commond = new ArrayList<String>();
commond.add("-i");
commond.add(fileInput.getAbsolutePath());
if (!withAudio) { //
commond.add("-an"); //
}
if (null != width && width > 0 && null != height && height > 0) { //
commond.add("-s");
String resolution = width.toString() + "x" + height.toString();
commond.add(resolution);
}
commond.add("-vcodec"); //
commond.add("libx264"); // x264
commond.add("-preset"); // x264
commond.add(preset); // preset
commond.add("-crf"); //
commond.add(crf.toString()); // ,
commond.add("-y"); // ,
commond.add(fileOutPut.getAbsolutePath());
executeCommand(commond);
}
/**
*
* 10
* 300px
*
* .gif , 10s , 10s gif
*
* @param videoFile
* @param fileOutPut
*/
public static void cutVideoFrame(File videoFile, File fileOutPut) {
cutVideoFrame(videoFile, fileOutPut, DEFAULT_TIME);
}
/**
* ( )
* 320px
*
* .gif , , 10s gif
*
* @param videoFile
* @param fileOutPut
* @param time ( :s)
*/
public static void cutVideoFrame(File videoFile, File fileOutPut, Time time) {
cutVideoFrame(videoFile, fileOutPut, time, DEFAULT_WIDTH);
}
/**
* ( 、 )
* ,
*
* .gif , , 10s gif
*
* @param videoFile
* @param fileOutPut
* @param time ( :s)
* @param width ( :px)
*/
public static void cutVideoFrame(File videoFile, File fileOutPut, Time time, int width) {
if (null == videoFile || !videoFile.exists()) {
throw new RuntimeException(" , ");
}
if (null == fileOutPut) {
throw new RuntimeException(" , ");
}
VideoMetaInfo info = getVideoMetaInfo(videoFile);
if (null == info) {
log.error("--- , --- : " + videoFile);
return;
}
int height = width * info.getHeight() / info.getWidth(); // ,
cutVideoFrame(videoFile, fileOutPut, time, width, height);
}
/**
* ( 、 、 )
*
* .gif , , 10s gif
*
* @param videoFile
* @param fileOutPut
* @param time ( :s)
* @param width ( :px)
* @param height ( :px)
*/
public static void cutVideoFrame(File videoFile, File fileOutPut, Time time, int width, int height) {
if (null == videoFile || !videoFile.exists()) {
throw new RuntimeException(" , ");
}
if (null == fileOutPut) {
throw new RuntimeException(" , ");
}
String format = getFormat(fileOutPut);
if (!isLegalFormat(format, IMAGE_TYPE)) {
throw new RuntimeException(" :" + format);
}
String fileOutPutPath = fileOutPut.getAbsolutePath();
if (!"GIF".equals(StringUtils.upperCase(format))) {
// .gif ,
cutVideoFrame(videoFile, fileOutPutPath, time, width, height, 1, false);
} else {
// gif(gif 10 )
String path = fileOutPut.getParent();
String name = fileOutPut.getName();
// gif
String tempPath = path + File.separator + System.currentTimeMillis() + "_" + name.substring(0, name.indexOf("."));
File file = new File(tempPath);
if (!file.exists()) {
file.mkdir();
}
try {
cutVideoFrame(videoFile, tempPath, time, width, height, DEFAULT_TIME_LENGTH, true);
// gif
String images[] = file.list();
for (int i = 0; i < images.length; i++) {
images[i] = tempPath + File.separator + images[i];
}
createGifImage(images, fileOutPut.getAbsolutePath(), DEFAULT_GIF_PLAYTIME);
} catch (Exception e) {
log.error("--- --- :" + e.getMessage());
} finally {
// gif
String images[] = file.list();
for (int i = 0; i < images.length; i++) {
File fileDelete = new File(tempPath + File.separator + images[i]);
fileDelete.delete();
}
file.delete();
}
}
}
/**
* ( 、 、 、 、 / )
*
* @param videoFile
* @param path
* @param time ( :s)
* @param width ( :px)
* @param height ( :px, 20)
* @param timeLength ( time , :s, )
* @param isContinuty false - ( time ),true - ( time ,timelength )
*/
private static void cutVideoFrame(File videoFile, String path, Time time, int width, int height, int timeLength, boolean isContinuty) {
if (videoFile == null || !videoFile.exists()) {
throw new RuntimeException(" , : ");
}
if (null == path) {
throw new RuntimeException(" , ");
}
VideoMetaInfo info = getVideoMetaInfo(videoFile);
if (null == info) {
throw new RuntimeException(" ");
}
if (time.getTime() + timeLength > info.getDuration()) {
throw new RuntimeException(" :" + time.toString() + ", ");
}
if (width <= 20 || height <= 20) {
throw new RuntimeException(" , 20");
}
try {
List<String> commond = new ArrayList<String>();
commond.add("-ss");
commond.add(time.toString());
if (isContinuty) {
commond.add("-t");
commond.add(timeLength + "");
} else {
commond.add("-vframes");
commond.add("1");
}
commond.add("-i");
commond.add(videoFile.getAbsolutePath());
commond.add("-an");
commond.add("-f");
commond.add("image2");
if (isContinuty) {
commond.add("-r");
commond.add("3");
}
commond.add("-s");
commond.add(width + "*" + height);
if (isContinuty) {
commond.add(path + File.separator + "foo-%03d.jpeg");
} else {
commond.add(path);
}
executeCommand(commond);
} catch (Exception e) {
log.error("--- --- : " + e.getMessage());
}
}
/**
* ,
*
* @param videoFile
* @param outputFile
* @param startTime ( :s)
* @param timeLength ( :s, ); : 10 startTime 10
*/
public static void cutVideo(File videoFile, File outputFile, Time startTime, int timeLength) {
if (videoFile == null || !videoFile.exists()) {
throw new RuntimeException(" :");
}
if (null == outputFile) {
throw new RuntimeException(" , ");
}
VideoMetaInfo info = getVideoMetaInfo(videoFile);
if (null == info) {
throw new RuntimeException(" ");
}
if (startTime.getTime() + timeLength > info.getDuration()) {
throw new RuntimeException(" :" + startTime.toString() + ", ");
}
try {
if (!outputFile.exists()) {
outputFile.createNewFile();
}
List<String> commond = new ArrayList<String>();
commond.add("-ss");
commond.add(startTime.toString());
commond.add("-t");
commond.add("" + timeLength);
commond.add("-i");
commond.add(videoFile.getAbsolutePath());
commond.add("-vcodec");
commond.add("copy");
commond.add("-acodec");
commond.add("copy");
commond.add(outputFile.getAbsolutePath());
executeCommand(commond);
} catch (IOException e) {
log.error("--- ---");
}
}
/**
*
* MP3
* @param videoFile
* @param audioFile
*/
public static void getAudioFromVideo(File videoFile, File audioFile) {
if (null == videoFile || !videoFile.exists()) {
throw new RuntimeException(" : ");
}
if (null == audioFile) {
throw new RuntimeException(" :");
}
String format = getFormat(audioFile);
if (!isLegalFormat(format, AUDIO_TYPE)) {
throw new RuntimeException(" :" + format + " AAC ");
}
try {
if (!audioFile.exists()) {
audioFile.createNewFile();
}
List<String> commond = new ArrayList<String>();
commond.add("-i");
commond.add(videoFile.getAbsolutePath());
commond.add("-vn"); // no video,
commond.add("-y");
commond.add("-acodec");
commond.add("copy");
commond.add(audioFile.getAbsolutePath());
executeCommand(commond);
} catch (Exception e) {
log.error("--- --- : " + e.getMessage());
}
}
/**
* ( )
*
* :
* Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '6.mp4':
* Duration: 00:00:30.04, start: 0.000000, bitrate: 19031 kb/s
* Stream #0:0(eng): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1920x1080, 18684 kb/s, 25 fps, 25 tbr, 25k tbn, 50 tbc (default)
* Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 317 kb/s (default)
*
* :
* Duration: 00:00:30.04【 】, start: 0.000000【 】, bitrate: 19031 kb/s【 / 】
* Stream #0:0(eng): Video: h264【 】 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1920x1080【 , x 】, 18684【 】 kb/s, 25【 】 fps, 25 tbr, 25k tbn, 50 tbc (default)
* Stream #0:1(eng): Audio: aac【 】 (LC) (mp4a / 0x6134706D), 48000【 】 Hz, stereo, fltp, 317【 】 kb/s (default)
*
* @param videoFile
* @return , null
*/
public static VideoMetaInfo getVideoMetaInfo(File videoFile) {
if (null == videoFile || !videoFile.exists()) {
log.error("--- , ---");
return null;
}
VideoMetaInfo videoInfo = new VideoMetaInfo();
String parseResult = getMetaInfoFromFFmpeg(videoFile);
Matcher durationMacher = durationPattern.matcher(parseResult);
Matcher videoStreamMacher = videoStreamPattern.matcher(parseResult);
Matcher videoMusicStreamMacher = musicStreamPattern.matcher(parseResult);
Long duration = 0L; //
Integer videoBitrate = 0; //
String videoFormat = getFormat(videoFile); //
Long videoSize = videoFile.length(); //
String videoEncoder = ""; //
Integer videoHeight = 0; //
Integer videoWidth = 0; //
Float videoFramerate = 0F; //
String musicFormat = ""; //
Long samplerate = 0L; //
Integer musicBitrate = 0; //
try {
//
if (durationMacher.find()) {
long hours = (long)Integer.parseInt(durationMacher.group(1));
long minutes = (long)Integer.parseInt(durationMacher.group(2));
long seconds = (long)Integer.parseInt(durationMacher.group(3));
long dec = (long)Integer.parseInt(durationMacher.group(4));
duration = dec * 100L + seconds * 1000L + minutes * 60L * 1000L + hours * 60L * 60L * 1000L;
//String startTime = durationMacher.group(5) + "ms";
videoBitrate = Integer.parseInt(durationMacher.group(6));
}
//
if (videoStreamMacher.find()) {
videoEncoder = videoStreamMacher.group(1);
String s2 = videoStreamMacher.group(2);
videoWidth = Integer.parseInt(videoStreamMacher.group(3));
videoHeight = Integer.parseInt(videoStreamMacher.group(4));
String s5 = videoStreamMacher.group(5);
videoFramerate = Float.parseFloat(videoStreamMacher.group(6));
}
//
if (videoMusicStreamMacher.find()) {
musicFormat = videoMusicStreamMacher.group(1); //
//String s2 = videoMusicStreamMacher.group(2);
samplerate = Long.parseLong(videoMusicStreamMacher.group(3)); //
//String s4 = videoMusicStreamMacher.group(4);
//String s5 = videoMusicStreamMacher.group(5);
musicBitrate = Integer.parseInt(videoMusicStreamMacher.group(6)); //
}
} catch (Exception e) {
log.error("--- ! --- : " + e.getMessage());
return null;
}
//
MusicMetaInfo musicMetaInfo = new MusicMetaInfo();
musicMetaInfo.setFormat(musicFormat);
musicMetaInfo.setDuration(duration);
musicMetaInfo.setBitRate(musicBitrate);
musicMetaInfo.setSampleRate(samplerate);
//
VideoMetaInfo videoMetaInfo = new VideoMetaInfo();
videoMetaInfo.setFormat(videoFormat);
videoMetaInfo.setSize(videoSize);
videoMetaInfo.setBitRate(videoBitrate);
videoMetaInfo.setDuration(duration);
videoMetaInfo.setEncoder(videoEncoder);
videoMetaInfo.setFrameRate(videoFramerate);
videoMetaInfo.setHeight(videoHeight);
videoMetaInfo.setWidth(videoWidth);
videoMetaInfo.setMusicMetaInfo(musicMetaInfo);
return videoMetaInfo;
}
/**
* ( )
*
* @param inputStream
* @return , null
*/
public static VideoMetaInfo getVideoMetaInfo(InputStream inputStream) {
VideoMetaInfo videoInfo = new VideoMetaInfo();
try {
File file = File.createTempFile("tmp", null);
if (!file.exists()) {
return null;
}
FileUtils.copyInputStreamToFile(inputStream, file);
videoInfo = getVideoMetaInfo(file);
file.deleteOnExit();
return videoInfo;
} catch (Exception e) {
log.error("--- --- : " + e.getMessage());
return null;
}
}
/**
* ( )
* @param musicFile
* @return , null
*/
public static MusicMetaInfo getMusicMetaInfo(File musicFile) {
if (null == musicFile || !musicFile.exists()) {
log.error("--- , ---");
return null;
}
// ,
String parseResult = getMetaInfoFromFFmpeg(musicFile);
Long duration = 0L; //
Integer musicBitrate = 0; //
Long samplerate = 0L; //
String musicFormat = ""; //
Long musicSize = musicFile.length(); //
Matcher durationMacher = durationPattern.matcher(parseResult);
Matcher musicStreamMacher = musicStreamPattern.matcher(parseResult);
try {
//
if (durationMacher.find()) {
long hours = (long)Integer.parseInt(durationMacher.group(1));
long minutes = (long)Integer.parseInt(durationMacher.group(2));
long seconds = (long)Integer.parseInt(durationMacher.group(3));
long dec = (long)Integer.parseInt(durationMacher.group(4));
duration = dec * 100L + seconds * 1000L + minutes * 60L * 1000L + hours * 60L * 60L * 1000L;
//String startTime = durationMacher.group(5) + "ms";
musicBitrate = Integer.parseInt(durationMacher.group(6));
}
//
if (musicStreamMacher.find()) {
musicFormat = musicStreamMacher.group(1); //
//String s2 = videoMusicStreamMacher.group(2);
samplerate = Long.parseLong(musicStreamMacher.group(3)); //
//String s4 = videoMusicStreamMacher.group(4);
//String s5 = videoMusicStreamMacher.group(5);
musicBitrate = Integer.parseInt(musicStreamMacher.group(6)); //
}
} catch (Exception e) {
log.error("--- ! --- : " + e.getMessage());
return null;
}
//
MusicMetaInfo musicMetaInfo = new MusicMetaInfo();
musicMetaInfo.setFormat(musicFormat);
musicMetaInfo.setDuration(duration);
musicMetaInfo.setBitRate(musicBitrate);
musicMetaInfo.setSampleRate(samplerate);
musicMetaInfo.setSize(musicSize);
return musicMetaInfo;
}
/**
* ( )
* @param inputStream
* @return , null
*/
public static MusicMetaInfo getMusicMetaInfo(InputStream inputStream) {
MusicMetaInfo musicMetaInfo = new MusicMetaInfo();
try {
File file = File.createTempFile("tmp", null);
if (!file.exists()) {
return null;
}
FileUtils.copyInputStreamToFile(inputStream, file);
musicMetaInfo = getMusicMetaInfo(file);
file.deleteOnExit();
return musicMetaInfo;
} catch (Exception e) {
log.error("--- --- : " + e.getMessage());
return null;
}
}
/**
* ( )
*
* @param inputStream
* @return , null
*/
public static ImageMetaInfo getImageInfo(InputStream inputStream) {
BufferedImage image = null;
ImageMetaInfo imageInfo = new ImageMetaInfo();
try {
image = ImageIO.read(inputStream);
imageInfo.setWidth(image.getWidth());
imageInfo.setHeight(image.getHeight());
imageInfo.setSize(Long.valueOf(String.valueOf(inputStream.available())));
return imageInfo;
} catch (Exception e) {
log.error("--- --- : " + e.getMessage());
return null;
}
}
/**
* ( )
*
* @param imageFile
* @return , null
*/
public static ImageMetaInfo getImageInfo(File imageFile) {
BufferedImage image = null;
ImageMetaInfo imageInfo = new ImageMetaInfo();
try {
if (null == imageFile || !imageFile.exists()) {
return null;
}
image = ImageIO.read(imageFile);
imageInfo.setWidth(image.getWidth());
imageInfo.setHeight(image.getHeight());
imageInfo.setSize(imageFile.length());
imageInfo.setFormat(getFormat(imageFile));
return imageInfo;
} catch (Exception e) {
log.error("--- --- : " + e.getMessage());
return null;
}
}
/**
*
* @param inputFile
* @param givenFormat ; :{"MP4", "AVI"}
* @return
*/
public static boolean isGivenFormat(File inputFile, String[] givenFormat) {
if (null == inputFile || !inputFile.exists()) {
log.error("--- , --- : " + inputFile);
return false;
}
if (null == givenFormat || givenFormat.length <= 0) {
log.error("--- , ---");
return false;
}
String fomat = getFormat(inputFile);
return isLegalFormat(fomat, givenFormat);
}
/**
* FFmpeg "-i"
* @param inputFile
* @return ,
*/
public static String getMetaInfoFromFFmpeg(File inputFile) {
if (inputFile == null || !inputFile.exists()) {
throw new RuntimeException(" , : ");
}
List<String> commond = new ArrayList<String>();
commond.add("-i");
commond.add(inputFile.getAbsolutePath());
String executeResult = MediaUtil.executeCommand(commond);
return executeResult;
}
/**
*
* @param format
* @param formats
* @return
*/
private static boolean isLegalFormat(String format, String formats[]) {
for (String item : formats) {
if (item.equals(StringUtils.upperCase(format))) {
return true;
}
}
return false;
}
/**
* gif
*
* @param image jpg ( )
* @param outputPath gif ( )
* @param playTime , gif
*/
private static void createGifImage(String image[], String outputPath, int playTime) {
if (null == outputPath) {
throw new RuntimeException(" GIF , GIF ");
}
try {
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
encoder.setRepeat(0);
encoder.start(outputPath);
BufferedImage src[] = new BufferedImage[image.length];
for (int i = 0; i < src.length; i++) {
encoder.setDelay(playTime); //
src[i] = ImageIO.read(new File(image[i])); // jpg
encoder.addFrame(src[i]); //
}
encoder.finish();
} catch (Exception e) {
log.error("--- GIF --- : " + e.getMessage());
}
}
/**
*
* @param file
* @return
*/
private static String getFormat(File file) {
String fileName = file.getName();
String format = fileName.substring(fileName.indexOf(".") + 1);
return format;
}
/**
* FFmpeg
*/
private static class ProcessKiller extends Thread {
private Process process;
public ProcessKiller(Process process) {
this.process = process;
}
@Override
public void run() {
this.process.destroy();
log.info("--- FFmpeg --- : " + process.toString());
}
}
/**
* ffmpeg
*/
static class PrintStream extends Thread {
InputStream inputStream = null;
BufferedReader bufferedReader = null;
StringBuffer stringBuffer = new StringBuffer();
public PrintStream(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public void run() {
try {
if (null == inputStream) {
log.error("--- ! !---");
}
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
log.info(line);
stringBuffer.append(line);
}
} catch (Exception e) {
log.error("--- !--- :" + e.getMessage());
} finally {
try {
if (null != bufferedReader) {
bufferedReader.close();
}
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
log.error("--- PrintStream , !---");
}
}
}
}
}
3.2.3ピットを踏む&ピットを埋める1、LinuxなどのサーバーにJavaプログラムを展開してビデオ圧縮を行う時、アカウントを実行する権限の問題に注意してください。実行プログラムに十分なファイル操作権限がないため、圧縮プロセスが失敗する場合があります。
2、第一版のプログラムがオンラインになったら、たまにこのような問題が発生します。
MediaUtil.javaを呼び出してビデオ圧縮を行っていますが、プログラム全体が突然「引っ掛かり」になりました。バックグラウンドもログがありません。この時、圧縮プロセスはまだ完成していません。スレッドが突然ふさがりました。
複数の検索を経て、JavaがFFmpegを呼び出すと、実際にはJVMでサブプロセスを生成して圧縮プロセスを実行しています。このサブプロセスはJVMと3つのチャネルリンク(標準入力、標準出力、標準エラーフローを含む)を確立しています。圧縮過程では、実際に標準出力とエラーフローに情報を書き込み続けます。
標準出力とエラーフローに対してローカルシステムが提供するバッファサイズが限られているため、標準出力とエラーフローの情報を書き込みバッファ領域を満たした場合、圧縮を実行するプロセスはブロックされます。
したがって、圧縮プロセスにおいて、標準出力及びエラーフローの情報を単独で作成し、圧縮プロセス全体のブロックを防止する必要がある。MediaUtil.javaのexecuteCommand()方法におけるerrorStreamとinputStreamの2つの内部クラスの例の動作を参照してください。
3.3 CentOSサーバにFFmpegマニュアルをインストールする
プロジェクトが最終的にCentOSサーバに展開されているので、事前にFFmpegプログラムをサーバにインストールしておく必要があります。この過程でも多くのピットを踏んでいます。これに対して別の総括記事を書きました。ここ私を注文しますよを参照してください。
4.ソースのダウンロード
ここでは2つのバージョンのソースコードを提供します。
以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に対して一定の参考となる学習価値を持っています。質問があれば、メッセージを書いて交流してください。ありがとうございます。