Alleggerisce il relay YouTube e rilascia Android 2.0.8.
ffmpeg fa solo remux copy A/V; le app fissano AAC 48 kHz mono; sync deploy non cancella più garage.prod.toml. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -116,31 +116,30 @@ module Streams
|
|||||||
raise Error, "ffmpeg non disponibile: #{e.message}"
|
raise Error, "ffmpeg non disponibile: #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# RTMP grezzo da telefono (overlay bruciato lato app); fallback HLS via proxy Rails.
|
# Remux verso YouTube: video+audio copy (AAC già da app/slate a 48k mono).
|
||||||
|
# HLS (ADTS) → FLV richiede -bsf:a aac_adtstoasc; RTMP ha già ASC in FLV tags.
|
||||||
def youtube_ffmpeg_args(intake_source, output)
|
def youtube_ffmpeg_args(intake_source, output)
|
||||||
mode, url = intake_source
|
mode, url = intake_source
|
||||||
if mode == :rtmp
|
common_head = [
|
||||||
return [
|
|
||||||
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "warning",
|
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "warning",
|
||||||
"-fflags", "+genpts+discardcorrupt",
|
"-fflags", "+genpts+discardcorrupt"
|
||||||
|
]
|
||||||
|
copy_tail = ["-c:v", "copy", "-c:a", "copy", "-f", "flv", output]
|
||||||
|
|
||||||
|
if mode == :rtmp
|
||||||
|
return common_head + [
|
||||||
"-analyzeduration", "10000000", "-probesize", "10000000",
|
"-analyzeduration", "10000000", "-probesize", "10000000",
|
||||||
"-rw_timeout", "15000000",
|
"-rw_timeout", "15000000",
|
||||||
"-i", url,
|
"-i", url
|
||||||
"-c:v", "copy",
|
] + copy_tail
|
||||||
"-c:a", "aac", "-b:a", "128k", "-ar", "48000", "-ac", "1",
|
|
||||||
"-bsf:a", "aac_adtstoasc",
|
|
||||||
"-f", "flv", output
|
|
||||||
]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
[
|
common_head + [
|
||||||
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "warning",
|
|
||||||
"-fflags", "+genpts+discardcorrupt",
|
|
||||||
"-rw_timeout", "15000000",
|
"-rw_timeout", "15000000",
|
||||||
"-live_start_index", "-1",
|
"-live_start_index", "-1",
|
||||||
"-i", url,
|
"-i", url,
|
||||||
"-c:v", "copy",
|
"-c:v", "copy",
|
||||||
"-c:a", "aac", "-b:a", "128k", "-ar", "48000", "-ac", "1",
|
"-c:a", "copy",
|
||||||
"-bsf:a", "aac_adtstoasc",
|
"-bsf:a", "aac_adtstoasc",
|
||||||
"-f", "flv", output
|
"-f", "flv", output
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
RSpec.describe Streams::YoutubeRelay do
|
||||||
|
describe ".youtube_ffmpeg_args (private)" do
|
||||||
|
def args(mode, url)
|
||||||
|
described_class.send(:youtube_ffmpeg_args, [mode, url], "rtmps://a.rtmps.youtube.com/live2/key")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "remuxes RTMP with video and audio copy (no AAC re-encode)" do
|
||||||
|
cmd = args(:rtmp, "rtmp://mediamtx:1935/live/match_x")
|
||||||
|
expect(cmd).to include("-c:v", "copy", "-c:a", "copy", "-f", "flv")
|
||||||
|
expect(cmd).not_to include("aac")
|
||||||
|
expect(cmd.join(" ")).not_to include("-b:a")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "remuxes HLS with AAC bitstream filter for FLV" do
|
||||||
|
cmd = args(:hls, "http://mediamtx:8888/live/match_x/index.m3u8")
|
||||||
|
expect(cmd).to include("-c:v", "copy", "-c:a", "copy", "-bsf:a", "aac_adtstoasc", "-f", "flv")
|
||||||
|
expect(cmd).not_to include("-b:a")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -202,7 +202,7 @@ MediaMTX :1935 — path live/match_{uuid}
|
|||||||
├──► HLS :8888 ──► edge /hls/ ──► spettatori web
|
├──► HLS :8888 ──► edge /hls/ ──► spettatori web
|
||||||
└──► (solo platform=youtube)
|
└──► (solo platform=youtube)
|
||||||
Streams::YoutubeRelay in Sidekiq
|
Streams::YoutubeRelay in Sidekiq
|
||||||
ffmpeg: -c:v copy, -c:a aac ──► YouTube RTMPS
|
ffmpeg: -c:v copy, -c:a copy ──► YouTube RTMPS
|
||||||
```
|
```
|
||||||
|
|
||||||
| Componente | Ruolo |
|
| Componente | Ruolo |
|
||||||
@@ -210,7 +210,7 @@ MediaMTX :1935 — path live/match_{uuid}
|
|||||||
| Path dinamici via API :9997 | `Mediamtx::Client` (`create_path`, recording patch, …) |
|
| Path dinamici via API :9997 | `Mediamtx::Client` (`create_path`, recording patch, …) |
|
||||||
| `alwaysAvailable` + slate | Copertina `/slates/offline.mp4` senza interrompere HLS |
|
| `alwaysAvailable` + slate | Copertina `/slates/offline.mp4` senza interrompere HLS |
|
||||||
| Overlay tabellone | **App nativa** (non più ricodifica server) |
|
| Overlay tabellone | **App nativa** (non più ricodifica server) |
|
||||||
| `Streams::YoutubeRelay` | Legge RTMP (o HLS) da MediaMTX; copy video + AAC → YouTube |
|
| `Streams::YoutubeRelay` | Legge RTMP (o HLS) da MediaMTX; remux copy A/V → YouTube |
|
||||||
| `Mediamtx::PublisherSync` | Publisher online/offline, go_live, recording on/off |
|
| `Mediamtx::PublisherSync` | Publisher online/offline, go_live, recording on/off |
|
||||||
|
|
||||||
Dettaglio pause, player e ruolo ffmpeg: [`LIVE_STREAMING.md`](LIVE_STREAMING.md).
|
Dettaglio pause, player e ruolo ffmpeg: [`LIVE_STREAMING.md`](LIVE_STREAMING.md).
|
||||||
@@ -281,7 +281,7 @@ App Android ◄──── REST API (/api/v1/...) ────► Rails
|
|||||||
Sidekiq ◄── Redis ◄──────┘
|
Sidekiq ◄── Redis ◄──────┘
|
||||||
│
|
│
|
||||||
├── HealthMonitorJob, UploadJob (merge/thumbnail ffmpeg)
|
├── HealthMonitorJob, UploadJob (merge/thumbnail ffmpeg)
|
||||||
├── YoutubeRelay (ffmpeg copy+AAC, solo dirette YouTube)
|
├── YoutubeRelay (ffmpeg remux copy A/V, solo dirette YouTube)
|
||||||
└── PublishToYoutubeJob, purge replay, ecc.
|
└── PublishToYoutubeJob, purge replay, ecc.
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -419,3 +419,4 @@ cd infra && cp .env.example .env && docker compose up -d --build
|
|||||||
| 2026-06 | `RAILS_MAX_THREADS=5`; relay YouTube da MediaMTX (non via Puma) |
|
| 2026-06 | `RAILS_MAX_THREADS=5`; relay YouTube da MediaMTX (non via Puma) |
|
||||||
| 2026-06 | Cleanup path MediaMTX orfani (`Mediamtx::CleanupOrphanPaths`); delete path sempre a fine diretta |
|
| 2026-06 | Cleanup path MediaMTX orfani (`Mediamtx::CleanupOrphanPaths`); delete path sempre a fine diretta |
|
||||||
| 2026-07 | Rimosso overlay server (`OverlayRelay`); tabellone bruciato in app; HLS su path camera (non `*_air`); `YoutubeRelay` = copy video + AAC |
|
| 2026-07 | Rimosso overlay server (`OverlayRelay`); tabellone bruciato in app; HLS su path camera (non `*_air`); `YoutubeRelay` = copy video + AAC |
|
||||||
|
| 2026-08 | `YoutubeRelay` = remux copy video+audio (niente ricodifica AAC); profilo app/slate AAC 48k mono |
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ MediaMTX espone **un unico flusso di uscita** per path (`live/match_{uuid}`):
|
|||||||
|
|
||||||
1. **Telefono in onda** → publisher RTMP (camera + **overlay grafico bruciato in app**).
|
1. **Telefono in onda** → publisher RTMP (camera + **overlay grafico bruciato in app**).
|
||||||
2. **Pausa / rete assente** → `alwaysAvailable` con file slate (`/slates/offline.mp4`) **senza interrompere** l’HLS verso il sito.
|
2. **Pausa / rete assente** → `alwaysAvailable` con file slate (`/slates/offline.mp4`) **senza interrompere** l’HLS verso il sito.
|
||||||
3. **YouTube** (solo se `platform=youtube`) → `Streams::YoutubeRelay` in **Sidekiq**: un processo `ffmpeg` legge RTMP/HLS da MediaMTX e inoltra a YouTube (`-c:v copy`, audio ricodificato in AAC).
|
3. **YouTube** (solo se `platform=youtube`) → `Streams::YoutubeRelay` in **Sidekiq**: un processo `ffmpeg` legge RTMP/HLS da MediaMTX e inoltra a YouTube (`-c:v copy`, `-c:a copy`; l’AAC 48 kHz mono è già prodotto dall’app / slate).
|
||||||
|
|
||||||
Il telefono **non** invia la copertina di pausa: risparmia banda; lo switch slate ↔ camera è lato MediaMTX.
|
Il telefono **non** invia la copertina di pausa: risparmia banda; lo switch slate ↔ camera è lato MediaMTX.
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ MediaMTX path live/match_{uuid}
|
|||||||
├──► HLS (segmenti ~1s) ──► edge /hls/ → MediaMTX ──► player web (HLS.js)
|
├──► HLS (segmenti ~1s) ──► edge /hls/ → MediaMTX ──► player web (HLS.js)
|
||||||
└──► (solo platform=youtube)
|
└──► (solo platform=youtube)
|
||||||
Streams::YoutubeRelay
|
Streams::YoutubeRelay
|
||||||
ffmpeg: -c:v copy, -c:a aac ──► RTMPS YouTube
|
ffmpeg: -c:v copy, -c:a copy ──► RTMPS YouTube
|
||||||
```
|
```
|
||||||
|
|
||||||
| Responsabilità | Dove |
|
| Responsabilità | Dove |
|
||||||
@@ -93,7 +93,7 @@ La pagina live può mostrare badge HTML di stato (in onda / pausa / attesa); non
|
|||||||
|
|
||||||
| Quando | Cosa | Peso CPU |
|
| Quando | Cosa | Peso CPU |
|
||||||
|--------|------|----------|
|
|--------|------|----------|
|
||||||
| Diretta `platform=youtube` | `YoutubeRelay`: demux + **copy video** + AAC audio → RTMPS | Medio-basso **per diretta**, scala con N processi |
|
| Diretta `platform=youtube` | `YoutubeRelay`: demux + **copy video/audio** → RTMPS (remux) | Basso **per diretta**, scala con N processi |
|
||||||
| Diretta solo `matchlivetv` | Nessun ffmpeg in live | Quasi zero |
|
| Diretta solo `matchlivetv` | Nessun ffmpeg in live | Quasi zero |
|
||||||
| Fine diretta | `UploadFromSession`: concat segmenti (`-c copy`) | Picco breve |
|
| Fine diretta | `UploadFromSession`: concat segmenti (`-c copy`) | Picco breve |
|
||||||
| Post-process | `GenerateThumbnail`: un frame → JPEG | Trascurabile |
|
| Post-process | `GenerateThumbnail`: un frame → JPEG | Trascurabile |
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ Sull’app nativa l’overlay è composito in GPU **prima** dell’encode RTMP (
|
|||||||
2. `OverlayRenderer` → `OverlayCanvasRenderer` disegna gli elementi su bitmap trasparente.
|
2. `OverlayRenderer` → `OverlayCanvasRenderer` disegna gli elementi su bitmap trasparente.
|
||||||
3. Il bitmap viene applicato come filtro sul flusso RTMP (OpenGL su Android, HaishinKit su iOS).
|
3. Il bitmap viene applicato come filtro sul flusso RTMP (OpenGL su Android, HaishinKit su iOS).
|
||||||
|
|
||||||
MediaMTX e `YoutubeRelay` ricevono quindi un flusso già “brandizzato”; il relay YouTube fa solo `-c:v copy` (+ AAC).
|
MediaMTX e `YoutubeRelay` ricevono quindi un flusso già “brandizzato”; il relay YouTube fa solo remux (`-c copy` video e audio).
|
||||||
|
|
||||||
Elementi grafici (`OverlayCanvasRenderer`):
|
Elementi grafici (`OverlayCanvasRenderer`):
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# YouTube Live — checklist operativa
|
# YouTube Live — checklist operativa
|
||||||
|
|
||||||
**Aggiornato:** 2026-07-29
|
**Aggiornato:** 2026-07-29
|
||||||
Pipeline attuale: telefono → MediaMTX → `Streams::YoutubeRelay` (ffmpeg copy+AAC in Sidekiq) → YouTube. Overlay tabellone in **app**, non più `OverlayRelay` server.
|
Pipeline attuale: telefono → MediaMTX → `Streams::YoutubeRelay` (ffmpeg remux copy A/V in Sidekiq) → YouTube. Overlay tabellone in **app**, non più `OverlayRelay` server.
|
||||||
|
|
||||||
## Verifica rapida su server
|
## Verifica rapida su server
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Match Live TV supporta due modalità YouTube, legate al piano società:
|
|||||||
| **Premium Light** | `matchlivetv_light` | Canale YouTube **Match Live TV** (gestito da te) |
|
| **Premium Light** | `matchlivetv_light` | Canale YouTube **Match Live TV** (gestito da te) |
|
||||||
| **Premium Full** | `team` | Canale YouTube **della società** (OAuth del club) |
|
| **Premium Full** | `team` | Canale YouTube **della società** (OAuth del club) |
|
||||||
|
|
||||||
Flusso tecnico: telefono (camera + overlay app) → RTMP MediaMTX → `Streams::YoutubeRelay` (ffmpeg `-c:v copy`, AAC) → YouTube Live.
|
Flusso tecnico: telefono (camera + overlay app) → RTMP MediaMTX → `Streams::YoutubeRelay` (ffmpeg `-c:v copy -c:a copy`) → YouTube Live.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Se il telefono andasse diretto a YouTube (o a un ingest cloud senza hold), la li
|
|||||||
|
|
||||||
### Problema di capacità
|
### Problema di capacità
|
||||||
|
|
||||||
Ogni diretta `platform=youtube` avvia un processo `ffmpeg` in Sidekiq (`Streams::YoutubeRelay`: video **copy**, audio **AAC**).
|
Ogni diretta `platform=youtube` avvia un processo `ffmpeg` in Sidekiq (`Streams::YoutubeRelay`: video+audio **copy** / remux).
|
||||||
Il carico scala con **N processi**, non con gli spettatori HLS.
|
Il carico scala con **N processi**, non con gli spettatori HLS.
|
||||||
|
|
||||||
Sul box attuale (~4 CPU / 8 GB, stack completo sullo stesso host):
|
Sul box attuale (~4 CPU / 8 GB, stack completo sullo stesso host):
|
||||||
@@ -299,7 +299,7 @@ Finché D1 non è chiusa, non stimare bandwitdh né SLA del picco.
|
|||||||
|
|
||||||
## 7. Dimensionamento indicativo
|
## 7. Dimensionamento indicativo
|
||||||
|
|
||||||
Ipotesi: 720p, video copy, AAC mono — **~0.3–0.7 vCPU** medi per relay (picchi più alti su analyze/reconnect).
|
Ipotesi: 720p, remux copy A/V — **~0.15–0.4 vCPU** medi per relay (picchi su analyze/reconnect).
|
||||||
|
|
||||||
| Target live YT | Dedicated (relay) | Overflow tipico | Note |
|
| Target live YT | Dedicated (relay) | Overflow tipico | Note |
|
||||||
|----------------|-------------------|-----------------|------|
|
|----------------|-------------------|-----------------|------|
|
||||||
@@ -349,7 +349,7 @@ Questi numeri vanno **calibati** con un load test (N sessioni fake + ffmpeg cont
|
|||||||
### Fase 4 — Solo se necessario
|
### Fase 4 — Solo se necessario
|
||||||
|
|
||||||
- Secondo MediaMTX / sharding
|
- Secondo MediaMTX / sharding
|
||||||
- Audio copy se l’encoder telefono è già YouTube-compatibile (meno CPU)
|
- ~~Audio copy se l’encoder telefono è già YouTube-compatibile (meno CPU)~~ → fatto (app AAC 48k mono + relay `-c:a copy`)
|
||||||
- Dedicated più grosso come baseline (spesso più economico dell’overflow cronico ogni weekend)
|
- Dedicated più grosso come baseline (spesso più economico dell’overflow cronico ogni weekend)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ android {
|
|||||||
applicationId = "com.matchlivetv.match_live_tv"
|
applicationId = "com.matchlivetv.match_live_tv"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 28
|
versionCode = 29
|
||||||
versionName = "2.0.7-native"
|
versionName = "2.0.8-native"
|
||||||
|
|
||||||
val apiBaseUrl = project.findProperty("API_BASE_URL") as String?
|
val apiBaseUrl = project.findProperty("API_BASE_URL") as String?
|
||||||
?: "https://www.matchlivetv.it"
|
?: "https://www.matchlivetv.it"
|
||||||
|
|||||||
+11
-1
@@ -1,5 +1,9 @@
|
|||||||
package com.matchlivetv.match_live_tv.streaming
|
package com.matchlivetv.match_live_tv.streaming
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profilo RTMP allineato a MediaMTX slate + relay YouTube (copy audio/video).
|
||||||
|
* Audio: AAC 48 kHz mono 128 kbps — stesso contratto di `infra/scripts/generate_slate.sh`.
|
||||||
|
*/
|
||||||
data class BroadcastConfig(
|
data class BroadcastConfig(
|
||||||
val rtmpUrl: String,
|
val rtmpUrl: String,
|
||||||
val width: Int = 1280,
|
val width: Int = 1280,
|
||||||
@@ -11,7 +15,13 @@ data class BroadcastConfig(
|
|||||||
val portrait: Boolean = false,
|
val portrait: Boolean = false,
|
||||||
val maxReconnectAttempts: Int = 10,
|
val maxReconnectAttempts: Int = 10,
|
||||||
val reconnectDelayMs: Long = 5_000L,
|
val reconnectDelayMs: Long = 5_000L,
|
||||||
)
|
) {
|
||||||
|
companion object {
|
||||||
|
const val AUDIO_SAMPLE_RATE_HZ = 48_000
|
||||||
|
/** false = mono in RootEncoder `prepareAudio(sampleRate, isStereo, bitrate)`. */
|
||||||
|
const val AUDIO_STEREO = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class BroadcastPhase {
|
enum class BroadcastPhase {
|
||||||
IDLE,
|
IDLE,
|
||||||
|
|||||||
+5
-1
@@ -316,7 +316,11 @@ class LiveBroadcastEngine(
|
|||||||
rotation = cfg.rotation,
|
rotation = cfg.rotation,
|
||||||
profile = MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline,
|
profile = MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline,
|
||||||
level = h264Level,
|
level = h264Level,
|
||||||
) && stream.prepareAudio(48_000, false, cfg.audioBitrate)
|
) && stream.prepareAudio(
|
||||||
|
BroadcastConfig.AUDIO_SAMPLE_RATE_HZ,
|
||||||
|
BroadcastConfig.AUDIO_STEREO,
|
||||||
|
cfg.audioBitrate,
|
||||||
|
)
|
||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
Log.w(TAG, "preparePipeline: ${it.message}")
|
Log.w(TAG, "preparePipeline: ${it.message}")
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Profilo RTMP allineato a MediaMTX slate + YoutubeRelay (copy A/V).
|
||||||
|
/// Audio: AAC 48 kHz mono 128 kbps.
|
||||||
struct BroadcastConfig: Sendable {
|
struct BroadcastConfig: Sendable {
|
||||||
let rtmpUrl: String
|
let rtmpUrl: String
|
||||||
var width: Int = 1280
|
var width: Int = 1280
|
||||||
@@ -11,6 +13,9 @@ struct BroadcastConfig: Sendable {
|
|||||||
var portrait: Bool = false
|
var portrait: Bool = false
|
||||||
var maxReconnectAttempts: Int = 10
|
var maxReconnectAttempts: Int = 10
|
||||||
var reconnectDelayMs: Int = 5_000
|
var reconnectDelayMs: Int = 5_000
|
||||||
|
|
||||||
|
static let audioSampleRateHz: Float64 = 48_000
|
||||||
|
static let audioChannels: UInt32 = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BroadcastPhase: String, Sendable {
|
enum BroadcastPhase: String, Sendable {
|
||||||
|
|||||||
@@ -214,6 +214,14 @@ final class LiveBroadcastEngine: ObservableObject {
|
|||||||
if let microphone = AVCaptureDevice.default(for: .audio) {
|
if let microphone = AVCaptureDevice.default(for: .audio) {
|
||||||
try await mixer.attachAudio(microphone, track: 0)
|
try await mixer.attachAudio(microphone, track: 0)
|
||||||
}
|
}
|
||||||
|
// AAC 48 kHz mono — allineato ad Android, slate MediaMTX e YoutubeRelay (-c:a copy).
|
||||||
|
try await mixer.setAudioMixerSettings(
|
||||||
|
AudioMixerSettings(
|
||||||
|
sampleRate: BroadcastConfig.audioSampleRateHz,
|
||||||
|
channels: BroadcastConfig.audioChannels,
|
||||||
|
tracks: [0: AudioMixerTrackSettings(downmix: true, channelMap: [0])]
|
||||||
|
)
|
||||||
|
)
|
||||||
if let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
|
if let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
|
||||||
try await mixer.attachVideo(camera, track: 0) { videoUnit in
|
try await mixer.attachVideo(camera, track: 0) { videoUnit in
|
||||||
videoUnit.isVideoMirrored = false
|
videoUnit.isVideoMirrored = false
|
||||||
@@ -355,10 +363,10 @@ final class LiveBroadcastEngine: ObservableObject {
|
|||||||
var audioSettings = await stream.audioSettings
|
var audioSettings = await stream.audioSettings
|
||||||
audioSettings = AudioCodecSettings(
|
audioSettings = AudioCodecSettings(
|
||||||
bitRate: config.audioBitrate,
|
bitRate: config.audioBitrate,
|
||||||
downmix: audioSettings.downmix,
|
downmix: true,
|
||||||
channelMap: audioSettings.channelMap,
|
channelMap: [0],
|
||||||
sampleRate: 48_000,
|
sampleRate: BroadcastConfig.audioSampleRateHz,
|
||||||
format: audioSettings.format
|
format: .aac
|
||||||
)
|
)
|
||||||
try await stream.setAudioSettings(audioSettings)
|
try await stream.setAudioSettings(audioSettings)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,17 @@ if ssh "$SERVER" 'command -v rsync >/dev/null 2>&1'; then
|
|||||||
--exclude '.git' \
|
--exclude '.git' \
|
||||||
--exclude 'native/android/build' \
|
--exclude 'native/android/build' \
|
||||||
--exclude 'native/android/.gradle' \
|
--exclude 'native/android/.gradle' \
|
||||||
|
--exclude 'native/android/app/build' \
|
||||||
|
--exclude 'native/android/dist' \
|
||||||
|
--exclude 'native/android/keystore' \
|
||||||
|
--exclude 'native/android/keystore.properties' \
|
||||||
|
--exclude 'native/android/local.properties' \
|
||||||
--exclude 'backend/log' \
|
--exclude 'backend/log' \
|
||||||
--exclude 'backend/tmp' \
|
--exclude 'backend/tmp' \
|
||||||
--exclude 'backend/.bundle' \
|
--exclude 'backend/.bundle' \
|
||||||
--exclude 'infra/.env' \
|
--exclude 'infra/.env' \
|
||||||
|
--exclude 'infra/garage/garage.prod.toml' \
|
||||||
|
--exclude 'infra/garage/prod-credentials.env' \
|
||||||
--exclude 'log/' \
|
--exclude 'log/' \
|
||||||
--exclude 'node_modules' \
|
--exclude 'node_modules' \
|
||||||
"$SRC/" "$SERVER:$DEST/"
|
"$SRC/" "$SERVER:$DEST/"
|
||||||
@@ -24,7 +31,10 @@ else
|
|||||||
echo "rsync non disponibile sul server, uso tar+scp..."
|
echo "rsync non disponibile sul server, uso tar+scp..."
|
||||||
tar -C "$SRC" -czf /tmp/matchlivetv-deploy.tar.gz \
|
tar -C "$SRC" -czf /tmp/matchlivetv-deploy.tar.gz \
|
||||||
--exclude='./native/android/build' --exclude='./native/android/.gradle' \
|
--exclude='./native/android/build' --exclude='./native/android/.gradle' \
|
||||||
--exclude='./backend/log' --exclude='./backend/tmp' --exclude='./infra/.env' --exclude='./.git' .
|
--exclude='./native/android/app/build' --exclude='./native/android/dist' \
|
||||||
|
--exclude='./native/android/keystore' --exclude='./native/android/keystore.properties' \
|
||||||
|
--exclude='./backend/log' --exclude='./backend/tmp' --exclude='./infra/.env' \
|
||||||
|
--exclude='./infra/garage/garage.prod.toml' --exclude='./.git' .
|
||||||
scp /tmp/matchlivetv-deploy.tar.gz "$SERVER:~/matchlivetv-deploy.tar.gz"
|
scp /tmp/matchlivetv-deploy.tar.gz "$SERVER:~/matchlivetv-deploy.tar.gz"
|
||||||
ssh "$SERVER" "mkdir -p $DEST && tar -xzf ~/matchlivetv-deploy.tar.gz -C $DEST"
|
ssh "$SERVER" "mkdir -p $DEST && tar -xzf ~/matchlivetv-deploy.tar.gz -C $DEST"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user