Untitled
unknown
plain_text
a year ago
9.2 kB
5
Indexable
package com.sangdev.miniproject; import android.media.MediaCodec; import android.media.MediaExtractor; import android.media.MediaFormat; import android.media.MediaMuxer; import android.os.Build; import android.util.Log; import androidx.annotation.RequiresApi; import java.io.IOException; import java.nio.ByteBuffer; import java.util.logging.Logger; public class VideoMerger { private static int mVideoTrackIndex1 = -1; private static int mAudioTrackIndex1 = -1; private static int mVideoTrackIndex2 = -1; private static int mAudioTrackIndex2 = -1; private static final String TAG = "VideoMerger"; private static final Logger logger = Logger.getLogger(VideoMerger.class.getName()); public static void mergeVideos(String filePath1, String filePath2, String outputFile) throws IOException { MediaMuxer mediaMuxer = null; MediaExtractor videoExtractor1 = new MediaExtractor(); MediaExtractor videoExtractor2 = new MediaExtractor(); try { mediaMuxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); // Trích xuất và ghi track video từ video đầu tiên videoExtractor1.setDataSource(filePath1); int videoTrackIndex1 = selectTrack(videoExtractor1, "video/"); MediaFormat videoFormat1 = videoExtractor1.getTrackFormat(videoTrackIndex1); videoExtractor1.selectTrack(videoTrackIndex1); int muxerVideoTrackIndex1 = mediaMuxer.addTrack(videoFormat1); // Tuỳ chọn, trích xuất và ghi track audio từ video đầu tiên int audioTrackIndex1 = selectTrack(videoExtractor1, "audio/"); int muxerAudioTrackIndex = -1; if (audioTrackIndex1 >= 0) { MediaFormat audioFormat1 = videoExtractor1.getTrackFormat(audioTrackIndex1); videoExtractor1.selectTrack(audioTrackIndex1); muxerAudioTrackIndex = mediaMuxer.addTrack(audioFormat1); } else { // Add silent audio track if no audio track is found MediaFormat silentAudioFormat = createSilentAudioFormat(); muxerAudioTrackIndex = mediaMuxer.addTrack(silentAudioFormat); writeSilentAudioData(mediaMuxer, muxerAudioTrackIndex, videoFormat1); } // Trích xuất và ghi track video từ video thứ hai videoExtractor2.setDataSource(filePath2); int videoTrackIndex2 = selectTrack(videoExtractor2, "video/"); MediaFormat videoFormat2 = videoExtractor2.getTrackFormat(videoTrackIndex2); videoExtractor2.selectTrack(videoTrackIndex2); int muxerVideoTrackIndex2 = mediaMuxer.addTrack(videoFormat2); // Trích xuất và ghi track audio từ video thứ hai int audioTrackIndex2 = selectTrack(videoExtractor2, "audio/"); int muxerAudioTrackIndex2 = -1; if (audioTrackIndex2 >= 0) { MediaFormat audioFormat2 = videoExtractor2.getTrackFormat(audioTrackIndex2); videoExtractor2.selectTrack(audioTrackIndex2); muxerAudioTrackIndex2 = mediaMuxer.addTrack(audioFormat2); } mediaMuxer.start(); // Ghi dữ liệu video từ video đầu tiên mVideoTrackIndex1 = videoTrackIndex1; long lastPresentationTimeUs = writeSampleData(videoExtractor1, mediaMuxer, muxerVideoTrackIndex1, muxerAudioTrackIndex, 0); // Ghi dữ liệu video từ video thứ hai mVideoTrackIndex2 = videoTrackIndex2; writeSampleData(videoExtractor2, mediaMuxer, muxerVideoTrackIndex2, muxerAudioTrackIndex2, lastPresentationTimeUs); } finally { if (mediaMuxer != null) { try { mediaMuxer.stop(); mediaMuxer.release(); } catch (Exception e) { logger.severe(e.toString()); } } videoExtractor1.release(); videoExtractor2.release(); } } private static int selectTrack(MediaExtractor extractor, String mimePrefix) { for (int i = 0; i < extractor.getTrackCount(); i++) { MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); assert mime != null; if (mime.startsWith(mimePrefix)) { return i; } } return -1; } private static long writeSampleData(MediaExtractor extractor, MediaMuxer muxer, int videoTrackIndex, int audioTrackIndex, long startPresentationTimeUs) { Log.d(TAG, "writeSampleData: check videoindex " + videoTrackIndex); Log.d(TAG, "writeSampleData: check audioindex " + audioTrackIndex); int bufferSize = determineBufferSize(extractor, mVideoTrackIndex1); ByteBuffer buffer = ByteBuffer.allocate(bufferSize); MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); long lastPresentationTimeUs = startPresentationTimeUs; while (true) { bufferInfo.offset = 0; bufferInfo.size = extractor.readSampleData(buffer, 0); if (bufferInfo.size < 0) { Log.d(TAG, "No more samples to read"); bufferInfo.size = 0; break; } bufferInfo.presentationTimeUs = extractor.getSampleTime() + startPresentationTimeUs; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { bufferInfo.flags = mapExtractorFlagsToCodecFlags(extractor.getSampleFlags()); } int trackIndex = extractor.getSampleTrackIndex(); Log.d(TAG, "writeSampleData: trackindex " + trackIndex); if (trackIndex == videoTrackIndex) { muxer.writeSampleData(videoTrackIndex, buffer, bufferInfo); } else if (trackIndex == audioTrackIndex) { muxer.writeSampleData(audioTrackIndex, buffer, bufferInfo); } lastPresentationTimeUs = bufferInfo.presentationTimeUs; extractor.advance(); } return lastPresentationTimeUs; } @RequiresApi(api = Build.VERSION_CODES.O) private static int mapExtractorFlagsToCodecFlags(int extractorFlags) { int codecFlags = 0; if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) { codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME; } if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) { codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME; } if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) { codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG; // Best guess for encrypted } return codecFlags; } private static int determineBufferSize(MediaExtractor extractor, int trackIndex) { MediaFormat format = extractor.getTrackFormat(trackIndex); int width = format.getInteger(MediaFormat.KEY_WIDTH); int height = format.getInteger(MediaFormat.KEY_HEIGHT); int pixelCount = width * height; int bufferSize; if (pixelCount <= 1280 * 720) { // 720p bufferSize = 256 * 1024; // 256 KB } else if (pixelCount <= 1920 * 1080) { // 1080p bufferSize = 512 * 1024; // 512 KB } else if (pixelCount <= 3840 * 2160) { // 4K bufferSize = 1024 * 1024; // 1 MB } else { bufferSize = 2048 * 1024; // 2 MB } return bufferSize; } private static MediaFormat createSilentAudioFormat() { MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 2); format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); format.setInteger(MediaFormat.KEY_BIT_RATE, 128000); format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 16384); return format; } private static void writeSilentAudioData(MediaMuxer muxer, int audioTrackIndex, MediaFormat videoFormat) { long videoDurationUs = videoFormat.getLong(MediaFormat.KEY_DURATION); int sampleRate = 44100; int channelCount = 2; int bitrate = 128000; int frameSize = 1024; // AAC frame size int bytesPerFrame = channelCount * frameSize * 2; // 16 bits per sample (2 bytes) long audioPresentationTimeUs = 0; ByteBuffer buffer = ByteBuffer.allocate(bytesPerFrame); MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); while (audioPresentationTimeUs < videoDurationUs) { buffer.clear(); bufferInfo.offset = 0; bufferInfo.size = bytesPerFrame; bufferInfo.presentationTimeUs = audioPresentationTimeUs; bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME; muxer.writeSampleData(audioTrackIndex, buffer, bufferInfo); audioPresentationTimeUs += (long) ((1.0 * frameSize / sampleRate) * 1000000); } } }
Editor is loading...
Leave a Comment