LibVLC Android设备休眠/重启APP后IP摄像头流无法显示问题排查
问题:VLC SDK Android休眠/重启APP后无法播放IP摄像头流
问题描述
在Android项目中集成VLC SDK播放IP摄像头RTSP流,首次启动能正常连接播放,但设备进入待机/休眠模式,或关闭APP后重新打开时,视频流无法显示;杀死APP进程后又能恢复正常播放。
Logcat错误日志
eglCreateWindowSurface: native_window_api_connect (win=0xb4000075e8334010) failed (0xffffffed) (already connected to another API?) eglCreateWindowSurfaceTmpl:689 error 3003 (EGL_BAD_ALLOC) [b400007673514f60/623d] libvlc gl: cannot create EGL window [b40000765a07b400/623d] libvlc video output: video output display creation failed
依赖版本
- implementation
org.videolan.android:libvlc-all:4.0.0-eap13
原问题代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MainActivity"; private String rtspUrl; private LibVLC libVlc; private MediaPlayer mediaPlayer; private Media media; private VLCVideoLayout videoLayout; private SurfaceView surfaceView; private ProgressBar progressBar; private float buffering; private TextView taksnapTv, recordTv; private boolean isRecording = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressBar = findViewById(R.id.progressBar); videoLayout = findViewById(R.id.videoLayout); try { String encodedUsername = URLEncoder.encode("admin", "UTF-8"); String encodedPassword = URLEncoder.encode("admin", "UTF-8"); rtspUrl = "rtsp://" + encodedUsername + ":" + encodedPassword + "@192.168.0.115:554/stream1"; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } ArrayList<String> args = new ArrayList<>(); args.add("--aout=opensles"); args.add("--audio-time-stretch"); args.add("--rtsp-tcp"); args.add("--live-caching=0"); args.add("--file-caching=0"); args.add("--network-caching=200"); args.add("-vvv"); libVlc = new LibVLC(this, args); mediaPlayer = new MediaPlayer(libVlc); initListener(); } @Override protected void onStart() { super.onStart(); videoLayout.clearFocus(); videoLayout.clearAnimation(); mediaPlayer.attachViews(videoLayout, null, false, false); media = new Media(libVlc, Uri.parse(rtspUrl)); media.setHWDecoderEnabled(true, true); mediaPlayer.setMedia(media); media.release(); mediaPlayer.play(); } @Override protected void onResume() { super.onResume(); } @Override protected void onStop() { super.onStop(); mediaPlayer.stop(); mediaPlayer.detachViews(); } @Override protected void onDestroy() { super.onDestroy(); mediaPlayer.release(); libVlc.release(); } private void initListener() { mediaPlayer.setEventListener(new MediaPlayer.EventListener() { @Override public void onEvent(MediaPlayer.Event event) { if (event.type == MediaPlayer.Event.Opening) { Log.d(TAG, "VLC Opening"); progressBar.setVisibility(View.VISIBLE); } else if (event.type == MediaPlayer.Event.Buffering) { Log.d(TAG, "VLC Buffering:" + event.getBuffering()); if (event.getBuffering() >= 100) { buffering = event.getBuffering(); progressBar.setVisibility(View.GONE); } else progressBar.setVisibility(View.VISIBLE); } else if (event.type == MediaPlayer.Event.Playing) { Log.d(TAG, "VLC Playing"); } else if (event.type == MediaPlayer.Event.Stopped) { Log.d(TAG, "VLC Stopped"); progressBar.setVisibility(View.GONE); } else if (event.type == MediaPlayer.Event.EncounteredError) { Log.d(TAG, "VLC EncounteredError"); progressBar.setVisibility(View.GONE); } else if (event.type == MediaPlayer.Event.Vout) { Log.d(TAG, "VLC Vout" + event.getVoutCount()); } else if (event.type == MediaPlayer.Event.RecordChanged) { Log.d(TAG, "VLC RecordChanged"); } } }); } }
问题分析与解决方案
错误日志显示EGL窗口创建失败,核心原因是Native窗口资源被旧实例占用,Activity重启/恢复时,未完全释放旧的MediaPlayer、LibVLC资源,导致新视图无法绑定。
具体问题点
- LibVLC和MediaPlayer仅在首次onCreate初始化,Activity重建时复用旧实例,旧实例仍持有已失效的窗口资源
- onStop中仅执行stop和detachViews,未清除Media资源,导致MediaPlayer保留旧Surface引用
- 原代码中
media.release()时机过早,可能引发资源管理异常
修复方案
1. 跟随Activity重建重新初始化实例
在onCreate中判断是否为重建场景,先释放旧实例再创建新实例:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressBar = findViewById(R.id.progressBar); videoLayout = findViewById(R.id.videoLayout); try { String encodedUsername = URLEncoder.encode("admin", "UTF-8"); String encodedPassword = URLEncoder.encode("admin", "UTF-8"); rtspUrl = "rtsp://" + encodedUsername + ":" + encodedPassword + "@192.168.0.115:554/stream1"; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } // 重建时先释放旧资源 if (libVlc != null) { mediaPlayer.release(); libVlc.release(); } ArrayList<String> args = new ArrayList<>(); args.add("--aout=opensles"); args.add("--audio-time-stretch"); args.add("--rtsp-tcp"); args.add("--live-caching=0"); args.add("--file-caching=0"); args.add("--network-caching=200"); args.add("-vvv"); libVlc = new LibVLC(this, args); mediaPlayer = new MediaPlayer(libVlc); initListener(); }
2. 优化生命周期资源管理
将播放逻辑移到onResume,确保视图完全恢复后再绑定;在onStop中彻底清除Media资源:
@Override protected void onResume() { super.onResume(); videoLayout.clearFocus(); videoLayout.clearAnimation(); mediaPlayer.attachViews(videoLayout, null, false, false); media = new Media(libVlc, Uri.parse(rtspUrl)); media.setHWDecoderEnabled(true, true); mediaPlayer.setMedia(media); mediaPlayer.play(); } @Override protected void onStop() { super.onStop(); mediaPlayer.stop(); mediaPlayer.clearMedia(); // 清除Media资源 mediaPlayer.detachViews(); if (media != null) { media.release(); media = null; } }
3. 移除过早的media.release()
删除原onStart中media.release(),统一在onStop中处理Media释放。
4. 可选:避免Activity配置重建
在AndroidManifest中添加配置,减少因旋转等操作导致的Activity重建:
<activity android:name=".MainActivity" android:configChanges="orientation|screenSize">
验证效果
修改后测试设备休眠唤醒、APP后台切前台场景,视频流应能正常重新加载,不再出现EGL窗口创建失败的错误。
内容的提问来源于stack exchange,提问作者Mobile
相关产品推荐
相关产品推荐

