Skip to content

Repository files navigation

Prism

Сетевой медиа-плеер из двух частей:

  • Prism.Host — сервер (ASP.NET Core). Сканирует одну или несколько папок с видеофайлами (например, двухчасовой .mkv) и отдаёт только HTTP API: данные библиотеки и потоковое видео. Если файл уже в браузерном формате — стримит напрямую; иначе, при наличии нужного кодека в системе, обрабатывает через ffmpeg и выдаёт H264 или H265 по HLS. Сам Prism.Host — библиотека; запускаемые обёртки над ней: Prism.Host.Console (обычный запуск, разработка) и Prism.Host.Service (служба Windows, ставится инсталлятором Prism.Host.Setup).
  • Prism.Client — клиент (Node.js + TypeScript + React + Vite). Минималистичный веб-интерфейс: библиотека и плеер. У клиента одна-единственная настройка — URL сервера; всё остальное он получает от сервера запросом (/api/info).
  • Плагины (Prism.Abstractions + Prism.Plugins.*) — опциональные модули, подключаемые списком в appsettings. Ядро корректно работает и без них. Метаданные библиотеки (названия фильмов, привязка эпизодов к сериалу) реализованы плагином Prism.Plugins.Library — см. раздел про плагины.

Как это устроено

Prism.Client (Vite/React)  ──HTTP/CORS──>  Prism.Host (ASP.NET Core / Kestrel)
                                            ├── GET /api/info                     данные о сервере (кодек, ffmpeg, …)
                                            ├── GET /api/media                    список библиотеки
                                            ├── GET /api/media/{id}               одна запись (+ дорожки аудио/субтитров)
                                            ├── PUT/DELETE /api/media/{id}/meta   мета файла (плагин Prism.Plugins.Library)
                                            ├── GET /api/library/tree + CRUD групп   дерево библиотеки (плагин)
                                            ├── GET /api/media/{id}/subtitle/N.vtt субтитры в WebVTT (текстовые дорожки)
                                            ├── GET /raw/{id}                     прямой стрим с Range (браузерные файлы)
                                            ├── GET /hls/{id}/playlist.m3u8       HLS master-плейлист (рендиции дорожек)
                                            ├── GET /hls/{id}/video.m3u8          медиа-плейлист видео (VOD)
                                            ├── GET /hls/{id}/segment/N.ts        видеосегмент H264/H265 на лету
                                            ├── GET /hls/{id}/audio/K.m3u8        медиа-плейлист аудиодорожки K
                                            ├── GET /hls/{id}/audio/K/N.ts        аудиосегмент AAC на лету
                                            ├── GET /hls/{id}/subs/K.m3u8         плейлист субтитров (VOD)
                                            └── GET /hls/{id}/subs/K/N.vtt        WebVTT-сегмент субтитров

Сервер не содержит UI — весь интерфейс в клиенте.

Для каждого файла сервер выбирает режим воспроизведения (Media/MediaLibrary.cs), а клиент получает его в поле streamType:

Режим (streamType) Когда Доставка
direct mp4/webm с браузерным кодеком (h264/vp9/av1 + aac/opus…) /raw с HTTP Range/перемоткой
hls любой другой контейнер/кодек и у ffmpeg есть декодер HLS, перекодирование в H264/H265 + AAC
unsupported нужна перекодировка, но ffmpeg или исходный кодек недоступны клиент показывает пояснение

Стриминг и перемотка для двухчасового файла

