Skip to content

Instantly share code, notes, and snippets.

@shaobin0604
Last active November 9, 2022 07:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shaobin0604/8e73e4eeceeb105566b761ba0df517fe to your computer and use it in GitHub Desktop.
Save shaobin0604/8e73e4eeceeb105566b761ba0df517fe to your computer and use it in GitHub Desktop.
Mix Video with vocal audio and music audio using ffmpeg
package com.arthenica.ffmpegkit.test;
import static com.arthenica.ffmpegkit.test.MainActivity.TAG;
import static com.arthenica.ffmpegkit.test.MainActivity.notNull;
import android.os.Bundle;
import android.os.Environment;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.ffmpegkit.FFmpegKit;
import com.arthenica.ffmpegkit.ReturnCode;
import java.io.File;
public class SlimScreenRecordTabFragment extends Fragment {
private static final String VOCAL_AUDIO_URL = "https://example.com/audio/vocal.mp3";
private static final String MUSIC_AUDIO_URL = "https://example.com/audio/music.mp3";
private static final String VIDEO_FILE_NAME = "record-1667288804378.ts";
private TextView outputText;
public static SlimScreenRecordTabFragment newInstance() {
return new SlimScreenRecordTabFragment();
}
public SlimScreenRecordTabFragment() {
super(R.layout.fragment_slim_screen_record_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button runButton = view.findViewById(R.id.runButton);
runButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
runTest();
}
});
outputText = view.findViewById(R.id.outputText);
outputText.setMovementMethod(new ScrollingMovementMethod());
}
private void runTest() {
clearOutput();
final File sdcardRoot = Environment.getExternalStorageDirectory();
final String inVideoPath = new File(sdcardRoot, VIDEO_FILE_NAME).getAbsolutePath();
final String outVideoPath = new File(sdcardRoot, "out.mp4").getAbsolutePath();
final String ffmpegCommand = String.format("-hide_banner -y "
+ "-i \"%s\" "
+ "-i \"%s\" "
+ "-i \"%s\" "
+ "-filter_complex \"[1:a][2:a]amix=inputs=2:duration=longest:dropout_transition=0:weights='1 0.5':normalize=0[a]\" "
+ "-map \"0:v\" -map \"[a]\" "
+ "-c:v \"copy\" -c:a \"aac\" "
+ "-f \"mp4\" \"%s\"", inVideoPath, VOCAL_AUDIO_URL, MUSIC_AUDIO_URL, outVideoPath);
Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, session -> {
Log.d(TAG, String.format("FFmpeg process exited with state %s and rc %s.%s", session.getState(),
session.getReturnCode(), notNull(session.getFailStackTrace(), "\n")));
MainActivity.addUIAction(() -> {
if (ReturnCode.isSuccess(session.getReturnCode())) {
Popup.show(requireContext(), "ffmpeg completed successfully.");
} else {
Popup.show(requireContext(), "ffmpeg failed. Please check logs for the details.");
}
});
}, log -> MainActivity.addUIAction(() -> {
appendOutput(log.getMessage());
}), statistics -> {
Log.d(TAG, statistics.toString());
});
}
public void appendOutput(final String logMessage) {
outputText.append(logMessage);
}
public void clearOutput() {
outputText.setText("");
}
}
# command on desktop
ffmpeg \
-i <video mp4> \
-i <vocal mp3> \
-i <background music mp3> \
-filter_complex "[1:a][2:a]amix=inputs=2:duration=longest:dropout_transition=0:weights='1 0.5':normalize=0[a]" \
-map "0:v" -map "[a]" \
-c:v "copy" -c:a "aac" \
-f "mp4" "out.mp4"
# see:
# - https://stackoverflow.com/questions/14498539/how-to-overlay-downmix-two-audio-files-using-ffmpeg
# - https://blog.nytsoi.net/2017/12/31/ffmpeg-combining-audio-tracks
# - https://gist.github.com/LukasKnuth/4d7ee6812ccb221b2775e984516b56d4
@shaobin0604
Copy link
Author

4分12 秒 1080p 30fps 视频,文件大小:107M
ffmpeg 处理耗时:15秒,CPU 占用:100%(即把一核占满),内存 约1%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment