Android:如何检测视频是否为.mp4格式并实现格式转换?
Android平台现代化视频容器检测与MP4转换方案
一、准确检测视频容器格式
你提到的MediaMetadataRetriever获取MIME类型可能存在不准确问题,确实它依赖文件元数据而非实际解析容器结构。更可靠的方式是使用MediaExtractor直接解析文件的容器信息:
fun isVideoMp4Container(filePath: String): Boolean { val extractor = MediaExtractor() return try { extractor.setDataSource(filePath) // API 29+可直接获取容器格式,更精准 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { extractor.containerFormat == MediaExtractor.CONTAINER_FORMAT_MPEG4 } else { // 低版本通过轨道MIME类型判断,MP4容器的音视频轨道通常以video/mp4或audio/mp4开头 val format = extractor.getTrackFormat(0) val mimeType = format.getString(MediaFormat.KEY_MIME) mimeType?.startsWith("video/mp4") == true || mimeType?.startsWith("audio/mp4") == true } } catch (e: IOException) { false } finally { extractor.release() } }
该方法通过实际解析文件轨道或直接读取容器标识(API 29+),能有效避免元数据不准确的问题。
二、原生现代化转换方案
1. API 29+:使用MediaTranscoder(推荐)
Android 10及以上提供的MediaTranscoder是系统级转码API,封装了底层的MediaCodec与MediaMuxer,无需手动处理编码解码逻辑,使用简单且性能优化到位:
fun transcodeToMp4(sourceUri: Uri, destUri: Uri, callback: (Boolean) -> Unit) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { callback(false) return } // 目标格式指定为MP4,0表示保留原分辨率 val targetFormat = MediaFormat.createVideoFormat("video/mp4", 0, 0) val transcoder = MediaTranscoder.create(sourceUri, destUri, targetFormat) transcoder.setCallback(object : MediaTranscoder.Callback { override fun onTranscodeCompleted(result: MediaTranscoder.Result) { callback(result == MediaTranscoder.Result.SUCCESS) } override fun onTranscodeFailed(e: Exception) { callback(false) } }) transcoder.start() }
注意:针对相册文件,建议通过MediaStore获取Uri而非直接操作文件路径;需确保应用拥有对应文件的读写权限。
2. 兼容低版本:MediaExtractor + MediaMuxer(容器级转换)
如果需要兼容API 29以下设备,可使用MediaExtractor提取音视频轨道,结合MediaMuxer直接封装为MP4容器。若源视频编码(如H.264/AAC)本身兼容MP4,无需重新编码,效率极高:
fun convertContainerToMp4(sourcePath: String, destPath: String): Boolean { var extractor: MediaExtractor? = null var muxer: MediaMuxer? = null return try { extractor = MediaExtractor() extractor.setDataSource(sourcePath) muxer = MediaMuxer(destPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) val trackIndexMap = mutableMapOf<Int, Int>() // 遍历所有轨道,仅处理音视频轨道 for (i in 0 until extractor.trackCount) { val format = extractor.getTrackFormat(i) val mimeType = format.getString(MediaFormat.KEY_MIME) if (mimeType?.startsWith("video/") == true || mimeType?.startsWith("audio/") == true) { extractor.selectTrack(i) val muxerTrackIndex = muxer.addTrack(format) trackIndexMap[i] = muxerTrackIndex } } muxer.start() val buffer = ByteBuffer.allocate(1024 * 1024) val bufferInfo = MediaCodec.BufferInfo() // 写入所有音视频轨道数据 trackIndexMap.keys.forEach { sourceTrackIndex -> extractor.selectTrack(sourceTrackIndex) val targetTrackIndex = trackIndexMap[sourceTrackIndex] ?: return@forEach while (true) { val readSize = extractor.readSampleData(buffer, 0) if (readSize < 0) break bufferInfo.apply { offset = 0 size = readSize presentationTimeUs = extractor.sampleTime flags = extractor.sampleFlags } muxer.writeSampleData(targetTrackIndex, buffer, bufferInfo) extractor.advance() } } true } catch (e: IOException) { false } finally { extractor?.release() muxer?.apply { try { stop() } catch (e: IllegalStateException) {} release() } } }
若源编码不兼容MP4,该方法会抛出异常,此时需结合MediaCodec进行编码转换,但这种场景相对少见。
三、核心注意事项
- 后台执行:视频转换属于耗时操作,需放在
WorkManager或后台线程中执行,避免阻塞UI。 - 权限适配:Android 10+使用
MediaStore访问文件,低版本需申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。 - 错误处理:需捕获IO异常、编码不兼容等情况,给用户明确的操作提示。
内容的提问来源于stack exchange,提问作者MaxSped
相关产品推荐
相关产品推荐

