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
相关产品推荐
相关产品推荐

