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

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资源,导致新视图无法绑定。

具体问题点

  1. LibVLC和MediaPlayer仅在首次onCreate初始化,Activity重建时复用旧实例,旧实例仍持有已失效的窗口资源
  2. onStop中仅执行stop和detachViews,未清除Media资源,导致MediaPlayer保留旧Surface引用
  3. 原代码中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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:25:55