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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 17:12:52