Android视频压缩中硬件编解码器使用及h264_mediacodec故障排查
Android硬件加速视频压缩:修复h264_mediacodec运行问题
一、排查h264_mediacodec通用错误的核心方向
1. 验证设备硬件编码支持性
不是所有Android设备都支持H.264硬件编码,先通过代码确认设备是否具备该能力:
private fun isH264HardwareEncoderSupported(): Boolean { val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS) return codecList.codecInfos.any { codecInfo -> codecInfo.isEncoder && codecInfo.name.contains("h264", ignoreCase = true) && codecInfo.isHardwareAccelerated } }
如果返回false,说明设备不支持H.264硬件编码,只能 fallback 到软编码。
2. 修正FFmpeg命令参数冲突
原命令的scale滤镜输出像素格式可能与硬件编码器不兼容,且命令顺序会导致软件解码拖慢速度,同时缺少硬件编码器必需的参数。
3. 确认FFmpegKit编译有效性
手动编译后,执行以下命令验证h264_mediacodec是否被正确编译进FFmpegKit:
ffmpeg -encoders
输出中应包含V....D h264_mediacodec(V代表视频编码器,D代表解码器)。
二、修改后的可运行代码示例
以下是调整后的视频压缩代码,针对硬件编码做了参数适配:
companion object { private const val VIDEO_WIDTH = 1080 private const val VIDEO_HEIGHT = 1920 private const val BITRATE_MBS = 7 * 1000000 private const val COMPRESSION_FILE_NAME = "compression" private const val VIDEO_EXTENSION = ".mp4" } suspend fun compressVideo(videoUri: Uri): String? = withContext(defaultDispatcher) { val tempInputFile = copyUriToFile(context, videoUri) val outputFile = File.createTempFile(COMPRESSION_FILE_NAME, VIDEO_EXTENSION, context.cacheDir) val outputPath = outputFile.absolutePath // 先检查硬件编码支持性 if (!isH264HardwareEncoderSupported()) { // fallback到软编码(libx264) FirebaseCrashlytics.getInstance().log("H264 hardware encoder not supported, falling back to libx264") val cmd = mutableListOf( "-y", "-i", tempInputFile.absolutePath, "-vf", "scale=w=$VIDEO_WIDTH:h=$VIDEO_HEIGHT:force_original_aspect_ratio=decrease", "-c:v", "libx264", "-b:v", "$BITRATE_MBS", "-preset", "ultrafast", "-movflags", "+faststart", outputPath ) val session = FFmpegKit.execute(cmd.joinToString(" ")) tempInputFile.delete() return@withContext if (ReturnCode.isSuccess(session.returnCode)) outputPath else null } // 硬件编码命令 val cmd = mutableListOf( "-y", "-i", tempInputFile.absolutePath, // 使用硬件解码输入,减少软件解码开销 "-c:v", "h264_mediacodec", // 调整分辨率并指定硬件编码器支持的像素格式(NV12是多数设备的通用格式) "-vf", "scale=w=$VIDEO_WIDTH:h=$VIDEO_HEIGHT:force_original_aspect_ratio=decrease,pix_fmt=nv12", // 指定硬件编码 "-c:v", "h264_mediacodec", "-b:v", "$BITRATE_MBS", // 适配硬件编码器的参数,部分设备需要指定profile/level "-profile:v", "baseline", "-level:v", "4.1", // 优化MP4封装,避免播放兼容性问题 "-movflags", "+faststart", outputPath ) val session: FFmpegSession = FFmpegKit.execute(cmd.joinToString(" ")) val logs = session.allLogsAsString tempInputFile.delete() return@withContext if (ReturnCode.isSuccess(session.returnCode)) { outputPath } else { FirebaseCrashlytics.getInstance().recordException(Exception("FFmpeg hardware compression failed: $logs")) null } } // 工具方法:检查H264硬件编码器支持 private fun isH264HardwareEncoderSupported(): Boolean { val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS) return codecList.codecInfos.any { codecInfo -> codecInfo.isEncoder && codecInfo.name.contains("h264", ignoreCase = true) && codecInfo.isHardwareAccelerated } }
三、参数调整说明
- 硬件解码前置:先指定
-c:v h264_mediacodec解码输入视频,避免软件解码拖慢整体速度,同时保证输入像素格式与编码器兼容。 - 像素格式指定:
scale滤镜后添加pix_fmt=nv12,这是Android硬件H.264编码器最广泛支持的像素格式,解决格式不匹配导致的初始化失败。 - 封装优化:
-movflags +faststart将MP4的moov原子移到文件开头,避免播放时加载缓慢,同时减少封装阶段的错误。 - 兼容性 fallback:加入硬件编码支持检查,当设备不支持时自动切换到libx264软编码,保证功能可用性。
四、ExoPlayer Transformer速度优化
如果仍想使用Transformer,可通过以下方式提升速度:
- 强制启用硬件加速:
val transformer = Transformer.Builder(context) .setEnableHardwareAcceleration(true) .build() - 调整线程池:使用设备核心数对应的线程池处理转码任务:
val executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) val transformer = Transformer.Builder(context) .setExecutor(executor) .build() - 跳过不必要的转码:如果输入视频分辨率已符合要求,仅调整码率,避免冗余的缩放操作。
内容的提问来源于stack exchange,提问作者Filnik
相关产品推荐
相关产品推荐

