Реализация RTMP-стриминга с мобильного устройства
RTMP — протокол 2002 года, придуманный Macromedia для Flash. В 2025 он всё ещё живёт, потому что его принимают YouTube Live, Twitch, Facebook Live, Restream и большинство медиасерверов. С мобильного устройства RTMP-стрим запускается через специализированные библиотеки — нативный iOS/Android это не умеет из коробки.
Почему RTMP, а не что-то другое
RTMP использует TCP, обеспечивает надёжную доставку данных и работает через большинство корпоративных файрволов (порт 1935). Задержка — 1-3 секунды, что приемлемо для стриминга на платформы. Для сверхнизкой задержки (<500 мс) берут WebRTC или SRT, но их не принимают напрямую YouTube и Twitch — нужен re-streamer на стороне сервера.
iOS: HaishinKit
Лучший Swift-нативный вариант. Без зависимости от FFmpeg, аппаратное кодирование через VideoToolbox.
Минимальная настройка:
import HaishinKit
let rtmpConnection = RTMPConnection()
let rtmpStream = RTMPStream(connection: rtmpConnection)
rtmpStream.videoSettings = VideoCodecSettings(
videoSize: CGSize(width: 1280, height: 720),
bitRate: 2_000_000,
profileLevel: kVTProfileLevel_H264_High_AutoLevel as String
)
rtmpStream.audioSettings = AudioCodecSettings(bitRate: 128_000)
// Привязываем камеру
let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
try rtmpStream.attachCamera(camera)
try rtmpStream.attachAudio(AVCaptureDevice.default(for: .audio))
// Превью
let hkView = MTHKView(frame: previewView.bounds)
hkView.videoGravity = .resizeAspectFill
rtmpStream.addOutput(hkView)
previewView.addSubview(hkView)
// Запуск
rtmpConnection.connect("rtmp://live.example.com/live")
rtmpStream.publish("stream_key")
Переподключение при обрыве: подписываемся на RTMPConnection.Event.rtmpStatus, при code == RTMPStatusCode.connectClosed — reconnectDelay(3) и повтор connect(). HaishinKit имеет встроенный reconnect механизм.
Мониторинг: rtmpStream.info.byteCount и RTMPStream.currentFPS — следим за реальным FPS. Если падает ниже 20 — сигнал плохого соединения.
Android: rtmp-rtsp-stream-client-java
Библиотека Pedro Vicente. Поддерживает Camera1, Camera2, CameraX, Screen capture. Аппаратное кодирование через MediaCodec.
val rtmpCamera = RtmpCamera2(binding.surfaceView, object : ConnectCheckerRtmp {
override fun onConnectionSuccessRtmp() { /* обновить UI */ }
override fun onConnectionFailedRtmp(reason: String) {
rtmpCamera.stopStream()
retryConnection()
}
override fun onDisconnectRtmp() { retryConnection() }
override fun onAuthErrorRtmp() { /* показать ошибку */ }
override fun onAuthSuccessRtmp() {}
override fun onNewBitrateRtmp(bitrate: Long) { updateBitrateUI(bitrate) }
})
// Подготовка (разрешение, битрейт, FPS, аудио)
rtmpCamera.prepareVideo(1280, 720, 30, 2_000_000) && rtmpCamera.prepareAudio(128_000, 44100, true)
rtmpCamera.startStream("rtmp://live.example.com/live/stream_key")
RtmpCamera2 принимает SurfaceView или TextureView. Для Jetpack Compose: AndroidView { RtmpCamera2(context, ...) }.
Биткрейт адаптация: rtmpCamera.setVideoBitrateOnFly(newBitrate) — меняем битрейт без перезапуска стрима.
Аутентификация RTMP
Twitch, YouTube, Facebook требуют stream key в URL: rtmp://live.twitch.tv/app/{stream_key}. Некоторые сервисы используют RTMP auth: rtmp://user:pass@server/app/stream.
Для YouTube Live дополнительно нужен rtmps:// (TLS): HaishinKit поддерживает, rtmp-rtsp-stream-client-java — через RtmpsCamera2.
Типичные проблемы
Задержка при старте. Первые секунды стрим нестабилен — SPS/PPS NAL units ещё не кешированы на сервере. Решение: на iOS устанавливаем kVTCompressionPropertyKey_AllowFrameReordering: false — B-frames отключены, задержка снижается.
Видео без звука у зрителей. Аудио-поток не инициализировался до начала трансляции. Убеждаемся что prepareAudio() вызван до startStream().
Чёрный экран на старте. AVCaptureSession не успела инициализироваться — startStream() вызван до startPreview(). Порядок важен: attachCamera → startPreview → rtmpConnection.connect → rtmpStream.publish.
Крэш на background. AVCaptureSession должна быть остановлена при applicationDidEnterBackground если трансляция не должна продолжаться в фоне. Если должна — нужен UIBackgroundTask и соответствующий UIBackgroundModes: audio (для аудио-трека).
Сроки
Интеграция RTMP-стриминга на одну платформу с превью, подключением к конкретному серверу и обработкой ошибок подключения — 2-3 рабочих дня.