playlist.m3u8 — это master-плейлист: в нём объявлены вариант видео и рендиции дорожек (#EXT-X-MEDIA:TYPE=AUDIO на каждую аудиодорожку, TYPE=SUBTITLES на каждую текстовую дорожку субтитров). Видео и каждая аудиодорожка — отдельные сегментные потоки, поэтому плеер (hls.js, ExoPlayer, Safari) переключает дорожки сам, локально и без перезагрузки, а смена аудио не запускает перекодирование видео. Медиа-плейлисты — VOD, рассчитанные заранее по длительности файла (ffprobe), поэтому браузер знает всю шкалу времени и может перематывать в любую точку.

Сегменты производят сессии (Media/HlsTranscoder.cs + Media/TranscodeSession.cs). Сессия — это один процесс ffmpeg, который своим HLS-муксером (-f hls) нарезает один поток (видео или выбранную аудиодорожку) на сегменты и пишет их во временную папку, ограничиваясь по длительности (-t, по умолчанию 15 минут). Поток внутри сессии непрерывный, поэтому на стыках сегментов нет priming-зазоров и перекрытий (которые при схеме «отдельный ffmpeg на каждый сегмент» дают регулярные щелчки). Таймстемпы смещаются на глобальную позицию (-output_ts_offset), так что сегменты всех сессий ложатся на единую шкалу времени. Аудиосессии (без видео) почти бесплатны по CPU; вытесняются видео- и аудиосессии раздельно.

Управление сессиями простое и без платформенных трюков:

  • Границы. Внутри сессии звук бесшовный. Возможный микро-разрыв звука бывает только на границе между сессиями — то есть примерно раз в SessionMinutes минут при непрерывном просмотре. Чем длиннее сессия, тем реже разрывы (но больше расход CPU/диска на сессию).
  • Перемотка. Если для запрошенного сегмента нет подходящей сессии, запускается новая прямо с этой позиции (ключевые кадры форсируются ровно на сетке сегментов, поэтому новая сессия выравнивается по глобальной шкале). Разрыв возникает только там, куда пользователь и так перемотал.
  • Без пауз и сигналов. Сессия ограничена по времени через -t, поэтому ffmpeg не транскодирует весь фильм вперёд — он выдаёт свой диапазон и завершается. Старые сессии вытесняются по LRU (держим до 3 на файл). Это работает одинаково на macOS/Linux/Windows.
  • Временные сегменты лежат в ‹temp›/prism-hls/ и удаляются при вытеснении сессии и при завершении приложения.

Нагрузка на CPU (пейсинг и уборка)

По умолчанию каждый процесс ffmpeg — один экземпляр на сессию. Чтобы он не транскодировал весь 15-минутный диапазон на полной скорости (это грузит CPU и греет ноутбук), включён пейсинг чтения: ffmpeg быстро выдаёт первые BufferBurstSeconds секунд (быстрый старт/перемотка), а дальше читает вход со скоростью MaxPlaybackRate×реального времени (-readrate + -readrate_initial_burst). Множитель берётся с запасом (по умолчанию 2.0), чтобы воспроизведение на 2x не буксовало; общий объём работы кодирования от этого не растёт. Плюс фоновый уборщик убивает сессии, к которым SessionIdleSeconds секунд никто не обращался. На практике это снижает пик CPU при перемотках в разы и опускает нагрузку до нуля, когда никто не смотрит.

Живая дебаг-панель — на клиенте по ссылке debug в шапке (или #/debug): число сессий, суммарный %CPU и память ffmpeg, прогресс и метрики каждой сессии. Данные — из GET /api/debug/sessions.

Аудиодорожки и субтитры

/api/media/{id} отдаёт списки audioTracks и subtitleTracks (язык, название, кодек, каналы), клиент показывает селекторы в плеере.

Дорожки из отдельных файлов. Кроме вшитых в контейнер, подхватываются файлы субтитров и аудио, лежащие рядом с видео. Где именно их искать — задаётся шаблонами в настройках (Player:SubtitleFiles, Player:AudioFiles), по умолчанию ["{name}"]:

"Player": {
  "SubtitleFiles": [ "{name}", "subs/{name}" ],
  "AudioFiles": [ "{name}" ]
}

Шаблон — путь относительно папки видеофайла, где {name} — имя видео без расширения (слеши прямые на всех ОС). Он должен совпасть с началом пути файла, а остаток имени становится подписью дорожки: при шаблоне {name} файл Фильм.Дубляж Мосфильм.ac3 даст дорожку с подписью «Дубляж Мосфильм», а Фильм.srt — дорожку без подписи. Шаблоны применяются по порядку, один файл берётся один раз.

Вид дорожки определяет формат: субтитры — .srt/.ass/.ssa/.vtt, аудио — .mka/.m4a/.aac/.ac3/.eac3/.dts/.flac/.mp3/.opus/.ogg/.wav. Такие дорожки дописываются в конец списка (номера вшитых не меняются) и помечены external: true. Язык у них не выставляется: плееры (ExoPlayer) показывают в меню название языка вместо подписи, и версия озвучки была бы не видна; у вшитых дорожек язык по-прежнему берётся из метаданных контейнера. Список обновляется при каждом скане, поэтому подложенный файл виден без перезапуска сервера. Считается, что дорожка из файла синхронна с видео (сдвиги не поддерживаются). Отдельное аудио возможно только в HLS-режиме, поэтому браузерный файл с такой дорожкой раздаётся через HLS. Кодировка субтитров: файл не в UTF-8 читается как CP1251 (частый случай русских .srt). Графические субтитры отдельными файлами (.sup, .idx/.sub) не поддерживаются.

  • Аудио. Каждая дорожка — отдельная рендиция в master-плейлисте со своим сегментным потоком (лёгкая сессия ffmpeg только-аудио); для дорожки из соседнего файла входом сессии служит этот файл. Плеер переключает дорожки локально, из одного URL, без перезагрузки потока и без повторного перекодирования видео (веб-клиент — через hls.audioTrack).
  • Субтитры. Текстовые дорожки извлекаются в WebVTT (/api/media/{id}/subtitle/N.vtt) и доступны двумя путями: рендиции TYPE=SUBTITLES в master-плейлисте — WebVTT-сегменты той же сетки, что видео, каждый с заголовком X-TIMESTAMP-MAP (без него ExoPlayer не привязывает реплики к шкале времени) — и <track> в веб-клиенте. Включаются/выключаются мгновенно, независимо от аудио. Поддерживаются текстовые субтитры (SRT/ASS/…); графические (PGS/VOBSUB/DVB) помечены textBased:false и в списке недоступны.

Плагины и метаданные

Сервер расширяется плагинами — опциональными модулями. Ядро (стриминг, скан библиотеки) работает и без единого плагина.

  • Контракт — Prism.Abstractions: IPrismModule (регистрирует сервисы и эндпоинты) и IMediaMetaSource (подмешивает произвольные пары ключ-значение к записям /api/media).
  • Список активных плагинов — в appsettings ("Plugins": ["Prism.Plugins.Library"]). Каждый грузится из plugins/<имя>/<имя>.dll (папка рядом с приложением) в собственный AssemblyLoadContext (зависимости плагина резолвятся из его папки; контракт — из хоста). Build плагина копирует его в Prism.Host.Console/plugins/ автоматически; в установку службы плагины кладёт инсталлятор.

Плагин Prism.Plugins.Library (структура библиотеки и мета)

Хранит виртуальное дерево библиотеки и мета-информацию — источник истины на сервере:

  • Группы — виртуальные папки с вложенностью (/api/library/nodes), не привязанные к расположению файлов на диске; файл может входить в несколько групп. Всё дерево с членством отдаётся одним запросом GET /api/library/tree — библиотека домашняя, дерево строит клиент.

  • Мета — свободные пары ключ-значение у файлов и групп. Сервер семантику не знает: в БД может храниться что угодно, потребитель работает с ключами, о которых знает сам (например, клиент показывает title вместо имени файла).

  • Id файла = fingerprint содержимого («размер-хеш краёв», считается ядром при скане с персистентным кэшем в data/fingerprints.json): мета, членство и сами URL переживают переименование и перенос файла; копии одного файла в разных папках — одна запись библиотеки. Записи файлов, которых нет на диске, не удаляются автоматически (диск может быть отключён): в дереве они помечены present:false, чистка — явной командой POST /api/library/gc. Если файл по прежнему пути сменил содержимое (докачался/перезаписан), фоновый ремап (при старте и раз в 5 минут) переносит записи на новый id.

  • СУБД задаётся конфигом (секция Database), код про конкретную СУБД не знает:

    "Database": {
      "Provider": "sqlite",                       // "sqlite" | "postgres"
      "ConnectionString": "Data Source=data/prism.db"
    }

    Для Postgres — своя строка подключения (можно ту же БД, что и у умного дома).

  • Схему ведёт мигратор ThinkingHome.Migrator (ключ версионирования prism.library — своя история миграций в общей БД). Таблицы плоские Prism_Node / Prism_NodeItem / Prism_Meta (префикс Prism_, без схемы) — одинаково на SQLite и Postgres.

  • Заполнение — вне ответственности сервера: он лишь отдаёт/принимает данные через API. Примеры:

    # мета файла: слияние ключей; значение null удаляет ключ
    curl -X PUT http://localhost:8080/api/media/<id>/meta \
      -H 'Content-Type: application/json' \
      -d '{"title":"Патриот · S01E02","genre":"драма"}'
    
    # группы: вложенная структура + положить файл (id группы — GUID, id файла — из /api/media)
    curl -X POST http://localhost:8080/api/library/nodes \
      -H 'Content-Type: application/json' -d '{"name":"Сериалы"}'   # → {"id":"9131…bd"}
    curl -X POST http://localhost:8080/api/library/nodes \
      -H 'Content-Type: application/json' -d '{"parentId":"<id Сериалов>","name":"Патриот"}'
    curl -X PUT http://localhost:8080/api/library/nodes/<nodeId>/items/<mediaId>
    
    # всё дерево (группы + мета групп + членство) одним запросом
    curl http://localhost:8080/api/library/tree
    
    # прогнать обслуживание немедленно (ремап + правила), не дожидаясь таймера;
    # ответ 202, завершения не ждёт — итог каждого прохода пишется в лог хоста
    curl -X POST http://localhost:8080/api/library/scan
    
    # отладка правил: очистить библиотеку целиком (группы, членство, мета) и
    # разложить её заново — удалятся и записи файлов, которых нет на диске
    curl -X POST "http://localhost:8080/api/library/scan?replace=true"
    
    # удалить записи файлов, которых больше нет на диске
    curl -X POST http://localhost:8080/api/library/gc

Правила автозаполнения (опционально, секция Library:Rules в appsettings.json) раскладывают файлы по группам и заполняют мету по шаблону пути:

"Library": {
  "Rules": [
    {
      "Path": "*/{series}.s{season:#}e{episode:#}*",
      "Node": "Сериалы/{series}/Сезон {season}",
      "Meta": { "title": "{series} · S{season}E{episode}", "season": "{season}", "episode": "{episode}" }
    }
  ]
}
  • Path — шаблон относительного пути файла от корня медиапапки (прямые слеши, расширение отбрасывается, регистр не учитывается), совпадать должен весь путь:
    • {имя} — непустой кусок в пределах одного сегмента пути; захваченное значение обрезается от краевых пробелов;
    • {имя:#} — то же, но только цифры. Для номеров сезона и серии берите именно его: s{season}e{episode} цепляется за буквы «s» и «e» в обычных словах и записывает в сериалы, например, Vechnoe.Siyanie.Chistogo…;
    • * — любой кусок сегмента, в том числе пустой, никуда не подставляется: ею «съедают» произвольный мусор реальных имён (…1080p.WEB-DL.RGzs), которого у части файлов может и не быть.
  • Node — путь к группе (недостающие группы создаются), Meta — ключи меты; в оба подставляются значения плейсхолдеров. Хотя бы одно из действий обязательно.
  • Правила применяются фоново (при старте, раз в 5 минут и по требованию — POST /api/library/scan) и только к файлам без единой записи в библиотеке — ни меты, ни членства. При отладке правил помогает POST /api/library/scan?replace=true: он очищает библиотеку целиком (группы, членство и мету — и файлов, и групп) и раскладывает её заново. Замена доступна только с этой ручки — проходы по таймеру заменой не бывают. Удаляются и записи файлов, которых сейчас нет на диске, поэтому мета файла с отключённого диска не вернётся, пока диск не подключат обратно (а ручные правки не вернутся вовсе — их правила не знают). Любая запись у файла — признак, что его уже трогали (правило или руки), и правила его больше не касаются: ручные правки, включая удаление отдельных ключей, не «возвращаются». Удаление всех записей файла = «разложить заново по правилам». Применяются все подходящие правила по порядку; при совпадении мета-ключа выигрывает первое.

Собирается всё решением: dotnet build Prism.sln (build плагина сам разворачивает его в plugins/). Данные и развёрнутые плагины (data/, plugins/) — в .gitignore.

Требования

  • .NET 10 SDK (сервер)
  • Node.js 18+ и npm (клиент)
  • ffmpeg + ffprobe в PATH (или укажите Player:FfmpegPath / Player:FfprobePath, или положите в подпапку ffmpeg/ рядом с приложением). Без них напрямую отдаются только уже браузерные файлы. Нужно для запуска из исходников — служба Windows получает ffmpeg в комплекте инсталлятора.

Установка ffmpeg

Windows

Вариант через пакетный менеджер (рекомендуется):

# winget (Windows 10/11)
winget install --id Gyan.FFmpeg -e

# либо Chocolatey
choco install ffmpeg

# либо Scoop
scoop install ffmpeg

Вручную: скачайте сборку с https://www.gyan.dev/ffmpeg/builds/ (архив ffmpeg-release-full), распакуйте, например, в C:\ffmpeg, и добавьте C:\ffmpeg\bin в переменную среды PATH (Параметры → Система → О системе → Дополнительные параметры системы → Переменные среды). Проверка: ffmpeg -version.

macOS

# Homebrew
brew install ffmpeg

# либо MacPorts
sudo port install ffmpeg

Linux

# Debian / Ubuntu
sudo apt update && sudo apt install -y ffmpeg

# Fedora
sudo dnf install -y ffmpeg            # потребуется репозиторий RPM Fusion

# Arch / Manjaro
sudo pacman -S ffmpeg

# openSUSE
sudo zypper install ffmpeg

# универсально (Snap)
sudo snap install ffmpeg

Проверить установку на любой ОС: ffmpeg -version и ffprobe -version.

Запуск

Нужно запустить оба приложения.

1. Сервер — Prism.Host.Console

# один раз собрать решение — заодно развернутся включённые плагины в plugins/
dotnet build Prism.sln

# положите видео в Prism.Host.Console/videos/  (или укажите папку через --media)
dotnet run --project Prism.Host.Console
# API слушает http://localhost:8080

Параметры командной строки:

dotnet run --project Prism.Host.Console -- --file "/путь/к/movie.mkv"    # отдать папку с этим файлом
dotnet run --project Prism.Host.Console -- --media "/путь/к/библиотеке"  # отдать папку
dotnet run --project Prism.Host.Console -- --media "/кино" --media "/сериалы"  # несколько папок
dotnet run --project Prism.Host.Console -- --codec h265                  # выдавать HEVC вместо H264
dotnet run --project Prism.Host.Console -- --ffmpeg /opt/homebrew/bin/ffmpeg

--media--file) можно указывать несколько раз; каждая папка добавляется к списку MediaDirectories из appsettings.json. Дубли и вложенные папки схлопываются автоматически.

2. Клиент — Prism.Client

cd Prism.Client
npm install          # один раз
npm run dev          # дев-сервер на http://localhost:5173
# для продакшена: npm run build → статика в Prism.Client/dist

Откройте http://localhost:5173. Единственная настройка клиента — URL сервера (шестерёнка в шапке, по умолчанию http://<хост>:8080, сохраняется в localStorage). Остальные параметры клиент берёт с сервера через /api/info.

Служба Windows и инсталлятор

На Windows хост ставится как служба (Prism.Host.Service) — MSI собирает проект Prism.Host.Setup (WiX 6 приезжает из NuGet при сборке, отдельно ставить ничего не нужно). Проект входит в решение, но собирается только в конфигурации Release:

dotnet build Prism.sln -c Release
# → Prism.Host.Setup/bin/Release/en-US/PrismHostSetup.msi

Сборка MSI сама публикует службу одним exe, плагин и лаунчер (ProjectReference Publish="true" в .wixproj — MSI пакует publish-выход, поэтому публикация и есть часть его сборки). Программы framework-dependent: на целевой машине нужны .NET 10 ASP.NET Core Runtime (служба) и Desktop Runtime (лаунчер) — проверки в инсталляторе пока нет. ffmpeg едет в комплекте: при сборке MSI скачивается пинованная essentials-сборка с gyan.dev (кэшируется в obj/, версия задаётся в .wixproj) и ставится в подпапку ffmpeg/ рядом со службой — хост ищет её там в первую очередь, на целевой машине ffmpeg отдельно не нужен. Из-за этого MSI весит ~115 МБ. Сборка ffmpeg — GPL; проект — MIT: текст и ссылки на FFmpeg — в LICENSE и на шаге лицензии в установщике.

Инсталлятор ставит всё в C:\Program Files\Prism Host, регистрирует службу Prism Host (автозапуск, LocalSystem, стартует сразу после установки) и открывает входящие подключения в брандмауэре для локальной сети. Деинсталляция останавливает и удаляет службу.

Лаунчер (значок в трее + пункт «Отправить» проводника) ставится в подпапку launcher/ — у него свой appsettings.json (адрес хоста и брокера MQTT), имя файла совпадает с конфигом хоста, поэтому папка отдельная. Инсталлятор прописывает его автозапуск при входе в систему (HKLM\…\CurrentVersion\Run); пункт «Отправить» лаунчер создаёт себе сам при первом запуске — папка SendTo лежит в профиле пользователя, а установка машинная.

Папки с видео в инсталляторе не спрашиваются — вся настройка живёт в C:\Program Files\Prism Host\appsettings.json: там и список папок (Player:MediaDirectories, их может быть несколько), и правила автозаполнения библиотеки. Диалог выбора одной папки только создавал иллюзию, что конфиг править не придётся. После правки — перезапуск службы:

sc stop PrismHost && sc start PrismHost

Бесшумная установка (без диалогов; из консоли администратора):

msiexec /i PrismHostSetup.msi /qn

:: с журналом установки, если что-то пошло не так
msiexec /i PrismHostSetup.msi /qn /l*v install.log

Обновление поверх установленной версии сначала удаляет старую, поэтому appsettings.json заменяется дефолтным — сохраните свой перед обновлением.

Для сборки MSI ничего ставить не нужно (dotnet build сам тянет WiX из NuGet), но чтобы .wixproj открывался в Visual Studio, нужно бесплатное расширение HeatWave for VS от FireGiant. Без него VS просто покажет проект как невыгруженный — сборке из CLI это не мешает. JetBrains Rider открывает .wixproj без расширений.

MQTT-брокер

Android-плеер (Prism.Player.Android) управляется и публикует свой статус по MQTT — нужен любой брокер в локальной сети, например Eclipse Mosquitto.

Минимальная настройка на Windows

  1. Установите mosquitto (инсталлятор с сайта регистрирует его службой Windows).

  2. По умолчанию mosquitto ≥ 2.0 слушает только localhost — внешние клиенты (плеер) подключиться не смогут. Добавьте в конец C:\Program Files\Mosquitto\mosquitto.conf:

    listener 1883 0.0.0.0
    allow_anonymous true
    

    allow_anonymous true обязателен: как только объявлен listener, анонимный доступ автоматически запрещается. Для домашней сети за роутером этого достаточно; если брокер доступен извне — вместо анонимного доступа заведите пользователя (mosquitto_passwd + password_file в конфиге).

  3. Перезапустите службу и откройте порт в брандмауэре (PowerShell от администратора):

    Restart-Service mosquitto
    New-NetFirewallRule -DisplayName "Mosquitto MQTT" -Direction Inbound -Protocol TCP -LocalPort 1883 -Action Allow

Проверка: Get-NetTCPConnection -State Listen -LocalPort 1883 должен показать адрес 0.0.0.0, а не 127.0.0.1.

Конфигурация (appsettings.json, секция Player)

У каждого запускаемого проекта свой appsettings.json: у Prism.Host.Console — конфигурация разработки, у Prism.Host.Service — то, что уезжает в установку.

Ключ По умолчанию Назначение
MediaDirectories ["videos"] список папок, сканируемых на медиа (относительные — от папки приложения)
SubtitleFiles ["{name}"] шаблоны путей к файлам субтитров рядом с видео (см. выше)
AudioFiles ["{name}"] шаблоны путей к файлам отдельных аудиодорожек
FfmpegPath (авто) явный путь к бинарю ffmpeg
FfprobePath (авто) явный путь к бинарю ffprobe
OutputCodec h264 h264 (libx264) или h265 (libx265/HEVC)
SegmentSeconds 6 длина HLS-сегмента
SessionMinutes 15 длина одной сессии транскодирования (реже разрывы ↔ больше диска)
BufferBurstSeconds 30 начальный «бёрст» перед пейсингом (быстрый старт ↔ нагрузка CPU); 0 — без пейсинга
MaxPlaybackRate 2.0 до какой скорости воспроизведения транскод «догоняет» (пейсинг = ×реального времени)
SessionIdleSeconds 25 через сколько секунд простоя убить сессию (освобождает CPU)
EncoderPreset veryfast пресет скорости x264/x265
Crf 23 качество видео (меньше = лучше/больше)
AudioBitrateKbps 256 битрейт аудио AAC, кбит/с
AudioSampleRate 48000 частота дискретизации аудио, Гц

Звук

Аудио всегда перекодируется в AAC (libfdk_aac, если он есть в сборке ffmpeg, иначе встроенный aac), 48 кГц, по умолчанию 256 кбит/с. Многоканальный звук (5.1/7.1) сводится в стерео качественным даунмиксом (pan-фильтр), где центр/диалоги идут на полном уровне, — это устраняет «тихий и глухой» звук обычного -ac 2.

HTTP-эндпоинт задаётся в секции Kestrel (по умолчанию http://0.0.0.0:8080).

Логи

Хост пишет логи в консоль и в файлы с ротацией: logs/prism-<дата>.log рядом с приложением (у службы Windows — в папке установки, C:\Program Files\Prism Host\logs). Новый файл начинается каждый день или по достижении 32 МБ; хранятся последние 14 файлов, старые удаляются автоматически. Уровни логирования настраиваются секцией Serilog в appsettings.json (MinimumLevel:Default и MinimumLevel:Override:<префикс категории>).

Про H265: HEVC по HLS воспроизводится нативно в Safari, но не поддерживается hls.js в Chrome/Firefox, поэтому по умолчанию выбран h264 для максимальной совместимости с браузерами.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages