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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:34:56