1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012 |
- package com.cooleshow.ffmpegcmd.util;
- import android.content.Context;
- import com.cooleshow.ffmpegcmd.model.VideoLayout;
- import java.util.List;
- import java.util.Locale;
- /**
- * ffmpeg tool: assemble the complete command
- * Created by frank on 2018/1/23.
- */
- public class FFmpegUtil {
- private static String[] insert(String[] cmd, int position, String inputPath) {
- return insert(cmd, position, inputPath, null);
- }
- /**
- * insert inputPath and outputPath into target array
- */
- private static String[] insert(String[] cmd, int position, String inputPath, String outputPath) {
- if (cmd == null || inputPath == null || position < 2) {
- return cmd;
- }
- int len = (outputPath != null ? (cmd.length + 2) : (cmd.length + 1));
- String[] result = new String[len];
- System.arraycopy(cmd, 0, result, 0, position);
- result[position] = inputPath;
- System.arraycopy(cmd, position, result, position + 1, cmd.length - position);
- if (outputPath != null) {
- result[result.length - 1] = outputPath;
- }
- return result;
- }
- public static String[] insert(String[] cmd, int position1, String inputPath1,
- int position2, String inputPath2, String outputPath) {
- if (cmd == null || inputPath1 == null || position1 < 2 || inputPath2 == null || position2 < 4) {
- return cmd;
- }
- int len = (outputPath != null ? (cmd.length + 3) : (cmd.length + 2));
- String[] result = new String[len];
- System.arraycopy(cmd, 0, result, 0, position1);
- result[position1] = inputPath1;
- System.arraycopy(cmd, position1, result, position1 + 1, position2 - position1 - 1);
- result[position2] = inputPath2;
- System.arraycopy(cmd, position2 - 1, result, position2 + 1, cmd.length - (position2 - 1));
- if (outputPath != null) {
- result[result.length - 1] = outputPath;
- }
- return result;
- }
- /**
- * transform audio, according to your assigning the output format
- *
- * @param inputPath input file
- * @param outputPath output file
- * @return transform success or not
- */
- public static String[] transformAudio(String inputPath, String outputPath) {
- String[] result = new String[4];
- result[0] = "ffmpeg";
- result[1] = "-i";
- result[2] = inputPath;
- result[3] = outputPath;
- return result;
- }
- public static String[] transformAudio(String inputPath, String acodec, String outputPath) {
- String transformAudioCmd = "ffmpeg -i -acodec %s -ac 2 -ar 44100";
- transformAudioCmd = String.format(transformAudioCmd, acodec);
- return insert(transformAudioCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * cut audio, you could assign the startTime and duration which you want to
- *
- * @param inputPath input file
- * @param startTime start time in the audio(unit is second)
- * @param duration start time(unit is second)
- * @param outputPath output file
- * @return cut success or not
- */
- public static String[] cutAudio(String inputPath, float startTime, float duration, String outputPath) {
- String cutAudioCmd = "ffmpeg -i -ss %f -t %f -vn";
- cutAudioCmd = String.format(Locale.getDefault(), cutAudioCmd, startTime, duration);
- return insert(cutAudioCmd.split(" "), 2, inputPath, outputPath);
- }
- public static String[] getAudioDuration(String input) {
- // String cmd = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 %s";
- String cmd = "ffmpeg -i %s -hide_banner -loglevel grep Duration | cut -d ' ' -f 4 | sed 's/,//'";
- // String cmd = "ffmpeg -i %s";
- cmd = String.format(Locale.getDefault(), cmd, input);
- return cmd.split(" ");
- }
- /**
- * concat all the audio together
- *
- * @param fileList list of files
- * @param outputPath output file
- * @return concat success or not
- */
- public static String[] concatAudio(List<String> fileList, String outputPath) {
- if (fileList == null || fileList.size() == 0) {
- return null;
- }
- StringBuilder concatBuilder = new StringBuilder();
- concatBuilder.append("concat:");
- for (String file : fileList) {
- concatBuilder.append(file).append("|");
- }
- String concatStr = concatBuilder.substring(0, concatBuilder.length() - 1);
- String concatAudioCmd = "ffmpeg -i -acodec copy"; // "ffmpeg -i -acodec libmp3lame"
- return insert(concatAudioCmd.split(" "), 2, concatStr, outputPath);
- }
- /**
- * mix multiple audio inputs into a single output
- */
- public static String[] mixAudio(String inputPath, String mixPath, String outputPath) {
- //mixing formula: value = sample1 + sample2 - ((sample1 * sample2) >> 0x10)
- return mixAudio(inputPath, mixPath, 0, 0, false, outputPath);
- }
- public static String[] mixAudio(String inputPath, String mixPath, int weight1, int weight2,
- boolean disableThumb, String outputPath) {
- //duration: first shortest longest
- //weight: adjust weight(volume) of each audio stream
- //disableThumb:(-vn)if not disable, it may cause incorrect mixing
- int len = 8;
- String amix = "amix=inputs=2:duration=shortest";
- if (disableThumb) len += 1;
- String[] mixAudioCmd = new String[len];
- mixAudioCmd[0] = "ffmpeg";
- mixAudioCmd[1] = "-i";
- mixAudioCmd[2] = inputPath;
- mixAudioCmd[3] = "-i";
- mixAudioCmd[4] = mixPath;
- mixAudioCmd[5] = "-filter_complex";
- if (weight1 > 0 && weight2 > 0) {
- String weight = ":weights=" + "'" + weight1 + " " + weight2 + "'";
- amix += weight;
- }
- mixAudioCmd[6] = amix;
- if (disableThumb) mixAudioCmd[7] = "-vn";
- mixAudioCmd[len - 1] = outputPath;
- return mixAudioCmd;
- }
- public static String[] mixAudio(String inputPath, String mixPath, int weight1, int weight2, float volume1, float volume2,
- boolean disableThumb, String outputPath, int delay, int delay2) {
- //duration: first shortest longest
- //weight: adjust weight(volume) of each audio stream
- //disableThumb:(-vn)if not disable, it may cause incorrect mixing
- int len = 8;
- // String amix = "[1]adelay=%d|%d[b];[0][b]amix=inputs=2:duration=shortest";
- // String amix = "";
- // if (isShortestAligning) {
- // amix = "[0]volume=%f,adelay=%d|%d[a];[1]volume=%f,adelay=%d|%d[b];[a][b]amix=inputs=2:duration=shortest";
- // } else {
- // amix = "[0]volume=%f,adelay=%d|%d[a];[1]volume=%f,adelay=%d|%d[b];[a][b]amix=inputs=2:duration=longest";
- // }
- String amix = "[0]volume=%f,adelay=%d|%d[a];[1]volume=%f,adelay=%d|%d[b];[a][b]amix=inputs=2:duration=first";
- amix = String.format(Locale.getDefault(), amix, volume1, delay, delay, volume2, delay2, delay2);
- if (disableThumb) len += 1;
- String[] mixAudioCmd = new String[len];
- mixAudioCmd[0] = "ffmpeg";
- mixAudioCmd[1] = "-i";
- mixAudioCmd[2] = inputPath;
- mixAudioCmd[3] = "-i";
- mixAudioCmd[4] = mixPath;
- mixAudioCmd[5] = "-filter_complex";
- if (weight1 > 0 && weight2 > 0) {
- String weight = ":weights=" + "'" + weight1 + " " + weight2 + "'";
- amix += weight;
- }
- mixAudioCmd[6] = amix;
- if (disableThumb) mixAudioCmd[7] = "-vn";
- mixAudioCmd[len - 1] = outputPath;
- return mixAudioCmd;
- }
- /**
- * merge multiple audio streams into a single multi-channel stream
- */
- public static String[] mergeAudio(String inputPath, String mergePath, String outputPath) {
- String mergeCmd = "ffmpeg -i %s -i %s -filter_complex [0:a][1:a]amerge=inputs=2[aout] -map [aout] %s";
- mergeCmd = String.format(mergeCmd, inputPath, mergePath, outputPath);
- return mergeCmd.split(" ");
- }
- /**
- * Set echo and delay effect
- *
- * @param inputPath input file
- * @param delay delay to play
- * @param outputPath output file
- */
- public static String[] audioEcho(String inputPath, int delay, String outputPath) {
- // in_gain (0, 1], Default is 0.6
- // out_gain (0, 1], Default is 0.3
- // delays (0 - 90000]. Default is 1000
- // decays (0 - 1.0]. Default is 0.5
- String echoCmd = "ffmpeg -i -af aecho=0.8:0.8:%d:0.5";
- echoCmd = String.format(Locale.getDefault(), echoCmd, delay);
- return insert(echoCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Set tremolo effect, sinusoidal amplitude modulation
- *
- * @param inputPath input file
- * @param frequency frequency
- * @param depth depth
- * @param outputPath output file
- */
- public static String[] audioTremolo(String inputPath, int frequency, float depth, String outputPath) {
- // frequency [0.1, 20000.0], Default is 5
- // depth (0, 1], Default is 0.5
- String tremoloCmd = "ffmpeg -i -af tremolo=%d:%f";
- tremoloCmd = String.format(Locale.getDefault(), tremoloCmd, frequency, depth);
- return insert(tremoloCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Denoise audio samples with FFT
- *
- * @param inputPath input file
- * @param outputPath output file
- */
- public static String[] audioDenoise(String inputPath, String outputPath) {
- // nr: noise reduction in dB, [0.01 to 97], Default value is 12 dB
- // nf: noise floor in dB, [-80 to -20], Default value is -50 dB
- // nt: noise type {w:white noise v:vinyl noise s:shellac noise}
- String fftDenoiseCmd = "ffmpeg -i -af afftdn";
- return insert(fftDenoiseCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Detect silence of a chunk of audio
- *
- * @param inputPath input file
- */
- public static String[] audioSilenceDetect(String inputPath) {
- // silence_start: 268.978
- // silence_end: 271.048 | silence_duration: 2.06975
- String silenceCmd = "ffmpeg -i -af silencedetect=noise=0.0001 -f null -";
- return insert(silenceCmd.split(" "), 2, inputPath);
- }
- /**
- * Change volume of a chunk of audio
- *
- * @param inputPath input file
- * @param volume volume
- * @param outputPath output file
- */
- public static String[] audioVolume(String inputPath, float volume, String outputPath) {
- // output_volume = volume * input_volume
- String volumeCmd = "ffmpeg -i -af volume=%f";
- volumeCmd = String.format(Locale.getDefault(), volumeCmd, volume);
- return insert(volumeCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Apply 18 band of equalizer into audio
- *
- * @param inputPath input file
- * @param bandList bandList
- * @param outputPath output file
- */
- public static String[] audioEqualizer(String inputPath, List<String> bandList, String outputPath) {
- // unit: Hz gain:0-20
- /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- | 1b | 2b | 3b | 4b | 5b | 6b | 7b | 8b | 9b |
- | 65 | 92 | 131 | 185 | 262 | 370 | 523 | 740 | 1047 |
- |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- | 10b | 11b | 12b | 13b | 14b | 15b | 16b | 17b | 18b |
- | 1480 | 2093 | 2960 | 4186 | 5920 | 8372 | 11840 | 16744 | 20000 |
- |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
- String bands = String.join(":", bandList);
- String equalizerCmd = "ffmpeg -i -af superequalizer=%s -y";
- equalizerCmd = String.format(Locale.getDefault(), equalizerCmd, bands);
- return insert(equalizerCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * mux audio and video together
- *
- * @param videoFile the file of pure video
- * @param audioFile the file of pure audio
- * @param copy copy codec
- * @param muxFile output file
- * @return mux success or not
- */
- public static String[] mediaMux(String videoFile, String audioFile, boolean copy, String muxFile) {
- String mediaMuxCmd;
- if (copy) {
- mediaMuxCmd = "ffmpeg -i %s -i %s -codec copy -shortest -y %s";
- } else {
- mediaMuxCmd = "ffmpeg -i %s -i %s -shortest -y %s";
- }
- mediaMuxCmd = String.format(Locale.getDefault(), mediaMuxCmd, videoFile, audioFile, muxFile);
- return mediaMuxCmd.split(" ");
- }
- /**
- * mux audio and video together
- *
- * @param videoFile the file of pure video
- * @param audioFile the file of pure audio
- * @param copy copy codec
- * @param muxFile output file
- * @return mux success or not
- */
- public static String[] mediaMux2(String videoFile, String audioFile, boolean copy, String muxFile, float videoDelay) {
- String mediaMuxCmd;
- if (copy) {
- mediaMuxCmd = "ffmpeg -itsoffset %f -i %s -itsoffset 0 -i %s -codec copy -shortest -y %s";
- } else {
- mediaMuxCmd = "ffmpeg -itsoffset %f -i %s -itsoffset 0 -i %s -shortest -y %s";
- }
- mediaMuxCmd = String.format(Locale.getDefault(), mediaMuxCmd, videoDelay, videoFile, audioFile, muxFile);
- return mediaMuxCmd.split(" ");
- }
- public static String[] getVideoFrame(String input, String output) {
- // String mediaMuxCmd = "ffmpeg -i %s -vf fps=1/1 %simg%03d.jpg";
- String mediaMuxCmd = "ffmpeg -i %s -vf fps=1/1 %s";
- mediaMuxCmd = String.format(Locale.getDefault(), mediaMuxCmd, input, output);
- return mediaMuxCmd.split(" ");
- }
- /**
- * extract audio from media file
- *
- * @param inputPath input file
- * @param outputPath output file
- * @return demux audio success or not
- */
- public static String[] extractAudio(String inputPath, String outputPath) {
- // -vn: disable video
- // multi audio track: ffmpeg -i input.mp4 -map 0:1 -vn output.mp3
- String extractAudioCmd = "ffmpeg -i -vn";
- return insert(extractAudioCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * extract pure video from media file
- *
- * @param inputPath input file
- * @param outputPath output file
- * @return demux video success or not
- */
- public static String[] extractVideo(String inputPath, String outputPath) {
- //-an: disable audio
- String extractVideoCmd = "ffmpeg -i -vcodec copy -an";
- return insert(extractVideoCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * transform video, according to your assigning the output format
- *
- * @param inputPath input file
- * @param outputPath output file
- * @return transform video success or not
- */
- public static String[] transformVideo(String inputPath, String outputPath) {
- //just copy codec
- // String transformVideoCmd = "ffmpeg -i %s -vcodec copy -acodec copy %s";
- // assign the frameRate, bitRate and resolution
- // String transformVideoCmd = "ffmpeg -i %s -r 25 -b 200 -s 1080x720 %s";
- // assign the encoder
- // ffmpeg -i %s -vcodec libx264 -acodec libmp3lame %s
- String transformVideoCmd = "ffmpeg -i -vcodec libx264 -acodec libmp3lame";
- return insert(transformVideoCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Using FFmpeg to transform video, with re-encode
- *
- * @param inputPath the source file
- * @param outputPath target file
- * @return transform video success or not
- */
- public static String[] transformVideoWithEncode(String inputPath, String outputPath) {
- return transformVideoWithEncode(inputPath, 0, 0, outputPath);
- }
- /**
- * Using FFmpeg to transform video, with re-encode and specific resolution
- *
- * @param inputPath the source file
- * @param width the width of video
- * @param height the height of video
- * @param outputPath target file
- * @return transform video success or not
- */
- public static String[] transformVideoWithEncode(String inputPath, int width, int height, String outputPath) {
- String transformVideoCmd;
- if (width > 0 && height > 0) {
- String scale = "-vf scale=" + width + ":" + height;
- transformVideoCmd = "ffmpeg -i -vcodec libx264 -acodec aac " + scale;
- } else {
- transformVideoCmd = "ffmpeg -i -vcodec libx264 -acodec aac";
- }
- return insert(transformVideoCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * joint every single video together
- *
- * @param fileListPath the path file list
- * @param outputPath output path
- * @return joint video success or not
- */
- public static String[] jointVideo(String fileListPath, String outputPath) {
- // ffmpeg -f concat -safe 0 -i %s -c copy %s
- String jointVideoCmd = "ffmpeg -f concat -safe 0 -i -c copy -y";
- return insert(jointVideoCmd.split(" "), 6, fileListPath, outputPath);
- }
- /**
- * cut video, you could assign the startTime and duration which you want to
- *
- * @param inputPath input file
- * @param startTime startTime in the video(unit is second)
- * @param duration duration
- * @param outputPath output file
- * @return cut video success or not
- */
- public static String[] cutVideo(String inputPath, float startTime, float duration, String outputPath) {
- // -map 0 -codec copy (copy all tracks)
- // -map 0:v -vcodec copy (copy track of video)
- // -map 0:a -acodec copy (copy all tracks of audio)
- // -map 0:s -scodec copy (copy all tracks of subtitle)
- // ffmpeg -ss %f -accurate_seek -t %f -i %s -map 0 -codec copy -avoid_negative_ts 1 %s
- String cutVideoCmd = "ffmpeg -ss %f -accurate_seek -t %f -i -map 0 -codec copy -avoid_negative_ts 1";
- cutVideoCmd = String.format(Locale.getDefault(), cutVideoCmd, startTime, duration);
- return insert(cutVideoCmd.split(" "), 7, inputPath, outputPath);
- }
- /**
- * screenshot from video, you could assign the specific time
- *
- * @param inputPath input file
- * @param offset which time you want to shot
- * @param outputPath output file
- * @return screenshot success or not
- */
- public static String[] screenShot(String inputPath, float offset, String outputPath) {
- // ffmpeg -ss %f -i %s -f image2 -vframes 1 -an %s
- String screenShotCmd = "ffmpeg -ss %f -i -f image2 -vframes 1 -an";
- screenShotCmd = String.format(Locale.getDefault(), screenShotCmd, offset);
- return insert(screenShotCmd.split(" "), 4, inputPath, outputPath);
- }
- private static String obtainOverlay(int offsetX, int offsetY, int location) {
- switch (location) {
- case 2:
- return "overlay='(main_w-overlay_w)-" + offsetX + ":" + offsetY + "'";
- case 3:
- return "overlay='" + offsetX + ":(main_h-overlay_h)-" + offsetY + "'";
- case 4:
- return "overlay='(main_w-overlay_w)-" + offsetX + ":(main_h-overlay_h)-" + offsetY + "'";
- case 1:
- default:
- // move from to right
- return "overlay=(10+t*20):" + offsetY;
- }
- }
- /**
- * add watermark with image to video, you could assign the location and bitRate
- *
- * @param inputPath input file
- * @param imgPath the path of the image
- * @param location the location in the video(1:top left 2:top right 3:bottom left 4:bottom right)
- * @param bitRate bitRate
- * @param offsetXY the offset of x and y in the video
- * @param outputPath output file
- * @return add watermark success or not
- */
- public static String[] addWaterMarkImg(String inputPath, String imgPath, int location, int bitRate,
- int offsetXY, String outputPath) {
- String mBitRate = bitRate + "k";
- String overlay = obtainOverlay(offsetXY, offsetXY, location);
- String waterMarkCmd = "ffmpeg -i -i -b:v %s -filter_complex %s -preset:v superfast -y";
- waterMarkCmd = String.format(waterMarkCmd, mBitRate, overlay);
- return insert(waterMarkCmd.split(" "), 2, inputPath, 4, imgPath, outputPath);
- }
- /**
- * add watermark with gif to video, you could assign the location and bitRate
- *
- * @param inputPath input file
- * @param imgPath the path of the gif
- * @param location the location in the video(1:top left 2:top right 3:bottom left 4:bottom right)
- * @param bitRate bitRate
- * @param offsetXY the offset of x and y in the video
- * @param outputPath output file
- * @return add watermark success or not
- */
- public static String[] addWaterMarkGif(Context context, String inputPath, String imgPath, int location, int bitRate,
- int offsetXY, String outputPath) {
- String mBitRate = bitRate + "k";
- int offset = ScreenUtil.INSTANCE.dp2px(context, offsetXY);
- String overlay = obtainOverlay(offset, offset, location) + ":shortest=1";
- String waterMarkCmd = "ffmpeg -i -ignore_loop 0 -i -b:v %s -filter_complex %s -preset:v superfast";
- waterMarkCmd = String.format(waterMarkCmd, mBitRate, overlay);
- return insert(waterMarkCmd.split(" "), 2, inputPath, 6, imgPath, outputPath);
- }
- /**
- * Remove watermark from video: Suppress logo by a simple interpolation of the surrounding pixels.
- * On the other hand, it can be used to mosaic video
- *
- * @return delogo cmd
- */
- public static String[] removeLogo(String inputPath, int x, int y, int width, int height, String outputPath) {
- // ffmpeg -i in.mp4 -filter_complex delogo=x=%d:y=%d:w=%d:h=%d out.mp4
- String delogoCmd = "ffmpeg -i -filter_complex delogo=x=%d:y=%d:w=%d:h=%d";
- delogoCmd = String.format(Locale.getDefault(), delogoCmd, x, y, width, height);
- return insert(delogoCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * generate a palette for gif
- *
- * @param inputPath input file
- * @param frameRate frameRate of the gif
- * @param startTime startTime in the video
- * @param duration duration, how long you want to
- * @param width width
- * @param outputPath output file
- * @return generate palette success or not
- */
- public static String[] generatePalette(String inputPath, int startTime, int duration,
- int frameRate, int width, String outputPath) {
- String paletteCmd = "ffmpeg -ss %d -accurate_seek -t %d -i -vf fps=%d,scale=%d:-1:flags=lanczos,palettegen";
- paletteCmd = String.format(Locale.getDefault(), paletteCmd, startTime,
- duration, frameRate, width);
- return insert(paletteCmd.split(" "), 7, inputPath, outputPath);
- }
- /**
- * convert video into gif with palette
- *
- * @param inputPath input file
- * @param palette the palette which will apply to gif
- * @param startTime startTime in the video
- * @param duration duration, how long you want to
- * @param frameRate frameRate of the gif
- * @param width width
- * @param outputPath output gif
- * @return convert gif success or not
- */
- public static String[] generateGifByPalette(String inputPath, String palette, int startTime, int duration,
- int frameRate, int width, String outputPath) {
- String paletteGifCmd = "ffmpeg -ss %d -accurate_seek -t %d -i -i -lavfi fps=%d,scale=%d:-1:flags=lanczos[x];[x][1:v]" +
- "paletteuse=dither=bayer:bayer_scale=3";
- paletteGifCmd = String.format(Locale.getDefault(), paletteGifCmd, startTime,
- duration, frameRate, width);
- return insert(paletteGifCmd.split(" "), 7, inputPath, 9, palette, outputPath);
- }
- /**
- * convert s series of pictures into video
- *
- * @param inputPath input file
- * @param frameRate frameRate
- * @param outputPath output file
- * @return convert success or not
- */
- public static String[] pictureToVideo(String inputPath, int frameRate, String outputPath) {
- //-f: stand for format
- String combineVideo = "ffmpeg -f image2 -r %d -i %simg#d.jpg -vcodec mpeg4 %s";
- combineVideo = String.format(Locale.getDefault(), combineVideo, frameRate, inputPath, outputPath);
- combineVideo = combineVideo.replace("#", "%");
- return combineVideo.split(" ");
- }
- /**
- * convert resolution
- *
- * @param inputPath input file
- * @param resolution resolution
- * @param outputPath output file
- * @return convert success or not
- */
- public static String[] convertResolution(String inputPath, String resolution, String outputPath) {
- String convertCmd = "ffmpeg -i -s %s -pix_fmt yuv420p";
- convertCmd = String.format(Locale.getDefault(), convertCmd, resolution);
- return insert(convertCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * encode audio, you could assign the sampleRate and channel
- *
- * @param inputPath pcm raw audio
- * @param outputPath output file
- * @param sampleRate sampleRate
- * @param channel sound channel: mono channel is 1, stereo channel is 2
- * @return encode audio success or not
- */
- public static String[] encodeAudio(String inputPath, String outputPath, int sampleRate, int channel) {
- String encodeAudioCmd = "ffmpeg -f s16le -ar %d -ac %d -i";
- encodeAudioCmd = String.format(Locale.getDefault(), encodeAudioCmd, sampleRate, channel);
- return insert(encodeAudioCmd.split(" "), 8, inputPath, outputPath);
- }
- /**
- * join multi videos together
- *
- * @param input1 input one
- * @param input2 input two
- * @param videoLayout the layout of video, which could be horizontal or vertical
- * @param outputPath output file
- * @return join success or not
- */
- public static String[] multiVideo(String input1, String input2, String outputPath, int videoLayout) {
- String multiVideo = "ffmpeg -i -i -filter_complex hstack";//hstack: horizontal
- if (videoLayout == VideoLayout.LAYOUT_VERTICAL) {//vstack: vertical
- multiVideo = multiVideo.replace("hstack", "vstack");
- }
- return insert(multiVideo.split(" "), 2, input1, 4, input2, outputPath);
- }
- /**
- * reverse video
- *
- * @param inputPath input file
- * @param outputPath output file
- * @return reverse success or not
- */
- public static String[] reverseVideo(String inputPath, String outputPath) {
- //-vf reverse: only video reverse, -an: disable audio
- //tip: reverse will cost a lot of time, only short video are recommended
- String reverseVideo = "ffmpeg -i -vf reverse -an";
- return insert(reverseVideo.split(" "), 2, inputPath, outputPath);
- }
- /**
- * noise reduction with video
- *
- * @param inputPath input file
- * @param outputPath output file
- * @return noise reduction success or not
- */
- public static String[] denoiseVideo(String inputPath, String outputPath) {
- String denoiseVideo = "ffmpeg -i -nr 500";
- return insert(denoiseVideo.split(" "), 2, inputPath, outputPath);
- }
- /**
- * covert video into a series of pictures
- *
- * @param inputPath input file
- * @param startTime startTime in the video
- * @param duration duration, how long you want
- * @param frameRate frameRate
- * @param outputPath output file
- * @return convert success or not
- */
- public static String[] videoToImage(String inputPath, int startTime, int duration, int frameRate, String outputPath) {
- return videoToImageWithScale(inputPath, startTime, duration, frameRate, 0, outputPath);
- }
- public static String[] videoToImageWithScale(String inputPath, int startTime, int duration,
- int frameRate, int width, String outputPath) {
- String toImage;
- if (width > 0) {
- toImage = "ffmpeg -ss %d -accurate_seek -t %d -i %s -an -vf fps=%d,scale=%d:-1 %s";
- toImage = String.format(Locale.CHINESE, toImage, startTime, duration, inputPath, frameRate, width, outputPath);
- } else {
- toImage = "ffmpeg -ss %d -accurate_seek -t %d -i %s -an -r %d %s";
- toImage = String.format(Locale.CHINESE, toImage, startTime, duration, inputPath, frameRate, outputPath);
- }
- toImage = toImage + "%3d.png";
- return toImage.split(" ");
- }
- /**
- * convert videos into picture-in-picture mode
- *
- * @param inputPath1 input one
- * @param inputPath2 input two
- * @param outputPath output file
- * @param x x coordinate point
- * @param y y coordinate point
- * @return convert success or not
- */
- public static String[] picInPicVideo(String inputPath1, String inputPath2, int x, int y, String outputPath) {
- String pipVideo = "ffmpeg -i -i -filter_complex overlay=%d:%d";
- pipVideo = String.format(Locale.getDefault(), pipVideo, x, y);
- return insert(pipVideo.split(" "), 2, inputPath1, 4, inputPath2, outputPath);
- }
- /**
- * convert videos into picture-in-picture mode
- *
- * @param whichCorner 0: top-left; 1: top-right; 2: bottom-left; 3: bottom-right
- * @return convert success or not
- */
- public static String[] picInPicVideoInCorner(String inputPath1, String inputPath2, int whichCorner, String outputPath) {
- String pipCmd = "ffmpeg -i -i -filter_complex ";
- switch (whichCorner) {
- case 1:
- pipCmd += "overlay=W-w";
- break;
- case 2:
- pipCmd += "overlay=0:H-h";
- break;
- case 3:
- pipCmd += "overlay=W-w:H-h";
- break;
- case 0:
- default:
- pipCmd += "overlay";
- break;
- }
- return insert(pipCmd.split(" "), 2, inputPath1, 4, inputPath2, outputPath);
- }
- /**
- * move moov box in the front of mdat box, when moox box is behind mdat box(only mp4)
- *
- * @param inputPath inputFile
- * @param outputPath outputFile
- * @return move success or not
- */
- public static String[] moveMoovAhead(String inputPath, String outputPath) {
- String moovCmd = "ffmpeg -i -movflags faststart -acodec copy -vcodec copy";
- return insert(moovCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Change Video from RGB to gray(black & white)
- *
- * @param inputPath inputPath
- * @param outputPath outputPath
- * @return grayCmd
- */
- public static String[] toGrayVideo(String inputPath, String outputPath) {
- String grayCmd = "ffmpeg -i -vf lutyuv='u=128:v=128'";
- return insert(grayCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Photo zoom to video
- *
- * @param inputPath inputPath
- * @param position position
- * 0:center
- * 1:left to right
- * 2:right to left
- * 3:down to up
- * 4:up to down
- * @param outputPath outputPath
- * @return zoomCmd
- */
- public static String[] photoZoomToVideo(String inputPath, int position, String outputPath) {
- String zoomCmd = "ffmpeg -i -vf ";
- switch (position) {
- case 1:
- zoomCmd += "zoompan='1.3':x='if(lte(on,1),(iw/zoom)/2,x-0.5)':y='if(lte(on,1),(ih-ih/zoom)/2,y)':d=180";
- break;
- case 2:
- zoomCmd += "zoompan='1.3':x='if(lte(on,-1),(iw-iw/zoom)/2,x+0.5)':y='if(lte(on,1),(ih-ih/zoom)/2,y)':d=180";
- break;
- case 3:
- zoomCmd += "zoompan='1.3':x='if(lte(on,1),(iw-iw/zoom)/2,x)':y='if(lte(on,-1),(ih-ih/zoom)/2,y+0.5)':d=180";
- break;
- case 4:
- zoomCmd += "zoompan='1.3':x='if(lte(on,1),(iw-iw/zoom)/2,x)':y='if(lte(on,1),(ih/zoom)/2,y-0.5)':d=180";
- break;
- case 0:
- default:
- zoomCmd += "zoompan=z='min(zoom+0.0015,1.5)':x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':d=180";
- break;
- }
- return insert(zoomCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * using FFprobe to parse the media format
- *
- * @param inputPath inputFile
- * @return probe success or not
- */
- public static String[] probeFormat(String inputPath) {
- // ffprobe -i hello.mp4 -show_streams -show_format -print_format json"
- String ffprobeCmd = "ffprobe -i -show_streams -show_format -print_format json";
- return insert(ffprobeCmd.split(" "), 2, inputPath);
- }
- /**
- * Changing the speed of playing, speed range at 0.5-2 in audio-video mode.
- * However, in pure video mode, the speed range at 0.25-4
- *
- * @param inputPath the inputFile of normal speed
- * @param outputPath the outputFile which you want to change speed
- * @param speed speed of playing
- * @param pureVideo whether pure video or not, default false
- * @return change speed success or not
- */
- public static String[] changeSpeed(String inputPath, String outputPath, float speed, boolean pureVideo) {
- //audio atempo: 0.5--2
- //video pts:0.25--4
- if (pureVideo) {
- if (speed > 4 || speed < 0.25) {
- throw new IllegalArgumentException("speed range is 0.25--4");
- }
- } else {
- if (speed > 2 || speed < 0.5) {
- throw new IllegalArgumentException("speed range is 0.5--2");
- }
- }
- float ptsFactor = 1 / speed;
- String speedCmd;
- if (pureVideo) {
- speedCmd = "ffmpeg -i -filter_complex [0:v]setpts=%.2f*PTS[v] -map [v]";
- speedCmd = String.format(Locale.getDefault(), speedCmd, ptsFactor);
- } else {
- speedCmd = "ffmpeg -i -filter_complex [0:v]setpts=%.2f*PTS[v];[0:a]atempo=%.2f[a] -map [v] -map [a]";
- speedCmd = String.format(Locale.getDefault(), speedCmd, ptsFactor, speed);
- }
- return insert(speedCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Changing the speed of playing, speed range at 0.5-2 in audio.
- *
- * @param inputPath the inputFile of normal speed
- * @param outputPath the outputFile which you want to change speed
- * @param speed speed of playing
- * @return change speed success or not
- */
- public static String[] changeAudioSpeed(String inputPath, String outputPath, float speed) {
- // atempo range [0.5, 100.0]
- if (speed > 100 || speed < 0.5) {
- throw new IllegalArgumentException("audio speed range is from 0.5 to 100");
- }
- String speedCmd = "ffmpeg -i -filter_complex atempo=%.2f -c:a copy -b:a 192k";
- speedCmd = String.format(Locale.getDefault(), speedCmd, speed);
- return insert(speedCmd.split(" "), 2, inputPath, outputPath);
- }
- public static String[] changeAudioSpeed2(String inputPath, String outputPath, float speed) {
- // atempo range [0.5, 100.0]
- if (speed > 100) {
- throw new IllegalArgumentException("audio speed range is from 0.5 to 100");
- }
- if (speed < 0.5) {
- String s = buildAtempoFilters(speed);
- String command = "ffmpeg -i -filter:a %s -b:a 192k";
- String format = String.format(Locale.getDefault(), command, s);
- return insert(format.split(" "), 2, inputPath, outputPath);
- } else {
- String speedCmd = "ffmpeg -i -filter:a atempo=%.2f -b:a 192k";
- speedCmd = String.format(Locale.getDefault(), speedCmd, speed);
- return insert(speedCmd.split(" "), 2, inputPath, outputPath);
- }
- }
- public static String[] changeAudioSpeed3(String inputPath, String outputPath, float speed) {
- if (speed > 100.0F) {
- throw new IllegalArgumentException("audio speed range is from 0.5 to 100");
- } else {
- String speedCmd;
- if ((double)speed < 0.5) {
- speedCmd = buildAtempoFilters((double)speed);
- String command = "ffmpeg -i -filter:a %s -b:a 192k -ar 44100 -ac 2 -acodec pcm_s16le";
- String format = String.format(Locale.getDefault(), command, speedCmd);
- return insert(format.split(" "), 2, inputPath, outputPath);
- } else {
- speedCmd = "ffmpeg -i -filter:a atempo=%.2f -b:a 192k -ar 44100 -ac 2 -acodec pcm_s16le";
- speedCmd = String.format(Locale.getDefault(), speedCmd, speed);
- return insert(speedCmd.split(" "), 2, inputPath, outputPath);
- }
- }
- }
- private static String buildAtempoFilters(double speed) {
- StringBuilder filters = new StringBuilder();
- double tempSpeed = speed;
- // 分解速度调整,FFmpeg的atempo值只能在0.5到2.0之间
- while (tempSpeed < 0.5) {
- filters.append("atempo=0.5,");
- tempSpeed *= 2;
- }
- filters.append(String.format("atempo=%.2f", tempSpeed));
- return filters.toString();
- }
- /**
- * Insert the picture into the header of video, which as a thumbnail
- *
- * @param inputPath inputFile
- * @param picturePath the path of thumbnail
- * @param outputPath targetFile
- * @return command of inserting picture
- */
- public static String[] insertPicIntoVideo(String inputPath, String picturePath, String outputPath) {
- String insertPicCmd = "ffmpeg -i -i -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic";
- return insert(insertPicCmd.split(" "), 2, inputPath, 4, picturePath, outputPath);
- }
- public static String[] addSubtitleIntoVideo(String inputPath, String subtitlePath, String outputPath) {
- String subtitleCmd = "ffmpeg -i -i -map 0:v -map 0:a -map 1:s -c copy";
- return insert(subtitleCmd.split(" "), 2, inputPath, 4, subtitlePath, outputPath);
- }
- /**
- * Using one input file to push multi streams.
- * After publish the streams, you could use VLC to play it
- * Note: if stream is rtmp protocol, need to start your rtmp server
- * Note: if stream is http protocol, need to start your http server
- *
- * @param inputPath inputFile
- * @param duration how long of inputFile you want to publish
- * @param streamUrl1 the url of stream1
- * @param streamUrl2 the url of stream2
- * @return command of build flv index
- */
- public static String[] pushMultiStreams(String inputPath, int duration, String streamUrl1, String streamUrl2) {
- //ffmpeg -i what.mp4 -vcodec libx264 -acodec aac -t 60 -f flv
- //"tee:rtmp://192.168.1.102/live/stream1|rtmp://192.168.1.102/live/stream2"
- String format = "flv";
- if (streamUrl1.startsWith("rtmp://")) {//rtmp protocol
- format = "flv";
- } else if (streamUrl1.startsWith("http://")) {//http protocol
- format = "mpegts";
- }
- String pushStreams = "ffmpeg -i %s -vcodec libx264 -acodec aac -t %d -f %s \"tee:%s|%s\"";
- pushStreams = String.format(Locale.getDefault(), pushStreams, inputPath, duration, format, streamUrl1, streamUrl2);
- return pushStreams.split(" ");
- }
- public static String[] rotateVideo(String inputPath, int rotateDegree, String outputPath) {
- String rotateCmd = "ffmpeg -i -c copy -metadata:s:v:0 rotate=%d";
- rotateCmd = String.format(Locale.getDefault(), rotateCmd, rotateDegree);
- return insert(rotateCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Trim one or more segments from a single video
- *
- * @param inputPath the path of input file
- * @param start1 start time of the first segment
- * @param end1 end time of the first segment
- * @param start2 start time of the second segment
- * @param end2 end time of the second segment
- * @param outputPath the path of output file
- * @return the command of trim video
- */
- public static String[] trimVideo(String inputPath, int start1, int end1, int start2, int end2, String outputPath) {
- String trimCmd = "ffmpeg -i -filter_complex " +
- "[0:v]trim=start=%d:end=%d,setpts=PTS-STARTPTS[v0];" +
- "[0:a]atrim=start=%d:end=%d,asetpts=PTS-STARTPTS[a0];" +
- "[0:v]trim=start=%d:end=%d,setpts=PTS-STARTPTS[v1];" +
- "[0:a]atrim=start=%d:end=%d,asetpts=PTS-STARTPTS[a1];" +
- "[v0][a0][v1][a1]concat=n=2:v=1:a=1[out] -map [out]";
- trimCmd = String.format(Locale.getDefault(), trimCmd, start1, end1, start1, end1, start2, end2, start2, end2);
- return insert(trimCmd.split(" "), 2, inputPath, outputPath);
- }
- public static String[] showAudioWaveform(String inputPath, String resolution, int splitChannels, String outputPath) {
- String waveformCmd = "ffmpeg -i -filter_complex showwavespic=s=%s:split_channels=%d";
- waveformCmd = String.format(Locale.getDefault(), waveformCmd, resolution, splitChannels);
- return insert(waveformCmd.split(" "), 2, inputPath, outputPath);
- }
- /**
- * Convert video into stereo3d mode(VR video)
- *
- * @param inputPath inputPath
- * @param outputPath outputPath
- * @return the command of stereo3d
- */
- public static String[] videoStereo3D(String inputPath, String outputPath) {
- /* *
- * sbsl: side by side parallel (left eye left, right eye right)
- * sbsr: side by side crosseye (right eye left, left eye right)
- * abl : above-below (left eye above, right eye below)
- * al : alternating frames (left eye first, right eye second)
- * irl : interleaved rows (left eye has top row, right eye next)
- * icl : interleaved columns, left eye first
- */
- String stereo3dCmd = "ffmpeg -i -filter_complex stereo3d=sbsl:arbg";
- return insert(stereo3dCmd.split(" "), 2, inputPath, outputPath);
- }
- public static String[] videoTransition(String inputPath1, int width, int height, int offset, String inputPath2, String outputPath) {
- String transitionCmd = "ffmpeg -i -i -filter_complex " +
- "[0]settb=AVTB,fps=24000/1001[v0];[1]settb=AVTB,fps=24000/1001,scale=%d:%d[v1];" +
- "[v0][v1]xfade=transition=radial:duration=1:offset=%d";
- transitionCmd = String.format(Locale.getDefault(), transitionCmd, width, height, offset);
- return insert(transitionCmd.split(" "), 2, inputPath1, 4, inputPath2, outputPath);
- }
- }
|