You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java实现视频分割:自动拆分5分钟片段至缓存并可访问

1. 如何在Android Java中分割视频?

在Android中分割视频主要有两种主流实现方案:利用系统原生API,或者集成FFmpeg库。

方案一:系统原生API(无第三方依赖)

通过MediaExtractor读取原视频的音视频轨道数据,筛选指定时间范围内的帧,再通过MediaMuxer写入新视频文件,核心步骤如下:

  • 初始化MediaExtractor并设置视频源路径/Uri
  • 遍历轨道,获取音视频轨道的索引与格式信息
  • 初始化MediaMuxer,指定输出文件路径与格式
  • 将音视频轨道添加到MediaMuxer
  • 设置截取的起始/结束时间(单位:微秒),循环读取样本数据,过滤时间范围外的帧并写入MediaMuxer
  • 完成后释放所有资源

示例代码:

public void splitVideoWithSystemAPI(String inputPath, String outputPath, long startUs, long endUs) throws IOException {
    MediaExtractor extractor = new MediaExtractor();
    extractor.setDataSource(inputPath);

    MediaMuxer muxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

    int videoTrackIndex = -1;
    int audioTrackIndex = -1;
    MediaFormat videoFormat = null;
    MediaFormat audioFormat = null;

    // 遍历获取音视频轨道信息
    for (int i = 0; i < extractor.getTrackCount(); i++) {
        MediaFormat format = extractor.getTrackFormat(i);
        String mime = format.getString(MediaFormat.KEY_MIME);
        if (mime.startsWith("video/")) {
            videoTrackIndex = i;
            videoFormat = format;
        } else if (mime.startsWith("audio/")) {
            audioTrackIndex = i;
            audioFormat = format;
        }
    }

    // 向Muxer添加轨道
    if (videoFormat != null) videoTrackIndex = muxer.addTrack(videoFormat);
    if (audioFormat != null) audioTrackIndex = muxer.addTrack(audioFormat);

    muxer.start();

    // 处理视频轨道
    if (videoTrackIndex != -1) {
        extractor.selectTrack(videoTrackIndex);
        ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        while (true) {
            int readSize = extractor.readSampleData(buffer, 0);
            if (readSize < 0) break;

            long sampleTime = extractor.getSampleTime();
            if (sampleTime < startUs || sampleTime > endUs) {
                extractor.advance();
                continue;
            }

            bufferInfo.offset = 0;
            bufferInfo.size = readSize;
            bufferInfo.presentationTimeUs = sampleTime - startUs;
            bufferInfo.flags = extractor.getSampleFlags();
            muxer.writeSampleData(videoTrackIndex, buffer, bufferInfo);
            extractor.advance();
        }
    }

    // 处理音频轨道
    if (audioTrackIndex != -1) {
        extractor.selectTrack(audioTrackIndex);
        ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        while (true) {
            int readSize = extractor.readSampleData(buffer, 0);
            if (readSize < 0) break;

            long sampleTime = extractor.getSampleTime();
            if (sampleTime < startUs || sampleTime > endUs) {
                extractor.advance();
                continue;
            }

            bufferInfo.offset = 0;
            bufferInfo.size = readSize;
            bufferInfo.presentationTimeUs = sampleTime - startUs;
            bufferInfo.flags = extractor.getSampleFlags();
            muxer.writeSampleData(audioTrackIndex, buffer, bufferInfo);
            extractor.advance();
        }
    }

    // 释放资源
    muxer.stop();
    muxer.release();
    extractor.release();
}

方案二:集成FFmpeg库

FFmpeg是功能强大的音视频处理工具,通过命令行即可快速实现视频分割,适合复杂场景:

  1. 集成预编译的FFmpeg Android库(如引入aar依赖)
  2. 执行分割命令(-c copy表示直接复制流,无重新编码,速度快)

Java执行命令示例:

public void splitVideoWithFFmpeg(String inputPath, String outputPath, int startSecond, int durationSecond) {
    String[] command = {
        "ffmpeg",
        "-i", inputPath,
        "-ss", String.valueOf(startSecond),
        "-t", String.valueOf(durationSecond),
        "-c", "copy",
        outputPath
    };

    // 异步执行,避免阻塞主线程
    new Thread(() -> {
        FFmpeg.execute(command);
        // 执行完成后的回调逻辑
    }).start();
}

2. 如何实现自动视频分割功能(选择相册视频→拆分5分钟片段→存缓存→访问)

步骤1:获取相册视频权限并选择视频

  • 权限处理:Android 10及以下需申请READ_EXTERNAL_STORAGE;Android 11+无需额外权限,直接通过MediaStore访问。
  • 打开相册选择视频:使用ActivityResultContracts.GetContent获取视频Uri:
