凌峰创科服务平台

Android如何搭建RTSP服务器?

核心原理

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

Android如何搭建RTSP服务器?-图1
(图片来源网络,侵删)
  1. 使用成熟的第三方库(推荐):这是最简单、最稳定、功能最全的方法,这些库已经封装了复杂的网络协议、音视频编码和打包逻辑,你只需要调用几行 API 就能启动服务器。绝大多数开发者都采用这种方式。
  2. 从零开始使用 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 来预览摄像头画面。

Android如何搭建RTSP服务器?-图2
(图片来源网络,侵删)

布局文件 (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() {
Android如何搭建RTSP服务器?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