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

PiP激活时打开视频致MPV崩溃的解决求助

问题描述

当PiP(画中画)处于激活状态时,打开新视频会导致MPV播放器崩溃;若停止初始化MPV则无法显示视频画面。需要解决方案,或了解如何在启动MPV Activity前关闭PiP并重新初始化。

相关代码

Fragment中打开MPV Activity的代码

private fun playFile(filepath: String) {
 val i: Intent
    if (filepath.startsWith("content://")) {
        i = Intent(Intent.ACTION_VIEW, Uri.parse(filepath))
    } else {
        i = Intent()
        i.putExtra("filepath", filepath)
    }
    i.setClass(requireContext(), MPVActivity::class.java)
    playerLauncher.launch(i)
}

MPV Activity的onCreate方法代码

override fun onCreate(icicle: Bundle?) { super.onCreate(icicle)    
Utils.copyAssets(this)
    BackgroundPlaybackService.createNotificationChannel(this)

    binding = PlayerBinding.inflate(layoutInflater)
    setContentView(binding.root)

    // Init controls to be hidden and view fullscreen
    hideControls()

    // Initialize listeners for the player view
    initListeners()

    gestures = TouchGestures(this)

    // set up initial UI state
    readSettings()
    onConfigurationChanged(resources.configuration)
    run {
        // edge-to-edge & immersive mode
        WindowCompat.setDecorFitsSystemWindows(window, false)
        val insetsController = WindowCompat.getInsetsController(window, window.decorView)
        insetsController.systemBarsBehavior =
            WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
    }
    if (!packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE))
        binding.topPiPBtn.visibility = View.GONE
    if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN))
        binding.topLockBtn.visibility = View.GONE

    if (showMediaTitle)
        binding.controlsTitleGroup.visibility = View.VISIBLE

    updateOrientation(true)

    // Parse the intent
    val filepath = parsePathFromIntent(intent)
    if (intent.action == Intent.ACTION_VIEW) {
        parseIntentExtras(intent.extras)
    }

    if (filepath == null) {
        Log.e(TAG, "No file given, exiting")
        showToast(getString(R.string.error_no_file))
        finishWithResult(RESULT_CANCELED)
        return
    }

    player.addObserver(this)
    player.initialize(filesDir.path, cacheDir.path)

    player.playFile(filepath)
    mediaSession = initMediaSession()
    updateMediaSession()
    BackgroundPlaybackService.mediaToken = mediaSession?.sessionToken
    audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
    volumeControlStream = STREAM_TYPE
    // Handle audio focus
    val req = with (AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN)) {
        setAudioAttributes(with (AudioAttributesCompat.Builder()) {
            // N.B.: libmpv may use different values in ao_audiotrack, but here we always pretend to be music.
            setUsage(AudioAttributesCompat.USAGE_MEDIA)
            setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)
            build()
        })
        setOnAudioFocusChangeListener(audioFocusChangeListener)
        build()
    }
    val res = AudioManagerCompat.requestAudioFocus(audioManager!!, req)
    if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        audioFocusRequest = req
    } else {
        Log.v(TAG, "Audio focus not granted")
        if (!ignoreAudioFocus)
            onloadCommands.add(arrayOf("set", "pause", "yes"))
    }
}

崩溃日志

2024-10-07 21:40:19.476 18179-18179 mpv is.xyz.mpv E mpv is already initialized
2024-10-07 21:40:19.478 18179-18216 gralloc4 is.xyz.mpv I @set_metadata: update dataspace from GM (0x00000000 -> 0x08010000)
2024-10-07 21:40:19.478 18179-18511 gralloc4 is.xyz.mpv I @set_metadata: update dataspace from GM (0x00000000 -> 0x08010000)
2024-10-07 21:40:19.510 18179-18511 libc is.xyz.mpv A Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 18511 (Thread-7), pid 18179 (is.xyz.mpv)
2024-10-07 21:40:19.511 18179-18190 libc++abi is.xyz.mpv E Pure virtual function called!
2024-10-07 21:40:19.669 18179-18234 libc is.xyz.mpv A FORTIFY: pthread_mutex_lock called on a destroyed mutex (0x7ee314ea68)
2024-10-07 21:40:19.669 18179-18235 libc is.xyz.mpv A FORTIFY: pthread_mutex_lock called on a destroyed mutex (0x7ee314ea68)

解决方案

1. 启动MPV前关闭PiP并释放旧播放器实例

崩溃日志核心报错是mpv is already initialized,说明PiP激活时旧MPV实例未被正确释放,新Activity重复初始化导致资源冲突。

实现步骤:

  • 在Fragment启动MPV前,检查并关闭所有处于运行状态的MPVActivity(包括PiP状态):
    private fun playFile(filepath: String) {
        // 关闭已运行的MPVActivity实例
        val activityManager = requireContext().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        for (task in activityManager.appTasks) {
            val info = task.taskInfo
            if (info.topActivity?.className == MPVActivity::class.java.name) {
                task.finishAndRemoveTask()
                break
            }
        }
    
        // 原有启动逻辑
        val i: Intent
        if (filepath.startsWith("content://")) {
            i = Intent(Intent.ACTION_VIEW, Uri.parse(filepath))
        } else {
            i = Intent()
            i.putExtra("filepath", filepath)
        }
        i.setClass(requireContext(), MPVActivity::class.java)
        playerLauncher.launch(i)
    }
    
  • 在MPVActivity中添加销毁逻辑,确保退出时释放所有资源:
    override fun onDestroy() {
        super.onDestroy()
        player.removeObserver(this)
        player.destroy() // 调用MPV底层销毁方法释放资源
        mediaSession?.release()
        audioFocusRequest?.let {
            AudioManagerCompat.abandonAudioFocusRequest(audioManager!!, it)
        }
    }
    

2. 修改MPVActivity初始化逻辑,避免重复初始化

在onCreate中先检查播放器状态,已初始化则先销毁再重新创建:

// 在player.addObserver(this)前添加
if (player.isInitialized) {
    player.removeObserver(this)
    player.destroy()
}
player.addObserver(this)
player.initialize(filesDir.path, cacheDir.path)

3. 优化PiP状态下的视频切换逻辑(复用现有实例)

如果希望保留切换流畅性,可设置MPVActivity为单实例模式,直接在现有实例中处理新播放请求:

  • 在Manifest中设置启动模式:
    <activity
        android:name=".MPVActivity"
        android:launchMode="singleTop"
        ... />
    
  • 实现onNewIntent处理新播放请求:
    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        setIntent(intent)
        // 退出PiP状态
        if (isInPictureInPictureMode) {
            exitPictureInPictureMode()
        }
        // 切换播放新视频
        val filepath = parsePathFromIntent(intent)
        if (filepath != null) {
            player.stop()
            player.playFile(filepath)
            updateMediaSession()
        }
    }
    

内容的提问来源于stack exchange,提问作者Waseem Akram

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:29:52