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是功能强大的音视频处理工具,通过命令行即可快速实现视频分割,适合复杂场景:
- 集成预编译的FFmpeg Android库(如引入aar依赖)
- 执行分割命令(
-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
相关产品推荐
相关产品推荐

