Riduce reload MediaMTX in live e allinea ingest mobile al preset qualità.

Evita patch recording ripetute che distruggevano i muxer HLS (500 ops) e fa encoderare 1080p/720p con codec e bitrate coerenti al wizard.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-21 22:17:18 +02:00
parent e20c2ae619
commit 7d5d66840f
15 changed files with 195 additions and 40 deletions

View File

@@ -166,6 +166,7 @@ data class StreamSessionDto(
@Json(name = "privacy_status") val privacyStatus: String? = null,
@Json(name = "target_bitrate") val targetBitrate: Int? = null,
@Json(name = "target_fps") val targetFps: Int? = null,
@Json(name = "quality_preset") val qualityPreset: String? = null,
@Json(name = "started_at") val startedAt: String? = null,
val score: ScoreStateDto? = null,
) {
@@ -183,6 +184,7 @@ data class StreamSessionDto(
privacyStatus = privacyStatus ?: "public",
targetBitrate = targetBitrate ?: 2_500_000,
targetFps = targetFps ?: 30,
qualityPreset = qualityPreset ?: "720p_30_2.5mbps",
startedAt = startedAt,
score = score?.toDomain(),
)

View File

@@ -125,6 +125,7 @@ data class StreamSession(
val privacyStatus: String = "public",
val targetBitrate: Int = 2_500_000,
val targetFps: Int = 30,
val qualityPreset: String = "720p_30_2.5mbps",
val startedAt: String? = null,
val score: ScoreState? = null,
) {

View File

@@ -286,16 +286,21 @@ class LiveBroadcastEngine(
if (stream.isStreaming) stream.stopStream()
if (stream.isOnPreview) stream.stopPreview()
val h264Level = if (cfg.height > 720) {
MediaCodecInfo.CodecProfileLevel.AVCLevel41
} else {
MediaCodecInfo.CodecProfileLevel.AVCLevel31
}
val prepared = runCatching {
stream.prepareVideo(
width = cfg.width,
height = cfg.height,
bitrate = cfg.videoBitrate,
fps = cfg.fps,
iFrameInterval = 1,
iFrameInterval = 2,
rotation = cfg.rotation,
profile = MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline,
level = MediaCodecInfo.CodecProfileLevel.AVCLevel31,
level = h264Level,
) && stream.prepareAudio(48_000, false, cfg.audioBitrate)
}.getOrElse {
Log.w(TAG, "preparePipeline: ${it.message}")

View File

@@ -0,0 +1,26 @@
package com.matchlivetv.match_live_tv.streaming
import com.matchlivetv.match_live_tv.domain.StreamSession
/** Risoluzione encoder allineata al preset qualità scelto in wizard. */
object StreamVideoPreset {
data class Dimensions(val width: Int, val height: Int)
fun dimensions(qualityPreset: String?): Dimensions =
if (qualityPreset?.startsWith("1080p") == true) {
Dimensions(1920, 1080)
} else {
Dimensions(1280, 720)
}
fun broadcastConfig(session: StreamSession, rtmpUrl: String): BroadcastConfig {
val dims = dimensions(session.qualityPreset)
return BroadcastConfig(
rtmpUrl = rtmpUrl,
width = dims.width,
height = dims.height,
videoBitrate = session.targetBitrate,
fps = session.targetFps,
)
}
}

View File

@@ -37,6 +37,7 @@ import com.matchlivetv.match_live_tv.domain.Match
import com.matchlivetv.match_live_tv.domain.MatchScoringRules
import com.matchlivetv.match_live_tv.domain.StreamSession
import com.matchlivetv.match_live_tv.streaming.BroadcastConfig
import com.matchlivetv.match_live_tv.streaming.StreamVideoPreset
import com.matchlivetv.match_live_tv.streaming.BroadcastPhase
import com.matchlivetv.match_live_tv.streaming.LivePreviewView
import com.matchlivetv.match_live_tv.streaming.overlay.CompactScoreboardState
@@ -83,13 +84,8 @@ fun BroadcastScreen(
scoringRules?.let { LiveScoreActions(it, container.scoreController, dialogHost) }
}
fun broadcastConfig(loaded: StreamSession): BroadcastConfig = BroadcastConfig(
rtmpUrl = loaded.rtmpIngestUrl.orEmpty(),
width = 1280,
height = 720,
videoBitrate = loaded.targetBitrate,
fps = loaded.targetFps,
)
fun broadcastConfig(loaded: StreamSession): BroadcastConfig =
StreamVideoPreset.broadcastConfig(loaded, loaded.rtmpIngestUrl.orEmpty())
suspend fun stopStreamPermanently() {
runCatching { container.sessionRepository.stopSession(sessionId) }