// 初始化选择视频的Contract
ActivityResultLauncher<String> pickVideoLauncher = registerForActivityResult(
    new ActivityResultContracts.GetContent(),
    uri -> {
        if (uri != null) startAutoSplitVideo(uri);
    }
);

// 触发选择操作
pickVideoLauncher.launch("video/*");

步骤2:计算视频总时长与分割片段

先获取原视频总时长,再按5分钟(300,000,000微秒)分割:

private long getVideoDuration(Uri uri) {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    retriever.setDataSource(getApplicationContext(), uri);
    String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
    retriever.release();
    return Long.parseLong(durationStr) * 1000; // 转换为微秒
}

private List<SplitSegment> calculateSplitSegments(long totalDurationUs) {
    List<SplitSegment> segments = new ArrayList<>();
    long segmentDurationUs = 5 * 60 * 1000 * 1000; // 5分钟对应的微秒数
    long currentStartUs = 0;

    while (currentStartUs < totalDurationUs) {
        long currentEndUs = Math.min(currentStartUs + segmentDurationUs, totalDurationUs);
        segments.add(new SplitSegment(currentStartUs, currentEndUs));
        currentStartUs = currentEndUs;
    }
    return segments;
}

// 存储片段时间的辅助类
static class SplitSegment {
    long startUs;
    long endUs;
    public SplitSegment(long startUs, long endUs) {
        this.startUs = startUs;
        this.endUs = endUs;
    }
}

步骤3:执行分割并保存到应用缓存

应用缓存路径使用getExternalCacheDir()(外部缓存,卸载时自动清除)或getCacheDir()(内部缓存),无需额外权限:

private void startAutoSplitVideo(Uri uri) {
    String inputPath = getRealPathFromUri(uri);
    if (inputPath == null) return;

    long totalDurationUs = getVideoDuration(uri);
    List<SplitSegment> segments = calculateSplitSegments(totalDurationUs);

    // 创建缓存目录
    File cacheDir = getExternalCacheDir() != null ? getExternalCacheDir() : getCacheDir();
    File splitDir = new File(cacheDir, "video_splits");
    if (!splitDir.exists()) splitDir.mkdirs();

    // 异步执行分割任务
    new Thread(() -> {
        for (int i = 0; i < segments.size(); i++) {
            SplitSegment segment = segments.get(i);
            String outputPath = new File(splitDir, "split_" + (i+1) + ".mp4").getAbsolutePath();
            try {
                splitVideoWithSystemAPI(inputPath, outputPath, segment.startUs, segment.endUs);
                // 或使用FFmpeg方案:splitVideoWithFFmpeg(inputPath, outputPath, (int)(segment.startUs/1000000), (int)((segment.endUs - segment.startUs)/1000000));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 分割完成后通知主线程更新UI
        runOnUiThread(() -> {
            // 更新列表或提示用户
        });
    }).start();
}

// Android 10+获取视频真实路径
private String getRealPathFromUri(Uri uri) {
    String filePath = null;
    if (DocumentsContract.isDocumentUri(this, uri)) {
        String docId = DocumentsContract.getDocumentId(uri);
        if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
            String id = docId.split(":")[1];
            String selection = MediaStore.Video.Media._ID + "=" + id;
            filePath = getFilePath(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, selection);
        }
    } else if ("content".equalsIgnoreCase(uri.getScheme())) {
        filePath = getFilePath(uri, null);
    }
    return filePath;
}

private String getFilePath(Uri uri, String selection) {
    String path = null;
    Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
    if (cursor != null) {
        if (cursor.moveToFirst()) {
            path = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA));
        }
        cursor.close();
    }
    return path;
}

步骤4:访问缓存中的分割片段

直接遍历缓存目录即可获取所有分割后的视频文件:

private List<File> getSplitVideoFiles() {
    List<File> files = new ArrayList<>();
    File cacheDir = getExternalCacheDir() != null ? getExternalCacheDir() : getCacheDir();
    File splitDir = new File(cacheDir, "video_splits");
    if (splitDir.exists() && splitDir.isDirectory()) {
        File[] videoFiles = splitDir.listFiles((dir, name) -> name.endsWith(".mp4"));
        if (videoFiles != null) files.addAll(Arrays.asList(videoFiles));
    }
    return files;
}

// 播放示例(使用VideoView)
private void playSplitVideo(File videoFile) {
    VideoView videoView = findViewById(R.id.video_view);
    videoView.setVideoPath(videoFile.getAbsolutePath());
    videoView.start();
}

内容的提问来源于stack exchange,提问作者user13926345

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 17:27:57