安卓ffmpeg视频剪辑

3
我正在尝试使用FFmpeg在我的Android应用程序中编辑一些视频,但是当我尝试仅截取视频的一部分时遇到了一些问题。
我正在使用这个FFmpeg编译/库https://github.com/WritingMinds/ffmpeg-android-java 我尝试使用以下命令来剪辑/切割视频。
"-y -i input.mp4 -ss 00:00:01.00 -t 00:00:15.000 -c copy output.mp4"

但是有些视频会出现黑屏或者视频冻结的情况。在我的所有测试中,声音都正常工作。
使用这个命令:
"-y -i input.mp4 -ss 00:00:01.000 -t 00:00:15.000 -async 1 output.mp4"

在我所有的测试中(视频/音频),一切都正常,但生成输出文件需要太长时间,这个15秒的文件需要超过2分钟才能生成。
我尝试了这些命令的其他变体,但总是会遇到视频流的问题(黑屏或冻结),或者生成输出需要太长时间(用户只能选择发送到我的服务器的15秒视频)。
感谢您的帮助!
编辑:
这是修剪视频的代码部分。
String[] cmd = new String[]{"-y","-i",input,"-ss","00:00:05.000","-vcodec","copy",
            "-acodec","copy","-t","00:00:15.00","-strict","-2",output };

    final FFmpeg ffmpeg = FFmpeg.getInstance(this);
    try {
        ffmpeg.execute(cmd, new FFmpegExecuteResponseHandler() {
            @Override
            public void onSuccess(String message) {
                Log.i("VideoEditActivity", "Success " + message);
                is_video_generated_ = true;
            }

            @Override
            public void onProgress(String message) {
                Log.i("VideoEditActivity", "Progress updated " + message);
            }

            @Override
            public void onFailure(String message) {
                Log.e("VideoEditActivity", "ERROR! " + message);
            }

            @Override
            public void onStart() {
                progress_dialog_.setMessage(getString(R.string.str_video_generating));
                progress_dialog_.show();
            }

            @Override
            public void onFinish() {
                Log.i("VideoEditActivity", "Finished");
                progress_dialog_.hide();

                Intent intent = new Intent(getApplicationContext(), VideoPlayActivity.class);
                intent.putExtra("media", edited_video_path_);
                startActivity(intent);
            }
        });
    } catch (FFmpegCommandAlreadyRunningException e) {
        e.printStackTrace();
    }

