使用MediaCodec时崩溃:native_window_api_connect返回-19设备不存在错误
解决MediaCodec配置时的
native_window_api_connect returned an error: No such device (-19)崩溃问题 这个-19错误(对应Linux系统的ENODEV错误码)我之前在高通设备上碰到过好几次,本质是MediaCodec无法正常连接到你传入的Surface对象,结合你的日志和代码,我整理了几个最可能的原因和对应的解决办法:
1. 传入的Surface已经失效或状态异常
从日志里看到D/SVPlayerView: onSurfaceTextureAvailable width 1080 h,但很可能在调用createVideoDecoder时,Surface已经因为页面生命周期变化(比如Activity暂停、SurfaceTexture被销毁)变得无效了。
解决思路:
- 严格绑定解码器的生命周期和Surface:在
onSurfaceTextureAvailable回调触发后再初始化解码器,在onSurfaceTextureDestroyed回调中立即停止并释放解码器资源,避免持有无效的Surface引用。 - 在调用
configure前先检查Surface有效性:添加((Surface) surface).isValid()判断,如果Surface无效就直接返回,不要继续执行配置逻辑。
2. SPS/PPS数据处理可能存在问题
你的代码手动给SPS/PPS添加了0,0,0,1的起始码,虽然这是H.264的标准起始码,但部分设备的硬件解码器可能期望原始的、不带起始码的NAL单元数据;或者传入的SPS/PPS本身就有截断、格式错误的问题。
解决思路:
- 尝试去掉手动添加的起始码,直接将原始的sps/pps字节数组放入
csd-0和csd-1中:// 替换原来的ByteBuffer处理逻辑 mFormat.setByteBuffer("csd-0", ByteBuffer.wrap(sps)); mFormat.setByteBuffer("csd-1", ByteBuffer.wrap(pps)); - 验证SPS/PPS数据的正确性:可以打印它们的十六进制值,对比标准H.264的SPS结构,确保数据没有损坏。
3. 高通硬件解码器的兼容性坑
日志里显示用的是OMX.qcom.video.decoder.avc(高通硬件解码器),部分高通机型在处理特定分辨率(比如奇数分辨率)、特殊编码参数的视频时,会出现这种配置失败的问题。
解决思路:
- 增加软件解码器作为降级方案:当硬件解码器配置失败时,自动切换到软件解码器。可以通过遍历MediaCodec列表找到软件解码器:
private MediaCodec getSoftwareAvcDecoder() { MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) { if (!codecInfo.isEncoder() && codecInfo.getName().contains("avc") && codecInfo.isSoftwareOnly()) { try { return MediaCodec.createByCodecName(codecInfo.getName()); } catch (IOException e) { e.printStackTrace(); } } } return null; } - 检查视频宽高是否符合设备支持范围:通过
MediaCodecInfo.CodecCapabilities查询硬件解码器支持的最大分辨率,避免传入超出范围的参数。
4. 线程同步问题
从日志的线程信息来看,createVideoDecoder可能在后台线程调用,但Surface的初始化是在主线程的onSurfaceTextureAvailable回调中完成的,存在线程同步问题——可能Surface还没完全初始化好,就被传入解码器进行配置了。
解决思路:
- 确保解码器的创建和配置操作在
onSurfaceTextureAvailable的同一个线程中执行,或者用Handler.post将配置逻辑post到主线程执行,保证Surface已经完全就绪。 - 避免在多个线程中同时操作MediaCodec或Surface对象,解码器的创建、配置、启动流程要在同一个线程中完成。
针对你的代码的具体修改建议
我给你的createVideoDecoder方法加了几个关键的防护和降级逻辑,你可以参考:
public boolean createVideoDecoder(Object surface, int width, int height, byte[] sps, byte[] pps) { Log.i(TAG,"createVideoDecoder"); // 先检查Surface有效性 if (!(surface instanceof Surface) || !((Surface) surface).isValid()) { Log.e(TAG, "Invalid or null Surface"); return false; } mFormat = new MediaFormat(); mFormat.setString(MediaFormat.KEY_MIME, "video/avc"); mFormat.setInteger(MediaFormat.KEY_WIDTH, width); mFormat.setInteger(MediaFormat.KEY_HEIGHT, height); // 尝试直接使用原始SPS/PPS,不添加起始码 mFormat.setByteBuffer("csd-0", ByteBuffer.wrap(sps)); mFormat.setByteBuffer("csd-1", ByteBuffer.wrap(pps)); mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); try { mCodec = MediaCodec.createDecoderByType("video/avc"); if (mCodec == null) { return false; } mCodec.configure(mFormat, (Surface) surface, null, 0); mCodec.start(); info = new BufferInfo(); } catch (IOException e) { e.printStackTrace(); return cleanupAndReturnFalse(); } catch (MediaCodec.CodecException e) { Log.e(TAG, "Hardware codec configure failed: " + e.getMessage()); // 尝试切换到软件解码器 mCodec = getSoftwareAvcDecoder(); if (mCodec == null) { return cleanupAndReturnFalse(); } try { mCodec.configure(mFormat, (Surface) surface, null, 0); mCodec.start(); info = new BufferInfo(); return true; } catch (Exception ex) { ex.printStackTrace(); return cleanupAndReturnFalse(); } } return true; } private boolean cleanupAndReturnFalse() { if (mCodec != null) { mCodec.release(); mCodec = null; } return false; } private MediaCodec getSoftwareAvcDecoder() { MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) { if (!codecInfo.isEncoder() && codecInfo.getName().contains("avc") && codecInfo.isSoftwareOnly()) { try { return MediaCodec.createByCodecName(codecInfo.getName()); } catch (IOException e) { e.printStackTrace(); } } } return null; }
内容的提问来源于stack exchange,提问作者CHUANGXIN LEE
相关产品推荐
相关产品推荐

