Untitled

 avatar
unknown
plain_text
9 months ago
7.2 kB
1
Indexable
package com.example.miniproject;

import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.logging.Logger;

public class VideoMerger {

    private static final String TAG = "VideoMerger";
    private static final Logger logger = Logger.getLogger(VideoMerger.class.getName());

    public void mergeVideos(AssetFileDescriptor afd1, AssetFileDescriptor afd2, 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à thêm track video và audio từ video đầu tiên
            videoExtractor1.setDataSource(afd1.getFileDescriptor(), afd1.getStartOffset(), afd1.getLength());
            int videoTrackIndex1 = selectTrack(videoExtractor1, "video/");
            if (videoTrackIndex1 == -1) {
                throw new IllegalArgumentException("No video track found in the first video.");
            }
            MediaFormat videoFormat1 = videoExtractor1.getTrackFormat(videoTrackIndex1);
            videoExtractor1.selectTrack(videoTrackIndex1);
            int muxerVideoTrackIndex = mediaMuxer.addTrack(videoFormat1);

            int audioTrackIndex1 = selectTrack(videoExtractor1, "audio/");
            int muxerAudioTrackIndex1 = -1;
            if (audioTrackIndex1 >= 0) {
                MediaFormat audioFormat1 = videoExtractor1.getTrackFormat(audioTrackIndex1);
                videoExtractor1.selectTrack(audioTrackIndex1);
                muxerAudioTrackIndex1 = mediaMuxer.addTrack(audioFormat1);
            }

            // Bắt đầu MediaMuxer sau khi thêm các track từ video đầu tiên
            mediaMuxer.start();

            // Ghi dữ liệu video và audio từ video đầu tiên
            writeSampleData(videoExtractor1, mediaMuxer, muxerVideoTrackIndex, muxerAudioTrackIndex1, 0);

            // Trích xuất và thêm track video và audio từ video thứ hai
            videoExtractor2.setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength());
            int videoTrackIndex2 = selectTrack(videoExtractor2, "video/");
            if (videoTrackIndex2 == -1) {
                throw new IllegalArgumentException("No video track found in the second video.");
            }
            MediaFormat videoFormat2 = videoExtractor2.getTrackFormat(videoTrackIndex2);
            videoExtractor2.selectTrack(videoTrackIndex2);

            int audioTrackIndex2 = selectTrack(videoExtractor2, "audio/");
            int muxerAudioTrackIndex2 = -1;
            if (audioTrackIndex2 >= 0) {
                MediaFormat audioFormat2 = videoExtractor2.getTrackFormat(audioTrackIndex2);
                videoExtractor2.selectTrack(audioTrackIndex2);
                muxerAudioTrackIndex2 = mediaMuxer.addTrack(audioFormat2);
            }

            // Tính toán độ trễ thời gian hiển thị cho video thứ hai
            long videoDurationUs1 = getDurationUs(videoExtractor1, videoTrackIndex1);
            long audioDurationUs1 = (audioTrackIndex1 >= 0) ? getDurationUs(videoExtractor1, audioTrackIndex1) : 0;

            // Ghi dữ liệu video và audio từ video thứ hai
            writeSampleData(videoExtractor2, mediaMuxer, muxerVideoTrackIndex, muxerAudioTrackIndex2, Math.max(videoDurationUs1, audioDurationUs1));

        } finally {
            if (mediaMuxer != null) {
                try {
                    mediaMuxer.stop();
                    mediaMuxer.release();
                } catch (Exception e) {
                    logger.severe(e.toString());
                }
            }
            videoExtractor1.release();
            videoExtractor2.release();
            afd1.close();
            afd2.close();
        }
    }

    private 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 void writeSampleData(MediaExtractor extractor, MediaMuxer muxer, int videoTrackIndex, int audioTrackIndex, long timeOffsetUs) {
        int bufferSize = determineBufferSize(extractor, videoTrackIndex);
        ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

        while (true) {
            bufferInfo.offset = 0;
            bufferInfo.size = extractor.readSampleData(buffer, 0);
            if (bufferInfo.size < 0) {
                bufferInfo.size = 0;
                break;
            }
            bufferInfo.presentationTimeUs = extractor.getSampleTime() + timeOffsetUs;
            bufferInfo.flags = mapExtractorFlagsToCodecFlags(extractor.getSampleFlags());
            int trackIndex = extractor.getSampleTrackIndex();
            if (trackIndex == videoTrackIndex) {
                muxer.writeSampleData(videoTrackIndex, buffer, bufferInfo);
            } else if (trackIndex == audioTrackIndex) {
                muxer.writeSampleData(audioTrackIndex, buffer, bufferInfo);
            }
            extractor.advance();
        }
    }

    private long getDurationUs(MediaExtractor extractor, int trackIndex) {
        MediaFormat format = extractor.getTrackFormat(trackIndex);
        return format.getLong(MediaFormat.KEY_DURATION);
    }

    private int mapExtractorFlagsToCodecFlags(int extractorFlags) {
        int codecFlags = 0;
        if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
            codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_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;
        }
        return codecFlags;
    }

    private 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;
    }
}
Editor is loading...
Leave a Comment