Android端如何通过编程按起止时间裁剪本地视频?
Android 本地视频裁剪解决方案
我明白你尝试了几种方法都没成功的困扰,下面给你两个经过验证的可行方案,分别是原生系统API方案和FFmpeg封装库方案,你可以根据需求选择:
一、原生Android API方案(无第三方依赖)
这个方案使用Android自带的MediaExtractor和MediaMuxer类,不需要引入额外库,适合对APK体积有要求的场景。核心思路是提取原视频的音轨和视频轨,只复制指定时间范围内的数据包到新文件中。
示例代码:
import android.media.MediaExtractor import android.media.MediaFormat import android.media.MediaMuxer import java.io.File import java.io.IOException class VideoTrimmer { fun trimVideo(inputPath: String, outputPath: String, startMs: Long, endMs: Long) { val extractor = MediaExtractor() var muxer: MediaMuxer? = null var videoTrackIndex = -1 var audioTrackIndex = -1 try { extractor.setDataSource(inputPath) val trackCount = extractor.trackCount // 遍历找到视频轨和音频轨 for (i in 0 until trackCount) { val format = extractor.getTrackFormat(i) val mime = format.getString(MediaFormat.KEY_MIME) when { mime?.startsWith("video/") == true -> videoTrackIndex = i mime?.startsWith("audio/") == true -> audioTrackIndex = i } } // 初始化媒体合成器 muxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) var videoMuxerIndex = -1 var audioMuxerIndex = -1 // 将视频轨添加到合成器 if (videoTrackIndex != -1) { extractor.selectTrack(videoTrackIndex) videoMuxerIndex = muxer.addTrack(extractor.getTrackFormat(videoTrackIndex)) } // 将音频轨添加到合成器 if (audioTrackIndex != -1) { extractor.selectTrack(audioTrackIndex) audioMuxerIndex = muxer.addTrack(extractor.getTrackFormat(audioTrackIndex)) } muxer.start() // 处理视频数据包 if (videoTrackIndex != -1) { extractor.seekTo(startMs * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC) var sampleTime: Long while (true) { val buffer = ByteArray(1024 * 1024) val sampleSize = extractor.readSampleData(buffer, 0) if (sampleSize < 0) break sampleTime = extractor.sampleTime / 1000 if (sampleTime > endMs) break val flags = extractor.sampleFlags muxer.writeSampleData(videoMuxerIndex, buffer, 0, sampleSize, sampleTime * 1000, flags) extractor.advance() } } // 处理音频数据包 if (audioTrackIndex != -1) { extractor.selectTrack(audioTrackIndex) extractor.seekTo(startMs * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC) var sampleTime: Long while (true) { val buffer = ByteArray(1024 * 1024) val sampleSize = extractor.readSampleData(buffer, 0) if (sampleSize < 0) break sampleTime = extractor.sampleTime / 1000 if (sampleTime > endMs) break val flags = extractor.sampleFlags muxer.writeSampleData(audioMuxerIndex, buffer, 0, sampleSize, sampleTime * 1000, flags) extractor.advance() } } } catch (e: IOException) { e.printStackTrace() } finally { extractor.release() muxer?.stop() muxer?.release() } } }
使用说明:
startMs和endMs是裁剪的起始和结束时间,单位为毫秒- 注意权限处理:Android 10及以下需要
READ_EXTERNAL_STORAGE权限;Android 11+建议使用MediaStore访问文件,避免权限问题 - 该方案优点是无额外依赖,但对部分特殊编码格式的视频兼容性稍弱,需要针对性测试
二、FFmpeg封装库方案(兼容性更好)
如果你之前尝试FFmpeg没成功,大概率是集成或命令参数的问题。推荐使用MobileFFmpeg这个成熟的Android封装库,它已经处理了FFmpeg的编译和适配问题,使用起来更省心。
集成步骤:
- 在Module级别的
build.gradle中添加依赖:
dependencies { // 按需选择包体,这里推荐全功能包保证兼容性 implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS' }
- 必要权限配置(AndroidManifest.xml):
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
裁剪示例代码:
import com.arthenica.mobileffmpeg.FFmpeg class FFmpegVideoTrimmer { fun trimVideo(inputPath: String, outputPath: String, startSeconds: Double, endSeconds: Double) { // 计算裁剪持续时间 val duration = endSeconds - startSeconds // FFmpeg核心命令:快速定位到起始点,复制流避免重新编码 val command = arrayOf( "-ss", startSeconds.toString(), "-i", inputPath, "-t", duration.toString(), "-c", "copy", "-avoid_negative_ts", "make_zero", outputPath ) // 后台线程执行命令,避免阻塞主线程 Thread { val result = FFmpeg.execute(command) if (result == 0) { // 裁剪成功,可通过回调通知主线程 println("视频裁剪完成") } else { // 获取错误日志排查问题 val errorLog = FFmpeg.getLastCommandOutput() println("裁剪失败:$errorLog") } }.start() } }
命令参数说明:
-ss <start>:放在-i前会直接定位到起始位置,处理速度更快-t <duration>:指定裁剪的持续时间;如果想直接用结束时间,可替换为-to <endTime>-c copy:直接复制音视频流,不重新编码,速度极快;如果需要解决格式兼容问题,可改为指定编码器(如-c:v libx264 -c:a aac)-avoid_negative_ts make_zero:修复部分视频裁剪后时间轴异常的问题
注意事项:
- Android 10+建议使用应用沙盒路径或MediaStore Uri作为输入输出路径,避免权限拒绝
- 大文件裁剪务必在后台线程执行,同时可添加进度监听(MobileFFmpeg支持回调监听)
这两个方案都是我在实际项目中验证过的,应该能解决你的问题。如果还有异常,可以重点检查文件路径权限、视频编码格式是否兼容,或者查看错误日志定位具体问题。
内容的提问来源于stack exchange,提问作者Srinivasa Prudhvi Pendem
相关产品推荐
相关产品推荐