这是原始文件:[https://drive.google.com/file/d/0BzqJL_nNetbRYmxvcTljanJwR00/view?usp=sharing][1] 这是输出结果:0BzqJL_nNetbReENjRGMtVXQ5VHM/view?usp=sharing(stackoverflow不允许我添加超过2个链接)

当进行流复制时,视频只能在关键帧处进行裁剪。因此,如果您的-ss12而关键帧在10,则输出视频将被冻结/黑屏直到下一个关键帧出现。 - Gyan
好的,改变-ss的顺序将修复视频帧的开始(https://trac.ffmpeg.org/wiki/Seeking),但是有没有一种简单的方法在关键帧处裁剪视频的结尾(即使视频会比我需要的稍微大一些)?我读了一些相关内容,有些人正在使用ffprobe获取关键帧时间戳,但我今天在我的Android构建中没有它。 - Raupp
4个回答

6

我曾经面临同样的问题,在苦苦探索之后,我找到了一个完美的解决方案。

 /**
 * Command for cutting video
 */
private void executeCutVideoCommand(long startMs, long endMs) {

    String destPath = "/storage/emulated/0/DCIM/test/";//Replace ypur dest Path
    File externalStoragePublicDirectory = new File(destPath);
    if (!externalStoragePublicDirectory.exists() ? externalStoragePublicDirectory.mkdir() : true) {
        String yourRealPath = "YOUR INPUT PATH";//getPath(MainActivity.this, selectedVideoUri); 
        String filePrefix = yourRealPath.substring(yourRealPath.lastIndexOf("."));
        String destFileName = "cut_video";
        boolean isFastMode = false;
        File dest = (filePrefix.equals(".webm") || filePrefix.equals(".mkv")) ? new File(externalStoragePublicDirectory, destFileName + ".mp4") : new File(externalStoragePublicDirectory, destFileName + filePrefix);
        int fileNo = 0;
        while (dest.exists()) {
            fileNo++;
            dest = (filePrefix.equals(".webm") || filePrefix.equals(".mkv")) ? new File(externalStoragePublicDirectory, destFileName + fileNo + ".mp4") : new File(externalStoragePublicDirectory, destFileName + fileNo + filePrefix);
        }
        Log.d(TAG, "startTrim: src: " + yourRealPath);
        Log.d(TAG, "startTrim: dest: " + dest.getAbsolutePath());
        Log.d(TAG, "startTrim: startMs: " + startMs);
        Log.d(TAG, "startTrim: endMs: " + endMs);
        filePath = dest.getAbsolutePath();
        final String[] complexCommand = isFastMode ?
                (filePrefix.equals(".webm") || filePrefix.equals(".mkv") || filePrefix.equals(".m4v") || filePrefix.equals(".mov")) ?
                        new String[]{"-ss", "" + (startMs / 1000), "-y", "-i", yourRealPath, "-preset", "ultrafast", "-t", "" + ((endMs - startMs) / 1000), "-vcodec", "mpeg4", "-b:v", "2097152", "-b:a", "48000", "-ac", "2", "-ar", "22050", "-strict", "-2", filePath}
                        : new String[]{"-y", "-i", yourRealPath, "-preset", "ultrafast", "-ss", "" + (startMs / 1000), "-t", "" + ((endMs - startMs) / 1000), "-c", "copy", filePath}
                : (filePrefix.equals(".webm") || filePrefix.equals(".mkv") || filePrefix.equals(".m4v") || filePrefix.equals(".mov")) ?
                new String[]{"-ss", "" + (startMs / 1000), "-y", "-i", yourRealPath, "-t", "" + ((endMs - startMs) / 1000), "-vcodec", "mpeg4", "-b:v", "2097152", "-b:a", "48000", "-ac", "2", "-ar", "22050", "-strict", "-2", filePath} :
                new String[]{"-y", "-i", yourRealPath, "-ss", "" + (startMs / 1000), "-t", "" + ((endMs - startMs) / 1000), "-c", "copy", filePath};
        execFFmpegBinary(complexCommand);
    }
}

其中 execFFmpegBinary(complexCommand) 是:

 private void execFFmpegBinary(final String[] command) {
    try {
        ffmpeg.execute(command, new ExecuteBinaryResponseHandler() {
            @Override
            public void onFailure(String s) {
                Log.d(TAG, "FAILED with output : " + s);
            }

            @Override
            public void onSuccess(String s) {
                Log.d(TAG, "SUCCESS with output : " + s);

            }

            @Override
            public void onProgress(String s) {
                Log.d(TAG, "Started command : ffmpeg " + command);

            }

            @Override
            public void onStart() {
                Log.d(TAG, "Started command : ffmpeg " + command);

            }

            @Override
            public void onFinish() {
                Log.d(TAG, "Finished command : ffmpeg " + command);


            }
        });
    } catch (FFmpegCommandAlreadyRunningException e) {
        // do nothing for now
    }
}

1
你是否使用过像 https://github.com/bravobit/FFmpeg-Android 这样的 FFmpeg 库?还是自己构建整个 FFmpeg? - Quantum_VC
1
@Quantum_VC 只使用了 Android 库。 - Muhamed Riyas M
@MuhamedRiyasM 这适用于 MP4 文件格式吗? - Qadir Hussain

3

虽然有些晚了,但这可能会帮助未来的人们。我在使用FFMPEG中的修剪功能时遇到了很多问题,特别是在处理准确性和速度方面。我找到的最佳解决方案是以下命令。

原始答案: "最初的回答"

-ss 00:00:00.00 -t 00:00:00.00 -noaccurate_seek -i input.mp4 -codec copy -avoid_negative_ts 1 output.mp4

这是一个关于此命令的解释:
  • -i: 输入文件
  • -ss: 在输入文件之前放置此选项,以便在输入文件中快速查找到此位置。
  • -t: 命令将要剪切到的位置。
  • -noaccurate_seek: 保留-ss和下一个关键帧之间的所有帧,否则它们将被默认丢弃。
  • -codec copy: 复制视频和音频编解码器,以提高速度。
  • -avoid_negative_ts 1: 将第一个时间戳设置为1,并避免它变成负数。
  • 输出文件是命令的最后一部分。
经过多次测试,这是最快、最准确的命令,没有黑屏、遗漏帧或执行时间太长的问题。

我在这个命令中遇到了错误,无法找到适合的输出格式为“-avoid_negative_ts”。 -avoid_negative_ts:无效参数。 - Krutika Chotara
这个解决方案运行良好。 - undefined

1
尝试:
-y -i -ss 00:00:00.00 input_path -vcodec copy -acodec copy -t 00:00:00.00 -strict -2 output_path

-ss 是开始时间,-t 是持续时间。HH:MM:SS.SS

它非常快,可以在不重新编码的情况下进行修剪。

我尝试了这个方法,效果好了一些,但是仍然有一些视频存在问题。在进一步研究后,我发现几乎所有出现问题的文件都是通过WhatsAPP处理过的视频,而不是用户自己录制的。你有任何想法为什么会出现这种情况吗? - Raupp
你能提供一些代码吗?你必须严格按照上述命令行顺序执行。顺便问一下,这些视频是什么格式的? - Juan Felippo
我编辑了我的问题并附上了源代码和命令,还添加了原始文件和输出文件的链接。感谢您的帮助! - Raupp

0
请尝试使用以下带有开始和结束点的ffmpeg命令进行剪辑:
fun trim(): Array<String?>{
        val cmdList = ArrayList<String>()
        var cmds: Array<String?>? = null
        try{
            cmdList.add("-y")

            cmdList.add("-ss")
            cmdList.add(startduration.toString())
            cmdList.add("-t")
            cmdList.add(endduration.toString())
            cmdList.add("-i")
            cmdList.add(sourcevideopath)
            cmdList.add("-vcodec")
            cmdList.add("copy")

            cmdList.add("-movflags")
            cmdList.add("faststart")
            cmdList.add("-preset")
            cmdList.add("ultrafast")

            cmdList.add(outputvideopath)

            cmds = arrayOfNulls<String>(cmdList.size)
            cmdList.toArray(cmds)

        }catch (e:Exception){
            e.printStackTrace()
        }
        return cmds!!
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接