如何用Mp4Parser给高码率视频添加水印并解决Pexels视频编码报错问题
问题根因
你遇到的0xfffffc0e错误属于Android MediaCodec硬编码器配置失败,触发原因是Pexels平台提供的视频参数超出了当前设备硬编码器的支持范围:
- YouTube、Instagram等平台的下载视频都经过了平台统一转码处理,分辨率、码率、编码规格都适配了绝大多数移动设备的硬编能力,所以可以正常处理
- Pexels的素材为了保留原始画质,通常会采用4K分辨率、10Mbps以上高码率、H.264 High Profile等高规格参数,很容易超出中低端Android设备的硬编上限
修复方案
1. 等比缩放输出分辨率
硬编码器对输入分辨率有明确上限,绝大多数移动设备硬编最大支持1080P分辨率,所以需要先读取原视频宽高,等比缩放到1080P以内,同时确保宽高为16的倍数(MediaCodec编码要求)。
2. 手动指定输出码率
不要使用Mp4Composer默认的原视频码率继承逻辑,手动将输出码率设置为设备兼容的范围:1080P视频对应4-8Mbps,720P视频对应2-4Mbps即可满足常规使用清晰度要求。
3. 可选适配项
如果修改分辨率和码率后仍报错,可以修改Mp4Composer源码中VideoComposer类的MediaFormat配置,强制指定兼容性最高的H.264 Main Profile和Level 4.1:
mediaFormat.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileMain); mediaFormat.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel41);
修改后代码示例
// 先获取原视频参数,计算适配后的输出分辨率 val retriever = MediaMetadataRetriever() retriever.setDataSource(videoPath) val originWidth = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt() val originHeight = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt() val maxSupportedSide = 1080 var targetWidth = originWidth var targetHeight = originHeight // 等比缩放到最大边不超过1080 if (originWidth > originHeight && originWidth > maxSupportedSide) { targetWidth = maxSupportedSide targetHeight = (originHeight * (maxSupportedSide.toFloat() / originWidth)).toInt() } else if (originHeight > maxSupportedSide) { targetHeight = maxSupportedSide targetWidth = (originWidth * (maxSupportedSide.toFloat() / originHeight)).toInt() } // 对齐16的倍数,符合硬编要求 targetWidth = targetWidth / 16 * 16 targetHeight = targetHeight / 16 * 16 Mp4Composer(videoPath!!, getDestinationPath()) .size(targetWidth, targetHeight) // 指定适配后的输出分辨率 .bitrate(6 * 1024 * 1024) // 指定6Mbps的输出码率,可根据需求调整 .filter(GlWatermarkFilter(bitmap, getRepostWatermarkPosition())) .listener(object : Mp4Composer.Listener { override fun onProgress(progress: Double) { mainScoopLauncher { loadStateEnable(true) val progressValue = (progress * 100).toString() val isUnder10Percentage = progressValue.take(2).contains(".") val under10PercentageValue = "${progress * 100}".take(1) + "%" val upto10PercentageValue = progressValue.take(2) + "%" val loadValue = if (isUnder10Percentage) under10PercentageValue else upto10PercentageValue binding.txtPercentage.text = loadValue } } override fun onCurrentWrittenVideoTime(timeUs: Long) { mainScoopLauncher { loadStateEnable(true) } } override fun onCompleted() { mainScoopLauncher { loadStateEnable(false) toast("Repost Video Complete") } } override fun onCanceled() { logger("cancelled") mainScoopLauncher { loadStateEnable(false) toast("Repost Process Cancel..") } } override fun onFailed(exception: Exception?) { mainScoopLauncher { loadStateEnable(false) toast("Repost Process Failed..") } } }).start()
兜底兼容方案
如果硬编方案仍有设备兼容性问题,可以切换为FFmpeg软编实现水印添加,软编不受硬件能力限制,兼容性100%,仅处理速度比硬编慢30%-50%左右。
内容的提问来源于stack exchange,提问作者Ravi Sorathiya
相关产品推荐
相关产品推荐

