Android (Compose)使用 LibVLC 播放 RTSP 视频流
问题描述
在 Android 应用中播放 RTSP 视频流时,常见问题:
- 无法播放:RTSP 地址正确,但播放器黑屏
- 延迟过高:实时性差,画面延迟数秒
- 播放卡顿:画面时断时续
- 内存泄漏:长时间播放后内存持续增长
依赖版本
kotlin
// app/build.gradle.kts
dependencies {
implementation("org.videolan.android:libvlc-all:3.6.5")
}
问题原因分析
与 iOS 端的关键差异
| 项目 | iOS (MobileVLCKit) | Android (LibVLC) |
|---|---|---|
| Media 创建 | Media(url) 字符串 |
需要 Uri.parse(url) |
| 硬件解码 | 默认启用 | 需要手动 setHWDecoderEnabled |
| 视图绑定 | player.drawable = view |
attachViews(layout, null, false, false) |
| Media 释放 | 自动管理 | 设置后需 media.release() |
常见错误写法
kotlin
// ❌ 错误:直接用字符串创建 Media
val media = Media(libVLC, rtspUrl)
// ❌ 错误:未启用硬件解码
// ❌ 错误:attachViews 参数不对
mediaPlayer.attachViews(videoLayout, null, true, true)
// ❌ 错误:选项太多太复杂
media.addOption(":network-caching=3000")
media.addOption(":rtsp-tcp")
media.addOption(":live-caching=3000")
// ... 更多选项
解决方案
完整代码实现
1. 权限配置
xml
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:networkSecurityConfig="@xml/network_security_config">
xml
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
2. VlcPlayer.kt(Compose 组件)
kotlin
package com.example.rtspplayer.ui.components
import android.net.Uri
import android.util.Log
import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import org.videolan.libvlc.LibVLC
import org.videolan.libvlc.Media
import org.videolan.libvlc.MediaPlayer
import org.videolan.libvlc.util.VLCVideoLayout
private const val TAG = "VlcPlayer"
@Composable
fun VlcPlayer(
mediaUrl: String,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
Log.d(TAG, "播放: $mediaUrl")
val libVLC = remember { LibVLC(context) }
val mediaPlayer = remember {
MediaPlayer(libVLC).apply {
setEventListener { event ->
when (event.type) {
MediaPlayer.Event.Opening -> Log.d(TAG, "打开中...")
MediaPlayer.Event.Playing -> Log.d(TAG, "播放中")
MediaPlayer.Event.Buffering -> Log.d(TAG, "缓冲 ${event.buffering}%")
MediaPlayer.Event.EncounteredError -> Log.e(TAG, "播放错误")
MediaPlayer.Event.Stopped -> Log.d(TAG, "已停止")
}
}
}
}
val videoLayout = remember {
VLCVideoLayout(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
}
DisposableEffect(mediaUrl) {
// 1. 绑定视图(参数:layout, subitems, esSelected)
mediaPlayer.attachViews(videoLayout, null, false, false)
// 2. 创建 Media(关键:使用 Uri.parse)
val media = Media(libVLC, Uri.parse(mediaUrl))
// 3. 启用硬件解码
media.setHWDecoderEnabled(true, false)
// 4. 设置 RTSP 播放选项
media.addOption(":network-caching=3000")
media.addOption(":rtsp-tcp")
media.addOption(":live-caching=3000")
media.addOption(":file-caching=3000")
media.addOption(":rtsp-frame-buffer-size=5000000")
media.addOption(":rtsp-max-frames=5000")
// 5. 设置后立即释放
mediaPlayer.media = media
media.release()
// 6. 播放
mediaPlayer.play()
onDispose {
mediaPlayer.stop()
mediaPlayer.detachViews()
}
}
AndroidView(factory = { videoLayout }, modifier = modifier)
}
3. 传统 Activity 方式
kotlin
class RtspPlayerActivity : AppCompatActivity() {
private lateinit var libVLC: LibVLC
private lateinit var mediaPlayer: MediaPlayer
private lateinit var videoLayout: VLCVideoLayout
private val rtspUrl = "rtsp://admin:123456@192.168.1.100:554/stream1"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
videoLayout = findViewById(R.id.videoLayout)
libVLC = LibVLC(this)
mediaPlayer = MediaPlayer(libVLC)
}
override fun onStart() {
super.onStart()
mediaPlayer.attachViews(videoLayout, null, false, false)
val media = Media(libVLC, Uri.parse(rtspUrl))
media.setHWDecoderEnabled(true, false)
media.addOption(":network-caching=3000")
media.addOption(":rtsp-tcp")
media.addOption(":live-caching=3000")
media.addOption(":file-caching=3000")
media.addOption(":rtsp-frame-buffer-size=5000000")
media.addOption(":rtsp-max-frames=5000")
mediaPlayer.media = media
media.release()
mediaPlayer.play()
}
override fun onStop() {
super.onStop()
mediaPlayer.stop()
mediaPlayer.detachViews()
}
override fun onDestroy() {
super.onDestroy()
mediaPlayer.release()
libVLC.release()
}
}
关键点总结
1. Media 创建必须用 Uri.parse
kotlin
val media = Media(libVLC, Uri.parse(rtspUrl)) // ✅ 正确
// val media = Media(libVLC, rtspUrl) // ❌ 错误
2. 启用硬件解码
kotlin
media.setHWDecoderEnabled(true, false) // ✅ 必须
3. attachViews 参数
kotlin
mediaPlayer.attachViews(videoLayout, null, false, false) // ✅ 正确参数
// mediaPlayer.attachViews(videoLayout, null, true, true) // ❌ 错误参数
4. Media 设置后立即释放
kotlin
mediaPlayer.media = media
media.release() // ✅ 立即释放
5. 缓存值根据网络调整
kotlin
// RTSP 播放完整选项配置
media.addOption(":network-caching=3000") // 网络缓存 3000ms
media.addOption(":rtsp-tcp") // 使用 TCP 传输
media.addOption(":live-caching=3000") // 直播缓存 3000ms
media.addOption(":file-caching=3000") // 文件缓存 3000ms
media.addOption(":rtsp-frame-buffer-size=5000000") // 帧缓冲大小
media.addOption(":rtsp-max-frames=5000") // 最大帧数
避坑指南
- 不要设置过多选项 :只需
network-caching即可,其他选项可能干扰播放 - 生命周期管理 :
onStop中stop()和detachViews(),onDestroy中release() - 状态监听 :通过
setEventListener监听播放状态,处理错误情况