核心原理
在 Android 上搭建 RTSP 服务器,主要有两种思路:

(图片来源网络,侵删)
- 使用成熟的第三方库(推荐):这是最简单、最稳定、功能最全的方法,这些库已经封装了复杂的网络协议、音视频编码和打包逻辑,你只需要调用几行 API 就能启动服务器。绝大多数开发者都采用这种方式。
- 从零开始使用 NDK 和 FFmpeg(不推荐):理论上,你可以使用 Android NDK 编译 FFmpeg,然后通过 JNI 调用它来创建 RTSP 服务器,这种方法极其复杂,需要深入理解 FFmpeg、RTSP 协议和 Android NDK 开发,调试困难,不适用于快速开发。
本文将重点介绍第一种方法,使用目前最流行的库 rtsp-server-lib。
准备工作:添加依赖
在你的 Android 项目的 app/build.gradle 文件中,添加 rtsp-server-lib 库的依赖。
dependencies {
// ... 其他依赖
// RTSP 服务器库
implementation 'com.pedro.encoder:rtsp-server-lib:2.0.2' // 请使用最新版本
}
别忘了在 AndroidManifest.xml 中添加必要的权限:
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 如果需要使用摄像头,需要相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 如果需要录音,需要麦克风权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 如果需要将推流画面显示在界面上,需要存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Android 13+ 需要通知权限 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 在 application 标签内声明一个 Service -->
<application ...>
<service android:name="com.pedro.rtplibrary.rtmp.RtmpService"
android:foregroundServiceType="mediaProjection" />
<service android:name="com.pedro.rtplibrary.rtsp.RtspService"
android:foregroundServiceType="mediaProjection" />
</application>
实现步骤:搭建 RTSP 服务器
我们将创建一个简单的 Activity,它包含一个按钮来启动/停止 RTSP 服务器,并使用 SurfaceView 来预览摄像头画面。

(图片来源网络,侵删)
布局文件 (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
<Button
android:id="@+id/btn_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="50dp"
android:text="Start Server" />
</RelativeLayout>
Activity 代码 (MainActivity.kt)
package com.example.rtspserverdemo
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.SurfaceHolder
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.pedro.rtplibrary.rtsp.RtspServerCamera1
import com.pedro.rtplibrary.view.OpenGlView
import com.pedro.rtsp.utils.ConnectCheckerRtsp
import java.util.*
class MainActivity : AppCompatActivity(), SurfaceHolder.Callback, ConnectCheckerRtsp {
private val TAG = "MainActivity"
private lateinit var btnToggle: Button
private lateinit var surfaceView: SurfaceView
private lateinit var rtspServerCamera1: RtspServerCamera1
// 请求权限的回调
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
// 权限被授予,可以启动服务器
startRtspServer()
} else {
// 权限被拒绝
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnToggle = findViewById(R.id.btn_toggle)
surfaceView = findViewById(R.id.surfaceView)
// 设置 SurfaceView 的回调
surfaceView.holder.addCallback(this)
// 初始化 RTSP 服务器
rtspServerCamera1 = RtspServerCamera1(surfaceView.holder, this)
rtspServerCamera1.setVideoBitrate(800 * 1000) // 设置视频码率 (800 kbps)
rtspServerCamera1.setAudioBitrate(128 * 1000) // 设置音频码率 (128 kbps)
rtspServerCamera1.setVideoEncoderEnabled(true) // 启用视频编码
rtspServerCamera1.setAudioEncoderEnabled(true) // 启用音频编码
// 设置服务器端口 (默认 8554)
rtspServerCamera1.setPort(8554)
btnToggle.setOnClickListener {
if (rtspServerCamera1.isStreaming) {
stopRtspServer()
} else {
// 检查并请求必要权限
checkAndRequestPermissions()
}
}
}
/**
* 检查并请求权限
*/
private fun checkAndRequestPermissions() {
val permissions = mutableListOf<String>()
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.CAMERA)
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.RECORD_AUDIO)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.POST_NOTIFICATIONS)
}
}
if (permissions.isNotEmpty()) {
requestPermissionLauncher.launch(permissions.toTypedArray())
} else {
startRtspServer()
}
}
/**
* 启动 RTSP 服务器
*/
private fun startRtspServer() {
try {
// 生成一个随机的流 ID,避免多设备连接时冲突
val streamId = UUID.randomUUID().toString()
rtspServerCamera1.startServer(streamId)
btnToggle.text = "Stop Server"
Toast.makeText(this, "RTSP Server started. Stream URL: rtsp://your_phone_ip:8554/$streamId", Toast.LENGTH_LONG).show()
} catch (e: Exception) {
Log.e(TAG, "Error starting RTSP server", e)
Toast.makeText(this, "Error starting server: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
/**
* 停止 RTSP 服务器
*/
private fun stopRtspServer() {
rtspServerCamera1.stopServer()
btnToggle.text = "Start Server"
Toast.makeText(this, "RTSP Server stopped", Toast.LENGTH_SHORT).show()
}
// --- SurfaceHolder.Callback ---
override fun surfaceCreated(holder: SurfaceHolder) {
Log.i(TAG, "Surface created")
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
Log.i(TAG, "Surface changed")
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
Log.i(TAG, "Surface destroyed")
// 当 Surface 被销毁时,停止服务器
if (rtspServerCamera1.isStreaming) {
stopRtspServer()
}
}
// --- ConnectCheckerRtsp ---
override fun onConnectionStartedRtsp() {
Log.i(TAG, "onConnectionStartedRtsp")
}
override fun onConnectionSuccessRtsp() {
Log.i(TAG, "onConnectionSuccessRtsp")
}
override fun onConnectionFailedRtsp(reason: String) {
Log.e(TAG, "onConnectionFailedRtsp: $reason")
runOnUiThread {
Toast.makeText(this, "Connection failed: $reason", Toast.LENGTH_SHORT).show()
}
}
override fun onDisconnectRtsp() {
Log.i(TAG, "onDisconnectRtsp")
}
override fun onAuthErrorRtsp() {
(图片来源网络,侵删)
