New upstream version 25.0.3+dfsg1

This commit is contained in:
Sebastian Ramacher 2020-03-25 09:07:22 +01:00
parent 04fe0efc67
commit 8b2e5f2130
569 changed files with 62491 additions and 5875 deletions

View file

@ -22,7 +22,7 @@ set(obs-ffmpeg_config_HEADERS
set(obs-ffmpeg_HEADERS
obs-ffmpeg-formats.h
obs-ffmpeg-compat.h
closest-pixel-format.h)
ffmpeg-encoded-output.h)
set(obs-ffmpeg_SOURCES
obs-ffmpeg.c
@ -30,6 +30,7 @@ set(obs-ffmpeg_SOURCES
obs-ffmpeg-nvenc.c
obs-ffmpeg-output.c
obs-ffmpeg-mux.c
ffmpeg-encoded-output.c
obs-ffmpeg-source.c)
if(UNIX AND NOT APPLE)

View file

@ -1,94 +0,0 @@
#pragma once
static const enum AVPixelFormat i420_formats[] = {
AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12, AV_PIX_FMT_NV21,
AV_PIX_FMT_YUYV422, AV_PIX_FMT_UYVY422, AV_PIX_FMT_YUV422P,
AV_PIX_FMT_YUV444P, AV_PIX_FMT_NONE};
static const enum AVPixelFormat nv12_formats[] = {
AV_PIX_FMT_NV12, AV_PIX_FMT_NV21, AV_PIX_FMT_YUV420P,
AV_PIX_FMT_YUYV422, AV_PIX_FMT_UYVY422, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_NONE};
static const enum AVPixelFormat i444_formats[] = {
AV_PIX_FMT_YUV444P, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
AV_PIX_FMT_YUYV422, AV_PIX_FMT_UYVY422, AV_PIX_FMT_NV12,
AV_PIX_FMT_NV21, AV_PIX_FMT_NONE};
static const enum AVPixelFormat yuy2_formats[] = {
AV_PIX_FMT_YUYV422, AV_PIX_FMT_UYVY422, AV_PIX_FMT_NV12,
AV_PIX_FMT_NV21, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_NONE};
static const enum AVPixelFormat uyvy_formats[] = {
AV_PIX_FMT_UYVY422, AV_PIX_FMT_YUYV422, AV_PIX_FMT_NV12,
AV_PIX_FMT_NV21, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_NONE};
static const enum AVPixelFormat rgba_formats[] = {
AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_YUYV422, AV_PIX_FMT_UYVY422, AV_PIX_FMT_NV12,
AV_PIX_FMT_NV21, AV_PIX_FMT_NONE};
static const enum AVPixelFormat bgra_formats[] = {
AV_PIX_FMT_BGRA, AV_PIX_FMT_RGBA, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_YUYV422, AV_PIX_FMT_UYVY422, AV_PIX_FMT_NV12,
AV_PIX_FMT_NV21, AV_PIX_FMT_NONE};
static enum AVPixelFormat get_best_format(const enum AVPixelFormat *best,
const enum AVPixelFormat *formats)
{
while (*best != AV_PIX_FMT_NONE) {
enum AVPixelFormat best_format = *best;
const enum AVPixelFormat *cur_formats = formats;
while (*cur_formats != AV_PIX_FMT_NONE) {
enum AVPixelFormat avail_format = *cur_formats;
if (best_format == avail_format)
return best_format;
cur_formats++;
}
best++;
}
return AV_PIX_FMT_NONE;
}
static inline enum AVPixelFormat
get_closest_format(enum AVPixelFormat format, const enum AVPixelFormat *formats)
{
enum AVPixelFormat best_format = AV_PIX_FMT_NONE;
if (!formats || formats[0] == AV_PIX_FMT_NONE)
return format;
switch ((int)format) {
case AV_PIX_FMT_YUV444P:
best_format = get_best_format(i444_formats, formats);
break;
case AV_PIX_FMT_YUV420P:
best_format = get_best_format(i420_formats, formats);
break;
case AV_PIX_FMT_NV12:
best_format = get_best_format(nv12_formats, formats);
break;
case AV_PIX_FMT_YUYV422:
best_format = get_best_format(yuy2_formats, formats);
break;
case AV_PIX_FMT_UYVY422:
best_format = get_best_format(uyvy_formats, formats);
break;
case AV_PIX_FMT_RGBA:
best_format = get_best_format(rgba_formats, formats);
break;
case AV_PIX_FMT_BGRA:
best_format = get_best_format(bgra_formats, formats);
break;
}
return (best_format == AV_PIX_FMT_NONE) ? formats[0] : best_format;
}

View file

@ -19,7 +19,7 @@ NVENC.Preset.llhp="Ниска латентност, висока произво
NVENC.LookAhead="Предопределяне"
NVENC.CQLevel="Ниво CQ"
FFmpegSource="Източник на медия"
FFmpegSource="Източник на медията"
LocalFile="Локален файл"
Looping="Преповтаряне"
Input="Вход"
@ -31,7 +31,18 @@ CloseFileWhenInactive="Затваряне на файла при неактив
ColorRange.Auto="Автоматично"
ColorRange.Partial="Частично"
ColorRange.Full="Пълно"
RestartMedia="Рестартиране"
SpeedPercentage="Скорост"
Play="Пускане"
Pause="Пауза"
Stop="Спиране"
MediaFileFilter.AllMediaFiles="Всички медийни файлове"
MediaFileFilter.VideoFiles="Видеофайлове"
MediaFileFilter.AudioFiles="Звукови файлове"
MediaFileFilter.AllFiles="Всички файлове"
ReplayBuffer="Буфер за повторение"
ReplayBuffer.Save="Записване на повторението"

View file

@ -40,9 +40,12 @@ ColorRange="Gamma de color YUV"
ColorRange.Auto="Automàtic"
ColorRange.Partial="Parcial"
ColorRange.Full="Màxim"
RestartMedia="Reinicia els mitjans"
RestartMedia="Reinicia"
SpeedPercentage="Velocitat"
Seekable="Cercable"
Play="Reprodueix"
Pause="Pausa"
Stop="Atura"
MediaFileFilter.AllMediaFiles="Tots els arxius multimèdia"
MediaFileFilter.VideoFiles="Arxius de vídeo"

View file

@ -40,9 +40,12 @@ ColorRange="Rozsah barev YUV"
ColorRange.Auto="Automatický"
ColorRange.Partial="Částečný"
ColorRange.Full="Celkový"
RestartMedia="Restartovat mediální zdroj"
RestartMedia="Restartovat"
SpeedPercentage="Rychlost"
Seekable="Posouvatelné"
Play="Přehrát"
Pause="Pozastavit"
Stop="Zastavit"
MediaFileFilter.AllMediaFiles="Všechny mediální soubory"
MediaFileFilter.VideoFiles="Video soubory"

View file

@ -40,9 +40,12 @@ ColorRange="YUV-farveområde"
ColorRange.Auto="Auto"
ColorRange.Partial="Delvis"
ColorRange.Full="Fuld"
RestartMedia="Genstart Media"
RestartMedia="Genstart"
SpeedPercentage="Hastighed"
Seekable="Søgbar"
Play="Afspil"
Pause="Pause"
Stop="Stop"
MediaFileFilter.AllMediaFiles="Alle mediefiler"
MediaFileFilter.VideoFiles="Videofiler"

View file

@ -4,7 +4,7 @@ FFmpegOpus="FFmpegOpusKodierer"
Bitrate="Bitrate"
MaxBitrate="Max. Bitrate"
Preset="Voreinstellung"
RateControl="Qualitäts Regulierungsmethode"
RateControl="Qualitätsregulierungsmethode"
KeyframeIntervalSec="Keyframeintervall in Sek. (0=automatisch)"
Lossless="Verlustfrei"
@ -16,12 +16,12 @@ NVENC.Preset.hq="Qualität"
NVENC.Preset.hp="Max. Leistung"
NVENC.Preset.mq="Max. Qualität"
NVENC.Preset.ll="Niedrige Latenz"
NVENC.Preset.llhq="Niedrige Latenz + Qualität"
NVENC.Preset.llhp="Niedrige Latenz + Leistung"
NVENC.LookAhead="Lookahead"
NVENC.LookAhead.ToolTip="Aktiviert dynamische BFrames.\n\nWenn deaktiviert, wird der Kodierer immer die Anzahl der BFrames verwenden, die in der „Max BFrames“Einstellung angegeben sind.\n\nWenn aktiviert, wird er die visuelle Qualität erhöhen, indem nur so viele BFrames verwendet werden wie benötigt, bis zum Maximum,\nzu den Kosten einer erhöhten GPUNutzung."
NVENC.Preset.llhq="Niedrige Latenz und Qualität"
NVENC.Preset.llhp="Niedrige Latenz und Leistung"
NVENC.LookAhead="Lookahead"
NVENC.LookAhead.ToolTip="Aktiviert dynamische BFrames.\n\nWenn deaktiviert, wird der Kodierer immer die Anzahl der BFrames verwenden, die in der Einstellung „Max. BFrames“ angegeben sind.\n\nWenn aktiviert, wird die visuelle Qualität erhöht, indem nur so viele BFrames bis zum Maximum verwendet werden, wie benötigt.\n(Resultiert in eine höhere GPUNutzung.)"
NVENC.PsychoVisualTuning="Psycho Visual Tuning"
NVENC.PsychoVisualTuning.ToolTip="Aktiviert Kodierereinstellungen, die die Verwendung der Bitrate für eine erhöhte wahrgenommene visuelle Qualität optimieren,\ninsbesondere in Situationen mit hoher Bewegung, zu Kosten einer erhöhten GPUNutzung."
NVENC.PsychoVisualTuning.ToolTip="Aktiviert Kodierereinstellungen, die die Verwendung der Bitrate für eine erhöhte wahrgenommene visuelle Qualität optimieren,\ninsbesondere in Situationen mit hoher Bewegung.\n(Resultiert in eine höhere GPUNutzung.)"
NVENC.CQLevel="CQLevel"
FFmpegSource="Medienquelle"
@ -33,26 +33,29 @@ BufferingMB="Netzwerkpufferung"
HardwareDecode="Hardwaredekodierung verwenden, falls verfügbar"
ClearOnMediaEnd="Nichts anzeigen, wenn Wiedergabe endet"
Advanced="Erweitert"
RestartWhenActivated="Wiedergabe erneut starten, wenn Quelle aktiviert wird"
RestartWhenActivated="Wiedergabe bei Quellenaktivierung erneut starten"
CloseFileWhenInactive="Datei schließen, wenn inaktiv"
CloseFileWhenInactive.ToolTip="Schließt die Datei, wenn die Quelle im Stream oder der Aufnahme nicht angezeigt wird.\nDies ermöglicht, dass die Datei geändert werden kann.\nDafür können aber Startverzögerungen auftreten, wenn die Quelle reaktiviert wird."
ColorRange="YUVFarbmatrix"
CloseFileWhenInactive.ToolTip="Schließt die Datei, wenn die Quelle im Stream oder in der Aufnahme nicht angezeigt wird.\nDies ermöglicht Änderungen an der Datei vorzunehmen,\nwas aber zu Reaktivierungsverzögerungen führen kann."
ColorRange="YUVFarbbereich"
ColorRange.Auto="Automatisch"
ColorRange.Partial="Teilweise"
ColorRange.Full="Voll"
RestartMedia="Medium neustarten"
RestartMedia="Neustarten"
SpeedPercentage="Geschwindigkeit"
Seekable="Durch­such­bar"
Play="Abspielen"
Pause="Pausieren"
Stop="Stoppen"
MediaFileFilter.AllMediaFiles="Alle Mediendateien"
MediaFileFilter.VideoFiles="Videodateien"
MediaFileFilter.AudioFiles="Audiodateien"
MediaFileFilter.AllFiles="Alle Dateien"
ReplayBuffer="ReplayPuffer"
ReplayBuffer.Save="Replay speichern"
ReplayBuffer="Wiederholungspuffer"
ReplayBuffer.Save="Wiederholung speichern"
HelperProcessFailed="Der AufnahmeHelferProzess kann nicht gestartet werden. Überprüfen Sie, ob OBSDateien nicht von einer DrittanbieterAntiviren/Sicherheitssoftware blockiert oder entfernt werden."
UnableToWritePath="Kann nicht zu %1 schreiben. Vergewissern Sie sich, dass Sie einen Aufnahmepfad verwenden, für das Ihr Benutzerkonto Schreibrechte hat und dass genügend Speicherplatz zur Verfügung steht."
WarnWindowsDefender="Wenn Windows10RansomwareSchutz aktiviert ist, kann dies auch den Fehler auslösen. Versuchen Sie, den überwachten Ordnerzugriff in WindowsSicherheit → Viren & Bedrohungsschutz auszuschalten."
UnableToWritePath="Kann nicht zu %1 schreiben. Vergewissern Sie sich, dass Sie einen Aufnahmepfad verwenden, für den Ihr Benutzerkonto Schreibrechte hat und dass genügend Speicherplatz zur Verfügung steht."
WarnWindowsDefender="Wenn Windows10RansomwareSchutz aktiviert ist, kann dies auch den Fehler auslösen. Versuchen Sie, den überwachten Ordnerzugriff in „WindowsSicherheit“„Viren & Bedrohungsschutz“ auszuschalten."

View file

@ -25,7 +25,6 @@ ColorRange="Χώρος Χρωμάτων YUV"
ColorRange.Auto="Αυτόματο"
ColorRange.Partial="Μερικός"
ColorRange.Full="Πλήρης"
RestartMedia="Επανεκκίνηση Πολυμέσων"
Seekable="Παρεχόμενη"
MediaFileFilter.AllMediaFiles="Όλα τα αρχεία πολυμέσων"

View file

@ -40,9 +40,12 @@ ColorRange="YUV Color Range"
ColorRange.Auto="Auto"
ColorRange.Partial="Partial"
ColorRange.Full="Full"
RestartMedia="Restart Media"
RestartMedia="Restart"
SpeedPercentage="Speed"
Seekable="Seekable"
Play="Play"
Pause="Pause"
Stop="Stop"
MediaFileFilter.AllMediaFiles="All Media Files"
MediaFileFilter.VideoFiles="Video Files"

View file

@ -40,9 +40,12 @@ ColorRange="Gama de Color YUV"
ColorRange.Auto="Automatico"
ColorRange.Partial="Parcial"
ColorRange.Full="Completo"
RestartMedia="Reiniciar Medio"
RestartMedia="Reiniciar"
SpeedPercentage="Velocidad"
Seekable="Buscable"
Play="Reproducir"
Pause="Pausa"
Stop="Detener"
MediaFileFilter.AllMediaFiles="Todos los archivos multimedia"
MediaFileFilter.VideoFiles="Archivos de vídeo"

View file

@ -1,12 +1,17 @@
FFmpegOutput="FFmpeg väljund"
FFmpegAAC="FFmpeg vaike AAC kodeerija"
Bitrate="Bitikiirus"
MaxBitrate="Maksimaalne bitikiirus"
Preset="Eelseadistus"
KeyframeIntervalSec="Võtmekaadri intervall (sekundit, 0=automaatne)"
Lossless="Kadudeta"
NVENC.Use2Pass="Kasuta Two-Pass kodeeringut"
NVENC.Preset.default="Jõudlus"
NVENC.Preset.hq="Kvaliteet"
NVENC.Preset.hp="Maksimaalne jõudlus"
NVENC.Preset.mq="Makimaalne kvaliteet"
NVENC.Preset.ll="Madal-viivitus"
FFmpegSource="Meedia allikas"

View file

@ -40,9 +40,12 @@ ColorRange="YUV kolore-barrutia"
ColorRange.Auto="Auto"
ColorRange.Partial="Partziala"
ColorRange.Full="Osoa"
RestartMedia="Berrabiarazi euskarria"
RestartMedia="Berrabiarazi"
SpeedPercentage="Abiadura"
Seekable="Bilagai"
Play="Erreproduzitu"
Pause="Pausatu"
Stop="Gelditu"
MediaFileFilter.AllMediaFiles="Multimedia-fitxategi guztiak"
MediaFileFilter.VideoFiles="Bideo-fitxategiak"

View file

@ -27,7 +27,6 @@ InputFormat="فرمت های ورودی"
ColorRange.Auto="خودکار"
ColorRange.Partial="جزئی"
ColorRange.Full="کامل"
RestartMedia="راه اندازی مجدد رسانه ها"
Seekable="جستجوگر"
MediaFileFilter.AllMediaFiles="تمامی فایل های رسانه"

View file

@ -29,6 +29,7 @@ LocalFile="Paikallinen tiedosto"
Looping="Toista jatkuvasti"
Input="Sisääntulo"
InputFormat="Sisääntulon muoto"
BufferingMB="Verkon puskurointi"
HardwareDecode="Käytä laitteistotason purkua, kun mahdollista"
ClearOnMediaEnd="Älä näytä mitään kun toisto päättyy"
Advanced="Lisäasetukset"
@ -39,8 +40,12 @@ ColorRange="YUV värialue"
ColorRange.Auto="Automaattinen"
ColorRange.Partial="Osittainen"
ColorRange.Full="Täysi"
RestartMedia="Uudelleenkäynnistä media"
RestartMedia="Toista uudelleen"
SpeedPercentage="Nopeus"
Seekable="Haettava"
Play="Toista"
Pause="Tauko"
Stop="Pysäytä"
MediaFileFilter.AllMediaFiles="Kaikki mediatiedostot"
MediaFileFilter.VideoFiles="Videotiedostot"

View file

@ -25,7 +25,6 @@ ColorRange="Antas ng kulay ng YUV"
ColorRange.Auto="I-Auto"
ColorRange.Partial="I-Partial"
ColorRange.Full="Buo"
RestartMedia="Simulan muli ang Media"
Seekable="I-Seekable"
MediaFileFilter.AllMediaFiles="Lahat ng Media Files"

View file

@ -26,15 +26,15 @@ NVENC.CQLevel="Niveau CQ"
FFmpegSource="Source média"
LocalFile="Fichier local"
Looping="En boucle"
Looping="Lire en boucle"
Input="Entrée"
InputFormat="Format d'entrée"
BufferingMB="Tampon réseau (Mo)"
HardwareDecode="Utiliser le décodage matériel si possible"
HardwareDecode="Utiliser le décodage matériel (si disponible)"
ClearOnMediaEnd="Ne rien afficher lorsque la lecture se termine"
Advanced="Options avancées"
RestartWhenActivated="Reprendre la lecture quand la source est active"
CloseFileWhenInactive="Fermer fichier lorsquil est inactif"
RestartWhenActivated="Reprendre depuis le début quand cette source redevient active"
CloseFileWhenInactive="Fermer le fichier quand cette source est inactive"
CloseFileWhenInactive.ToolTip="Ferme le fichier lorsque la source ne s'affiche pas sur le stream ou \nl'enregistrement. Cela permet de modifier le fichier lorsque la source n'est pas active, \nmais il peut y avoir un délai de démarrage lorsque la source se réactive."
ColorRange="Gamme de couleurs YUV"
ColorRange.Auto="Auto"
@ -43,6 +43,9 @@ ColorRange.Full="Complète"
RestartMedia="Reprendre depuis le début"
SpeedPercentage="Vitesse"
Seekable="Navigable"
Play="Lecture"
Pause="Pause"
Stop="Arrêter"
MediaFileFilter.AllMediaFiles="Tous les fichiers multimédias"
MediaFileFilter.VideoFiles="Fichiers vidéo"

View file

@ -33,7 +33,6 @@ ColorRange="Rainse dhathan YUV"
ColorRange.Auto="Fèin-obrachail"
ColorRange.Partial="Leth-phàirteach"
ColorRange.Full="Làn"
RestartMedia="Ath-thòisich am meadhan"
Seekable="Gabhaidh sireadh ann"
MediaFileFilter.AllMediaFiles="A h-uile faidhle meadhain"

View file

@ -30,7 +30,7 @@ Looping="Bucle"
Input="Entrada"
InputFormat="Formato de entrada"
BufferingMB="Memoria intermedia de rede"
HardwareDecode="Utilizar a descodificación por hárdware cando estiver dispoñible"
HardwareDecode="Utilizar a descodificación por hárdware cando estiver dispoñíbel"
ClearOnMediaEnd="Non amosar nada ao rematar a reprodución"
Advanced="Avanzado"
RestartWhenActivated="Reiniciar a reprodución cando fonte estea activa"
@ -40,9 +40,12 @@ ColorRange="Gama de cor YUV"
ColorRange.Auto="Automático"
ColorRange.Partial="Parcial"
ColorRange.Full="Total"
RestartMedia="Reiniciar multimedia"
RestartMedia="Reiniciar"
SpeedPercentage="Velocidade"
Seekable="Buscábel"
Play="Reproducir"
Pause="Pausa"
Stop="Parar"
MediaFileFilter.AllMediaFiles="Todos os ficheiros multimedia"
MediaFileFilter.VideoFiles="Ficheiros de vídeo"

View file

@ -40,9 +40,12 @@ ColorRange="YUV színtartomány"
ColorRange.Auto="Auto"
ColorRange.Partial="Részleges"
ColorRange.Full="Teljes"
RestartMedia="Media újraindítása"
RestartMedia="Újraindítás"
SpeedPercentage="Sebesség"
Seekable="Kereshető"
Play="Lejátszás"
Pause="Szünet"
Stop="Leállítás"
MediaFileFilter.AllMediaFiles="Minden médiafájl"
MediaFileFilter.VideoFiles="Videofájlok"

View file

@ -40,9 +40,12 @@ ColorRange="Gamma di colore YUV"
ColorRange.Auto="Automatico"
ColorRange.Partial="Parziale"
ColorRange.Full="Intero"
RestartMedia="Riavvia media dall'inizio"
RestartMedia="Riavvia"
SpeedPercentage="Velocità"
Seekable="Ricercabile"
Play="Avvia"
Pause="Pausa"
Stop="Stop"
MediaFileFilter.AllMediaFiles="Tutti i file multimediali"
MediaFileFilter.VideoFiles="File video"

View file

@ -40,9 +40,12 @@ ColorRange="YUV 色範囲"
ColorRange.Auto="自動"
ColorRange.Partial="一部"
ColorRange.Full="全部"
RestartMedia="メディアを再開する"
RestartMedia="再開"
SpeedPercentage="速度"
Seekable="シーク可能"
Play="再生"
Pause="一時停止"
Stop="停止"
MediaFileFilter.AllMediaFiles="すべてのメディアファイル"
MediaFileFilter.VideoFiles="ビデオファイル"

View file

@ -5,7 +5,7 @@ Bitrate="ბიტური სიხშირე"
MaxBitrate="უმაღლესი დასაშვები ბიტური სიხშირე"
Preset="მზა პარამეტრები"
RateControl="სიხშირის მართვა"
KeyframeIntervalSec="საკვანძო კადრებს შორის შუალედი (წამი, 0=თვითშერჩევა)"
KeyframeIntervalSec="საკვანძო კადრების შუალედი (წამი, 0=თვითშერჩევა)"
Lossless="უდანაკარგო"
BFrames="B-კადრების დასაშვები რაოდენობა"
@ -40,9 +40,12 @@ ColorRange="YUV ფერთა გამა"
ColorRange.Auto="ავტომატური"
ColorRange.Partial="ნაწილობრივი"
ColorRange.Full="სრული"
RestartMedia="მასალის ხელახლა გაშვება"
RestartMedia="თავიდან ჩართვა"
SpeedPercentage="სიჩქარე"
Seekable="გადახვევით"
Play="გაშვება"
Pause="შეჩერება"
Stop="შეწყვეტა"
MediaFileFilter.AllMediaFiles="ყველა მასალა"
MediaFileFilter.VideoFiles="ვიდეოფაილები"

View file

@ -40,9 +40,12 @@ ColorRange="YUV 색상 범위"
ColorRange.Auto="자동"
ColorRange.Partial="부분"
ColorRange.Full="전체"
RestartMedia="미디어 다시재생"
RestartMedia="재시작"
SpeedPercentage="속도"
Seekable="탐색 가능"
Play="재생"
Pause="일시정지"
Stop="중단"
MediaFileFilter.AllMediaFiles="모든 미디어 파일"
MediaFileFilter.VideoFiles="비디오 파일"

View file

@ -29,6 +29,7 @@ LocalFile="Lokal fil"
Looping="Repeter"
Input="Inngang"
InputFormat="Inngangsformat"
BufferingMB="Nettverksbuffer"
HardwareDecode="Bruk maskinvaredekoding når tilgjengelig"
ClearOnMediaEnd="Vis ingenting når avspillingen slutter"
Advanced="Avansert"
@ -39,7 +40,7 @@ ColorRange="YUV fargerom"
ColorRange.Auto="Automatisk"
ColorRange.Partial="Delvis"
ColorRange.Full="Hel"
RestartMedia="Start media på nytt"
SpeedPercentage="Hastighet"
Seekable="Søkbar"
MediaFileFilter.AllMediaFiles="Alle mediefiler"

View file

@ -40,9 +40,12 @@ ColorRange="YUV Kleurbereik"
ColorRange.Auto="Automatisch"
ColorRange.Partial="Gedeeltelijk"
ColorRange.Full="Volledig"
RestartMedia="Media herstarten"
RestartMedia="Herstart"
SpeedPercentage="Snelheid"
Seekable="Zoekbaar"
Play="Speel"
Pause="Pauze"
Stop="Stop"
MediaFileFilter.AllMediaFiles="Alle mediabestanden"
MediaFileFilter.VideoFiles="Videobestanden"

View file

@ -40,9 +40,12 @@ ColorRange="Zakres kolorów YUV"
ColorRange.Auto="Automatycznie"
ColorRange.Partial="Częściowy"
ColorRange.Full="Pełny"
RestartMedia="Zrestartuj plik audio-wideo"
RestartMedia="Zrestartuj"
SpeedPercentage="Szybkość"
Seekable="Przeszukiwalny"
Play="Odtwórz"
Pause="Pauza"
Stop="Zatrzymaj"
MediaFileFilter.AllMediaFiles="Wszystkie pliki multimedialne"
MediaFileFilter.VideoFiles="Pliki video"

View file

@ -40,9 +40,12 @@ ColorRange="Intervalo de Cores YUV"
ColorRange.Auto="Auto"
ColorRange.Partial="Parcial"
ColorRange.Full="Completo"
RestartMedia="Reiniciar Mídia"
RestartMedia="Reiniciar"
SpeedPercentage="Velocidade"
Seekable="Procurável"
Play="Iniciar"
Pause="Pausar"
Stop="Parar"
MediaFileFilter.AllMediaFiles="Todos Arquivos de Mídia"
MediaFileFilter.VideoFiles="Arquivos de Vídeo"

View file

@ -16,13 +16,19 @@ NVENC.Preset.hq="Qualidade"
NVENC.Preset.hp="Máximo Desempenho"
NVENC.Preset.mq="Qualidade Máxima"
NVENC.Preset.ll="Baixa latência"
NVENC.Preset.llhq="Qualidade com baixa latência"
NVENC.Preset.llhp="Perfomance com baixa latência"
NVENC.LookAhead.ToolTip="Ativa as B-frames dinâmicas.\n\nSe desativado, o codificador usará sempre o número de B-Frames especificado na definição 'Max B-frames'.\n\nSe ativado, aumentará a qualidade visual usando somente quantos quadros B forem necessários, até ao máximo,\ncom o custo na utilização da GPU."
NVENC.CQLevel="Nível CQ"
FFmpegSource="Fonte de multimédia"
LocalFile="Ficheiro local"
Looping="Repetir"
Input="Entrada"
InputFormat="Formato de entrada"
BufferingMB="Buffering de Rede"
HardwareDecode="Utilizar descodificação de hardware quando disponível"
ClearOnMediaEnd="Não mostrar nada quando a reprodução terminar"
Advanced="Avançado"
RestartWhenActivated="Reiniciar reprodução quando a fonte se torna ativo"
CloseFileWhenInactive="Fechar ficheiro quando inativo"
@ -30,6 +36,8 @@ ColorRange="Gama de cor YUV"
ColorRange.Auto="Auto"
ColorRange.Partial="Parcial"
ColorRange.Full="Completo"
SpeedPercentage="Velocidade"
Seekable="Pesquisável"
MediaFileFilter.AllMediaFiles="Todos os Arquivos de Media"
MediaFileFilter.VideoFiles="Arquivos de Vídeo"
@ -37,4 +45,7 @@ MediaFileFilter.AudioFiles="Arquivos de Áudio"
MediaFileFilter.AllFiles="Todos os ficheiros"
HelperProcessFailed="Não foi possível iniciar o processo auxiliar de gravação. Verifique se os ficheiros do OBS não bloqueados ou removidos por qualquer software de segurança ou antivírus de terceiros."
UnableToWritePath="Não foi possível gravar em %1. Certifique-se de que está a usar um caminho de gravação no qual sua conta de utilizador tem permissão para escrever e que há espaço suficiente em disco."
WarnWindowsDefender="Se a proteção contra Ransomware estiver habilitada no Windows 10, pode ser essa a causa deste erro. Tente desativar o acesso controlado a pastas nas configurações de segurança do Windows ou nas definições de proteção contra ameaças e vírus."

View file

@ -40,9 +40,12 @@ ColorRange="Цветовой диапазон YUV"
ColorRange.Auto="Автоматически"
ColorRange.Partial="Частичный"
ColorRange.Full="Полный"
RestartMedia="Перезапустить медиа"
RestartMedia="Перезапустить"
SpeedPercentage="Скорость"
Seekable="Перематываемый"
Play="Запустить"
Pause="Приостановить"
Stop="Остановить"
MediaFileFilter.AllMediaFiles="Все медиа-файлы"
MediaFileFilter.VideoFiles="Видеофайлы"

View file

@ -2,21 +2,36 @@ FFmpegOutput="Výstup FFmpeg"
FFmpegAAC="Predvolený FFmpeg AAC enkodér"
FFmpegOpus="FFmpeg Opus enkodér"
Bitrate="Bitrate"
MaxBitrate="Max. bitrate"
Preset="Predvoľba"
RateControl="Riadenie toku"
KeyframeIntervalSec="Interval kľúčových snímok (sekúnd, 0 = automaticky)"
Lossless="Bezstratová"
BFrames="Maximum B-snímkov"
NVENC.Use2Pass="Použiť dvojfázové enkódovanie"
NVENC.Preset.default="Výkon"
NVENC.Preset.hq="Kvalita"
NVENC.Preset.hp="Maximálny Výkon"
NVENC.Preset.mq="Maximálna Kvalita"
NVENC.Preset.ll="Nízka odozva"
NVENC.Preset.llhq="Kvalita pri nízkej odozve"
NVENC.Preset.llhp="Výkon pri nízkej odozve"
NVENC.LookAhead="Look-ahead"
NVENC.LookAhead.ToolTip="Aktivuje dynamické B-snímky.\n\nKeď sú vypnuté, enkodér bude vždy používať počet snímkov špecifikovaný v nastavení 'Maximum B-snímkov'.\n\nKeď sú zapnuté, zvýši sa vizuálna kvalita tým, že bude používať iba toľko B-snímkov, koľko je potrebné, až do maxima,\nna úkor zvýšeného používania GPU."
NVENC.PsychoVisualTuning="Psycho Visual Tuning"
NVENC.PsychoVisualTuning.ToolTip="Povolí nastavenia enkodéra, ktoré optimalizujú využitie bitrate pre vylepšenú vnímanú vizuálnu kvalitu,\nhlavne v situáciách s veľkým množstvom pohybu, na úkor zvýšenia používania GPU."
NVENC.CQLevel="CQ Level"
FFmpegSource="Zdroj médií"
LocalFile="Lokálny súbor"
Looping="Slučka"
Input="Vstup"
InputFormat="Vstupný formát"
BufferingMB="Vyrovnávacia pamäť siete"
HardwareDecode="Použiť hardvérové dekódovanie podľa dostupnosti"
ClearOnMediaEnd="Po skončení prehrávania neukazovať nič"
Advanced="Rozšírené"
RestartWhenActivated="Obnoviť prehrávanie pri aktivovaní zdroja"
CloseFileWhenInactive="Zatvoriť súbor pri neaktivite"
@ -25,8 +40,12 @@ ColorRange="Rozsah farieb YUV"
ColorRange.Auto="Automaticky"
ColorRange.Partial="Čiastočný"
ColorRange.Full="Plný"
RestartMedia="Reštartuj mediálny zdroj"
RestartMedia="Reštartovať"
SpeedPercentage="Rýchlosť"
Seekable="Posúvateľný"
Play="Prehrať"
Pause="Pause"
Stop="Zastaviť"
MediaFileFilter.AllMediaFiles="Všetky mediálne súbory"
MediaFileFilter.VideoFiles="Video súbory"
@ -38,4 +57,5 @@ ReplayBuffer.Save="Uložiť záznam"
HelperProcessFailed="Nepodarilo sa spustiť pomocný proces nahrávania. Skontrolujte, či neboli súbory OBS zablokované alebo odstránené zabezpečovacím softvérom/antivírusom tretej strany."
UnableToWritePath="Nedá sa zapisovať do %1. Uistite sa, že v ceste nahrávania má váš používateľský účet právo na zápis a že máte dostatok miesta na disku."
WarnWindowsDefender="Ak je ochrana proti Ransomwaru Windows 10 zapnutá, tak to môže taktiež spôsobiť túto chybu. Skúste vypnúť kontrolovaný prístup do priečinkov vo Windows Zabezpečení / Ochrana pred vírusmi a hrozbami."

View file

@ -5,7 +5,7 @@ Bitrate="Bitna hitrost"
MaxBitrate="Največja bitna hitrost"
Preset="Prednastavitev"
RateControl="Nadzor hitrosti"
KeyframeIntervalSec="Razmik med ključnimi sličicami (s, 0=samodejno)"
KeyframeIntervalSec="Razmik med ključnimi sličicami (ssekunde, 0=samodejno)"
Lossless="Brezizgubno"
BFrames="Največje št. sličic B"
@ -28,7 +28,7 @@ FFmpegSource="Predstavnostni vir"
LocalFile="Lokalna datoteka"
Looping="Ponavljaj"
Input="Vhod"
InputFormat="Oblika vhoda"
InputFormat="Vhodni format"
BufferingMB="Omrežno medpomnenje"
HardwareDecode="Uporabi strojno odkodiranje, ko je na voljo"
ClearOnMediaEnd="Ne prikaži ničesar, ko se predvajanje konča"
@ -40,17 +40,20 @@ ColorRange="Barvni razpon YUV"
ColorRange.Auto="Samodejno"
ColorRange.Partial="Delno"
ColorRange.Full="Polno"
RestartMedia="Pon. zaženi predstavnost"
RestartMedia="Ponovno zaženi"
SpeedPercentage="Hitrost"
Seekable="Omogoči iskanje"
Play="Predvajaj"
Pause="Premor"
Stop="Ustavi"
MediaFileFilter.AllMediaFiles="Vse predstavne datoteke"
MediaFileFilter.VideoFiles="Videodatoteke"
MediaFileFilter.AudioFiles="Zvočne datoteke"
MediaFileFilter.AllFiles="Vse datoteke"
ReplayBuffer="Medpomn. za pon. predv."
ReplayBuffer.Save="Shrani pon. predv."
ReplayBuffer="Medpomn. za ponovitev"
ReplayBuffer.Save="Shrani ponovitev"
HelperProcessFailed="Pomožnega opravila za snemanje ni bilo mogoče začeti. Preverite, da protivirusni/varnostni program ni blokiral ali odstranil datotek OBS."
UnableToWritePath="Ni mogoče pisati v %1. Prepričajte se, da uporabljate pot za snemanje, na katero lahko vaš uporabniški račun zapisuje in da imate dovolj prostora na disku."

View file

@ -38,7 +38,6 @@ ColorRange="YUV opseg boja"
ColorRange.Auto="Automatski"
ColorRange.Partial="Delimični"
ColorRange.Full="Potpuni"
RestartMedia="Restartuj medij"
Seekable="Pretraživanje"
MediaFileFilter.AllMediaFiles="Sve medija datoteke"

View file

@ -38,7 +38,6 @@ ColorRange="YUV опсег боја"
ColorRange.Auto="Аутоматски"
ColorRange.Partial="Делимични"
ColorRange.Full="Потпуни"
RestartMedia="Рестартуј медиј"
Seekable="Претраживање"
MediaFileFilter.AllMediaFiles="Све медија датотеке"

View file

@ -40,9 +40,12 @@ ColorRange="YUV-färgområde"
ColorRange.Auto="Automatisk"
ColorRange.Partial="Delvis"
ColorRange.Full="Full"
RestartMedia="Starta om media"
RestartMedia="Starta om"
SpeedPercentage="Hastighet"
Seekable="Sökbar"
Play="Spela upp"
Pause="Pausa"
Stop="Stoppa"
MediaFileFilter.AllMediaFiles="Alla mediafiler"
MediaFileFilter.VideoFiles="Videofiler"

View file

@ -25,7 +25,6 @@ ColorRange="Ang Saklaw ng Kulay na YUV"
ColorRange.Auto="Awto"
ColorRange.Partial="Bahagya"
ColorRange.Full="Puno"
RestartMedia="I-restart ang Media"
Seekable="Maayos"
MediaFileFilter.AllMediaFiles="Lahat ng mga Media File"

View file

@ -18,14 +18,20 @@ NVENC.Preset.mq="Maksimum Kalite"
NVENC.Preset.ll="Düşük Gecikme"
NVENC.Preset.llhq="Düşük Gecikme, Düşük Kalite"
NVENC.Preset.llhp="Düşük Gecikme, Düşük Performans"
NVENC.LookAhead="İleri bakma"
NVENC.LookAhead.ToolTip="Dinamik B karelerini etkinleştirir.\n\n Devre dışı bırakılırsa, kodlayıcı her zaman 'Maks B-kareleri' ayarında belirtilen B-karelerinin sayısını kullanır.\n\n Eğer için, sadece bununla birçok B-kare gereklidir kullanarak görsel kaliteyi yukarı artacak, etkin maksimum, \nartan GPU kullanımı pahasına."
NVENC.PsychoVisualTuning="Psiko Görsel Ayarlama"
NVENC.PsychoVisualTuning.ToolTip="Bit hızı kullanımını, arttırılmış algılanan görsel kalite için optimize eden ayarları etkinleştirir,\nÖzellikle yüksek haraketli durumlarda, arttırılmış GPU kullanımı pahasına."
NVENC.CQLevel="CQ Seviyesi"
FFmpegSource="Ortam Kaynağı"
LocalFile="Yerel Dosya"
Looping="Döngü"
Input="Giriş"
InputFormat="Giriş Biçimi"
BufferingMB="Ağ Arabelleğe Alma"
HardwareDecode="Kullanılabilir ise, donanım kod çözmeyi kullan"
ClearOnMediaEnd="Oynatma bittiğinde hiçbir şey gösterme"
Advanced="Gelişmiş"
RestartWhenActivated="Yeniden oynatmayı kaynak etkin olduğunda yeniden başlat"
CloseFileWhenInactive="Etkin değilken dosyayı kapat"
@ -34,9 +40,12 @@ ColorRange="YUV Renk Aralığı"
ColorRange.Auto="Otomatik"
ColorRange.Partial="Kısmi"
ColorRange.Full="Tam"
RestartMedia="Ortamı Yeniden Başlat"
RestartMedia="Yeniden başlat"
SpeedPercentage="Hız"
Seekable="Aranabilir"
Play="Oynat"
Pause="Duraklat"
Stop="Durdur"
MediaFileFilter.AllMediaFiles="Tüm Medya Dosyaları"
MediaFileFilter.VideoFiles="Video Dosyaları"

View file

@ -40,9 +40,12 @@ ColorRange="YUV, колірний діапазон"
ColorRange.Auto="Автовизначення"
ColorRange.Partial="Частковий"
ColorRange.Full="Повний"
RestartMedia="Перезапустити медіа"
RestartMedia="Перезавантажити"
SpeedPercentage="Швидкість"
Seekable="HTTP з перемотуванням"
Play="Відтворити"
Pause="Призупинити"
Stop="Зупинити"
MediaFileFilter.AllMediaFiles="Файли мультимедіа"
MediaFileFilter.VideoFiles="Відео"

View file

@ -29,7 +29,6 @@ ColorRange="Phạm vi màu YUV"
ColorRange.Auto="Tự động"
ColorRange.Partial="Một phần"
ColorRange.Full="Đầy đủ"
RestartMedia="Khởi động lại media"
MediaFileFilter.AllMediaFiles="Tất cả tập tin Media"
MediaFileFilter.VideoFiles="Tập tin video"

View file

@ -40,9 +40,12 @@ ColorRange="YUV 颜色范围"
ColorRange.Auto="自动"
ColorRange.Partial="局部"
ColorRange.Full="全部"
RestartMedia="重新启动媒体"
RestartMedia="重新开始"
SpeedPercentage="速度"
Seekable="可搜索"
Play="播放"
Pause="暂停"
Stop="停止"
MediaFileFilter.AllMediaFiles="所有媒体文件"
MediaFileFilter.VideoFiles="视频文件"

View file

@ -1,8 +1,8 @@
FFmpegOutput="FFmpeg 輸出"
FFmpegAAC="FFmpeg 預設 AAC 編碼器"
FFmpegOpus="FFmpeg Opus 編碼器"
Bitrate="位元率"
MaxBitrate="最大位元率"
Bitrate="位元率"
MaxBitrate="最大位元率"
Preset="預置"
RateControl="位元率控制"
KeyframeIntervalSec="關鍵訊框間隔 (秒0 = 自動)"
@ -40,19 +40,22 @@ ColorRange="YUV 色彩空間"
ColorRange.Auto="自動"
ColorRange.Partial="部分"
ColorRange.Full="全部"
RestartMedia="重新播放媒體"
RestartMedia="重新開始"
SpeedPercentage="速度"
Seekable="可查找"
Seekable="可搜尋"
Play="播放"
Pause="暫停"
Stop="停止"
MediaFileFilter.AllMediaFiles="所有媒體檔案"
MediaFileFilter.VideoFiles="影像檔"
MediaFileFilter.AudioFiles="音效檔"
MediaFileFilter.VideoFiles="視訊檔案"
MediaFileFilter.AudioFiles="音訊檔案"
MediaFileFilter.AllFiles="所有檔案"
ReplayBuffer="重播緩衝"
ReplayBuffer="重播緩衝"
ReplayBuffer.Save="儲存重播"
HelperProcessFailed="無法啟動錄影協助程式。請確定 OBS 檔案沒有被防毒/安全軟體阻擋或移除。"
HelperProcessFailed="無法啟動錄影協助程式。請確定 OBS 檔案沒有被防毒/安全軟體阻擋或移除。"
UnableToWritePath="無法寫入到 %1。請確定使用者帳戶可以寫入錄影檔路徑以及有足夠的磁碟空間。"
WarnWindowsDefender="如果啟用了 windows 10 勒索軟體保護, 也可能導致此錯誤。請嘗試將 obs 從 windows 安全/病毒和威脅防護設置中的受控資料夾訪問清單中移除。"
WarnWindowsDefender="如果啟用了 Windows 10 勒索軟體保護,也可能會導致此錯誤。請試試將 Windows 安全性 > [病毒與威脅防護] 設定中的 [受控資料夾存取權] 關閉。"

View file

@ -0,0 +1,873 @@
/******************************************************************************
Copyright (C) 2019 Haivision Systems Inc.
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "ffmpeg-encoded-output.h"
static int proto_init(struct ffmpeg_encoded_output *stream);
static int proto_try_connect(struct ffmpeg_encoded_output *stream);
static int proto_connect_time(struct ffmpeg_encoded_output *stream);
static void proto_close(struct ffmpeg_encoded_output *stream);
static void proto_set_output_error(struct ffmpeg_encoded_output *stream);
static int proto_send_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet, bool is_header);
static inline size_t num_buffered_packets(struct ffmpeg_encoded_output *stream);
static inline void free_packets(struct ffmpeg_encoded_output *stream);
static inline bool stopping(struct ffmpeg_encoded_output *stream);
static inline bool connecting(struct ffmpeg_encoded_output *stream);
static inline bool active(struct ffmpeg_encoded_output *stream);
static inline bool disconnected(struct ffmpeg_encoded_output *stream);
static inline bool get_next_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet);
#ifdef TEST_FRAMEDROPS
static void droptest_cap_data_rate(struct ffmpeg_encoded_output *stream,
size_t size);
#endif
static inline bool can_shutdown_stream(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet);
static void *send_thread(void *data);
static bool send_sps_pps(struct ffmpeg_encoded_output *stream);
static inline bool reset_semaphore(struct ffmpeg_encoded_output *stream);
static bool init_connect(struct ffmpeg_encoded_output *stream);
static int init_send(struct ffmpeg_encoded_output *stream);
static void *connect_thread(void *data);
static inline bool add_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet);
static inline size_t num_buffered_packets(struct ffmpeg_encoded_output *stream);
static void drop_frames(struct ffmpeg_encoded_output *stream, const char *name,
int highest_priority, bool pframes);
static bool find_first_video_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *first);
static void check_to_drop_frames(struct ffmpeg_encoded_output *stream,
bool pframes);
static bool add_video_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet);
static const char *ffmpeg_encoded_output_getname(void *unused)
{
UNUSED_PARAMETER(unused);
return obs_module_text("FFmpegEncodedOutput");
}
static inline bool add_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet)
{
circlebuf_push_back(&stream->packets, packet,
sizeof(struct encoder_packet));
return true;
}
static bool add_video_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet)
{
check_to_drop_frames(stream, false);
check_to_drop_frames(stream, true);
/* if currently dropping frames, drop packets until it reaches the
* desired priority */
if (packet->drop_priority < stream->min_priority) {
stream->dropped_frames++;
return false;
} else {
stream->min_priority = 0;
}
stream->last_dts_usec = packet->dts_usec;
return add_packet(stream, packet);
}
static inline bool get_next_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet)
{
bool new_packet = false;
pthread_mutex_lock(&stream->packets_mutex);
if (stream->packets.size) {
circlebuf_pop_front(&stream->packets, packet,
sizeof(struct encoder_packet));
new_packet = true;
}
pthread_mutex_unlock(&stream->packets_mutex);
return new_packet;
}
static inline void free_packets(struct ffmpeg_encoded_output *stream)
{
size_t num_packets;
pthread_mutex_lock(&stream->packets_mutex);
num_packets = num_buffered_packets(stream);
if (num_packets)
info("Freeing %d remaining packets", (int)num_packets);
while (stream->packets.size) {
struct encoder_packet packet;
circlebuf_pop_front(&stream->packets, &packet, sizeof(packet));
obs_encoder_packet_release(&packet);
}
pthread_mutex_unlock(&stream->packets_mutex);
}
static inline bool stopping(struct ffmpeg_encoded_output *stream)
{
return os_event_try(stream->stop_event) != EAGAIN;
}
static inline bool connecting(struct ffmpeg_encoded_output *stream)
{
return os_atomic_load_bool(&stream->connecting);
}
static inline bool active(struct ffmpeg_encoded_output *stream)
{
return os_atomic_load_bool(&stream->active);
}
static inline bool disconnected(struct ffmpeg_encoded_output *stream)
{
return os_atomic_load_bool(&stream->disconnected);
}
#ifdef TEST_FRAMEDROPS
static void droptest_cap_data_rate(struct ffmpeg_encoded_output *stream,
size_t size)
{
uint64_t ts = os_gettime_ns();
struct droptest_info info;
info.ts = ts;
info.size = size;
circlebuf_push_back(&stream->droptest_info, &info, sizeof(info));
stream->droptest_size += size;
if (stream->droptest_info.size) {
circlebuf_peek_front(&stream->droptest_info, &info,
sizeof(info));
if (stream->droptest_size > DROPTEST_MAX_BYTES) {
uint64_t elapsed = ts - info.ts;
if (elapsed < 1000000000ULL) {
elapsed = 1000000000ULL - elapsed;
os_sleepto_ns(ts + elapsed);
}
while (stream->droptest_size > DROPTEST_MAX_BYTES) {
circlebuf_pop_front(&stream->droptest_info,
&info, sizeof(info));
stream->droptest_size -= info.size;
}
}
}
}
#endif
static void *send_thread(void *data)
{
struct ffmpeg_encoded_output *stream = data;
os_set_thread_name("ffmpeg-stream: send_thread");
while (os_sem_wait(stream->send_sem) == 0) {
struct encoder_packet packet;
if (stopping(stream) && stream->stop_ts == 0)
break;
if (!get_next_packet(stream, &packet))
continue;
if (stopping(stream)) {
if (can_shutdown_stream(stream, &packet)) {
obs_encoder_packet_release(&packet);
break;
}
}
if (!stream->sent_sps_pps) {
if (!send_sps_pps(stream)) {
os_atomic_set_bool(&stream->disconnected, true);
break;
}
}
if (proto_send_packet(stream, &packet, false) < 0) {
os_atomic_set_bool(&stream->disconnected, true);
break;
}
}
if (disconnected(stream))
info("Disconnected from %s", stream->path.array);
else
info("User stopped the stream");
proto_set_output_error(stream);
proto_close(stream);
if (!stopping(stream)) {
pthread_detach(stream->send_thread);
obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED);
} else {
obs_output_end_data_capture(stream->output);
}
free_packets(stream);
os_event_reset(stream->stop_event);
os_atomic_set_bool(&stream->active, false);
stream->sent_sps_pps = false;
return NULL;
}
static bool send_sps_pps(struct ffmpeg_encoded_output *stream)
{
obs_output_t *context = stream->output;
obs_encoder_t *vencoder = obs_output_get_video_encoder(context);
uint8_t *header;
struct encoder_packet packet = {
.type = OBS_ENCODER_VIDEO, .timebase_den = 1, .keyframe = true};
if (obs_encoder_get_extra_data(vencoder, &header, &packet.size)) {
packet.data = bmemdup(header, packet.size);
stream->sent_sps_pps =
proto_send_packet(stream, &packet, true) >= 0;
}
return stream->sent_sps_pps;
}
static inline bool reset_semaphore(struct ffmpeg_encoded_output *stream)
{
os_sem_destroy(stream->send_sem);
return os_sem_init(&stream->send_sem, 0) == 0;
}
static bool init_connect(struct ffmpeg_encoded_output *stream)
{
obs_service_t *service;
obs_data_t *settings;
int64_t drop_p;
int64_t drop_b;
if (stopping(stream))
pthread_join(stream->send_thread, NULL);
free_packets(stream);
service = obs_output_get_service(stream->output);
if (!service)
return false;
os_atomic_set_bool(&stream->disconnected, false);
stream->total_bytes_sent = 0;
stream->dropped_frames = 0;
stream->min_priority = 0;
stream->got_first_video = false;
settings = obs_output_get_settings(stream->output);
dstr_copy(&stream->path, obs_service_get_url(service));
dstr_copy(&stream->key, obs_service_get_key(service));
dstr_copy(&stream->username, obs_service_get_username(service));
dstr_copy(&stream->password, obs_service_get_password(service));
dstr_depad(&stream->path);
dstr_depad(&stream->key);
drop_b = (int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD);
drop_p = (int64_t)obs_data_get_int(settings, OPT_PFRAME_DROP_THRESHOLD);
stream->max_shutdown_time_sec =
(int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC);
if (drop_p < (drop_b + 200))
drop_p = drop_b + 200;
stream->drop_threshold_usec = 1000 * drop_b;
stream->pframe_drop_threshold_usec = 1000 * drop_p;
obs_data_release(settings);
return true;
}
static void *connect_thread(void *data)
{
struct ffmpeg_encoded_output *stream = data;
int ret;
os_set_thread_name("ffmpeg-stream: connect_thread");
if (!init_connect(stream)) {
obs_output_signal_stop(stream->output, OBS_OUTPUT_BAD_PATH);
return NULL;
}
ret = proto_try_connect(stream);
if (ret == OBS_OUTPUT_SUCCESS)
ret = init_send(stream);
if (ret != OBS_OUTPUT_SUCCESS) {
obs_output_signal_stop(stream->output, ret);
info("Connection to %s failed: %d", stream->path.array, ret);
}
if (!stopping(stream))
pthread_detach(stream->connect_thread);
os_atomic_set_bool(&stream->connecting, false);
return NULL;
}
static inline size_t num_buffered_packets(struct ffmpeg_encoded_output *stream)
{
return stream->packets.size / sizeof(struct encoder_packet);
}
static void drop_frames(struct ffmpeg_encoded_output *stream, const char *name,
int highest_priority, bool pframes)
{
UNUSED_PARAMETER(pframes);
struct circlebuf new_buf = {0};
int num_frames_dropped = 0;
#ifdef _DEBUG
int start_packets = (int)num_buffered_packets(stream);
#else
UNUSED_PARAMETER(name);
#endif
circlebuf_reserve(&new_buf, sizeof(struct encoder_packet) * 8);
while (stream->packets.size) {
struct encoder_packet packet;
circlebuf_pop_front(&stream->packets, &packet, sizeof(packet));
/* do not drop audio data or video keyframes */
if (packet.type == OBS_ENCODER_AUDIO ||
packet.drop_priority >= highest_priority) {
circlebuf_push_back(&new_buf, &packet, sizeof(packet));
} else {
num_frames_dropped++;
obs_encoder_packet_release(&packet);
}
}
circlebuf_free(&stream->packets);
stream->packets = new_buf;
if (stream->min_priority < highest_priority)
stream->min_priority = highest_priority;
if (!num_frames_dropped)
return;
stream->dropped_frames += num_frames_dropped;
#ifdef _DEBUG
debug("Dropped %s, prev packet count: %d, new packet count: %d", name,
start_packets, (int)num_buffered_packets(stream));
#endif
}
static bool find_first_video_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *first)
{
size_t count = stream->packets.size / sizeof(*first);
for (size_t i = 0; i < count; i++) {
struct encoder_packet *cur =
circlebuf_data(&stream->packets, i * sizeof(*first));
if (cur->type == OBS_ENCODER_VIDEO && !cur->keyframe) {
*first = *cur;
return true;
}
}
return false;
}
static void check_to_drop_frames(struct ffmpeg_encoded_output *stream,
bool pframes)
{
struct encoder_packet first;
int64_t buffer_duration_usec;
size_t num_packets = num_buffered_packets(stream);
const char *name = pframes ? "p-frames" : "b-frames";
int priority = pframes ? OBS_NAL_PRIORITY_HIGHEST
: OBS_NAL_PRIORITY_HIGH;
int64_t drop_threshold = pframes ? stream->pframe_drop_threshold_usec
: stream->drop_threshold_usec;
if (num_packets < 5) {
if (!pframes)
stream->congestion = 0.0f;
return;
}
if (!find_first_video_packet(stream, &first))
return;
/* if the amount of time stored in the buffered packets waiting to be
* sent is higher than threshold, drop frames */
buffer_duration_usec = stream->last_dts_usec - first.dts_usec;
if (!pframes) {
stream->congestion =
(float)buffer_duration_usec / (float)drop_threshold;
}
if (buffer_duration_usec > drop_threshold) {
debug("buffer_duration_usec: %" PRId64, buffer_duration_usec);
drop_frames(stream, name, priority, pframes);
}
}
static int init_send(struct ffmpeg_encoded_output *stream)
{
int ret;
reset_semaphore(stream);
ret = pthread_create(&stream->send_thread, NULL, send_thread, stream);
if (ret != 0) {
proto_close(stream);
warn("Failed to create send thread");
return OBS_OUTPUT_ERROR;
}
os_atomic_set_bool(&stream->active, true);
obs_output_begin_data_capture(stream->output, 0);
return OBS_OUTPUT_SUCCESS;
}
static inline bool can_shutdown_stream(struct ffmpeg_encoded_output *stream,
struct encoder_packet *packet)
{
uint64_t cur_time = os_gettime_ns();
bool timeout = cur_time >= stream->shutdown_timeout_ts;
if (timeout)
info("Stream shutdown timeout reached (%d second(s))",
stream->max_shutdown_time_sec);
return timeout || packet->sys_dts_usec >= (int64_t)stream->stop_ts;
}
static int proto_init(struct ffmpeg_encoded_output *stream)
{
AVOutputFormat *outfmt = NULL;
int ret = 0;
//1. set up output format
outfmt = av_guess_format("mpegts", NULL, "video/MP2T");
if (outfmt == NULL) {
ret = -1;
} else {
stream->ff_data.output = avformat_alloc_context();
if (stream->ff_data.output)
stream->ff_data.output->oformat = outfmt;
else
ret = -1;
}
return ret;
}
static int get_audio_mix_count(int audio_mix_mask)
{
int mix_count = 0;
for (int i = 0; i < MAX_AUDIO_MIXES; i++) {
if ((audio_mix_mask & (1 << i)) != 0)
mix_count++;
}
return mix_count;
}
static inline int encoder_bitrate(obs_encoder_t *encoder)
{
obs_data_t *settings = obs_encoder_get_settings(encoder);
const int bitrate = (int)obs_data_get_int(settings, "bitrate");
obs_data_release(settings);
return bitrate;
}
static int proto_try_connect(struct ffmpeg_encoded_output *stream)
{
int ret = 0;
const char *url = stream->path.array;
obs_encoder_t *vencoder = obs_output_get_video_encoder(stream->output);
obs_encoder_t *aencoder =
obs_output_get_audio_encoder(stream->output, 0);
video_t *video = obs_encoder_video(vencoder);
audio_t *audio = obs_encoder_audio(aencoder);
const struct video_output_info *voi = video_output_get_info(video);
struct ffmpeg_cfg config;
bool success;
config.url = url;
config.format_name = "mpegts";
config.format_mime_type = "video/MP2T";
config.muxer_settings = "";
config.video_bitrate = encoder_bitrate(vencoder);
config.audio_bitrate = encoder_bitrate(aencoder);
config.gop_size = 250;
config.video_encoder = "";
config.video_encoder_id = AV_CODEC_ID_H264;
config.audio_encoder = "";
config.audio_encoder_id = AV_CODEC_ID_AAC;
config.video_settings = "";
config.audio_settings = "";
config.scale_width = 0;
config.scale_height = 0;
config.width = obs_encoder_get_width(vencoder);
config.height = obs_encoder_get_height(vencoder);
config.audio_tracks = (int)obs_output_get_mixer(stream->output);
config.audio_mix_count = 1;
config.format =
obs_to_ffmpeg_video_format(video_output_get_format(video));
if (format_is_yuv(voi->format)) {
config.color_range = voi->range == VIDEO_RANGE_FULL
? AVCOL_RANGE_JPEG
: AVCOL_RANGE_MPEG;
config.color_space = voi->colorspace == VIDEO_CS_709
? AVCOL_SPC_BT709
: AVCOL_SPC_BT470BG;
} else {
config.color_range = AVCOL_RANGE_UNSPECIFIED;
config.color_space = AVCOL_SPC_RGB;
}
if (config.format == AV_PIX_FMT_NONE) {
blog(LOG_DEBUG, "invalid pixel format used for FFmpeg output");
return false;
}
if (!config.scale_width)
config.scale_width = config.width;
if (!config.scale_height)
config.scale_height = config.height;
success = ffmpeg_data_init(&stream->ff_data, &config);
if (!success)
return -1;
return ret;
}
static int proto_connect_time(struct ffmpeg_encoded_output *stream)
{
UNUSED_PARAMETER(stream);
return 0;
}
static void proto_close(struct ffmpeg_encoded_output *stream)
{
ffmpeg_data_free(&stream->ff_data);
}
static inline int64_t _rescale_ts(struct ffmpeg_encoded_output *stream,
int64_t val, int idx)
{
AVStream *avstream = stream->ff_data.output->streams[idx];
return av_rescale_q_rnd(val / avstream->codec->time_base.num,
avstream->codec->time_base, avstream->time_base,
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
}
static int proto_send_packet(struct ffmpeg_encoded_output *stream,
struct encoder_packet *obs_packet, bool is_header)
{
AVPacket av_packet = {0};
int ret = 0;
int streamIdx = (obs_packet->type == OBS_ENCODER_VIDEO) ? 0 : 1;
//2. send
av_init_packet(&av_packet);
av_packet.data = obs_packet->data;
av_packet.size = (int)obs_packet->size;
av_packet.stream_index = streamIdx;
av_packet.pts = _rescale_ts(stream, obs_packet->pts, streamIdx);
av_packet.dts = _rescale_ts(stream, obs_packet->dts, streamIdx);
if (obs_packet->keyframe)
av_packet.flags = AV_PKT_FLAG_KEY;
ret = av_interleaved_write_frame(stream->ff_data.output, &av_packet);
stream->total_bytes_sent += obs_packet->size;
if (is_header)
bfree(obs_packet->data);
else
obs_encoder_packet_release(obs_packet);
return ret;
}
static void proto_set_output_error(struct ffmpeg_encoded_output *stream)
{
UNUSED_PARAMETER(stream);
}
static void ffmpeg_encoded_output_destroy(void *data)
{
struct ffmpeg_encoded_output *stream = data;
if (stopping(stream) && !connecting(stream)) {
pthread_join(stream->send_thread, NULL);
} else if (connecting(stream) || active(stream)) {
if (stream->connecting)
pthread_join(stream->connect_thread, NULL);
stream->stop_ts = 0;
os_event_signal(stream->stop_event);
if (active(stream)) {
os_sem_post(stream->send_sem);
obs_output_end_data_capture(stream->output);
pthread_join(stream->send_thread, NULL);
}
}
free_packets(stream);
dstr_free(&stream->path);
dstr_free(&stream->key);
dstr_free(&stream->username);
dstr_free(&stream->password);
dstr_free(&stream->encoder_name);
os_event_destroy(stream->stop_event);
os_sem_destroy(stream->send_sem);
pthread_mutex_destroy(&stream->packets_mutex);
circlebuf_free(&stream->packets);
#ifdef TEST_FRAMEDROPS
circlebuf_free(&stream->droptest_info);
#endif
os_event_destroy(stream->buffer_space_available_event);
os_event_destroy(stream->buffer_has_data_event);
os_event_destroy(stream->send_thread_signaled_exit);
pthread_mutex_destroy(&stream->write_buf_mutex);
ffmpeg_data_free(&stream->ff_data);
if (stream->write_buf)
bfree(stream->write_buf);
bfree(stream);
}
static void *ffmpeg_encoded_output_create(obs_data_t *settings,
obs_output_t *output)
{
struct ffmpeg_encoded_output *stream =
bzalloc(sizeof(struct ffmpeg_encoded_output));
stream->output = output;
pthread_mutex_init_value(&stream->packets_mutex);
proto_init(stream);
if (pthread_mutex_init(&stream->packets_mutex, NULL) != 0)
goto fail;
if (os_event_init(&stream->stop_event, OS_EVENT_TYPE_MANUAL) != 0)
goto fail;
if (pthread_mutex_init(&stream->write_buf_mutex, NULL) != 0) {
warn("Failed to initialize write buffer mutex");
goto fail;
}
if (os_event_init(&stream->buffer_space_available_event,
OS_EVENT_TYPE_AUTO) != 0) {
warn("Failed to initialize write buffer event");
goto fail;
}
if (os_event_init(&stream->buffer_has_data_event, OS_EVENT_TYPE_AUTO) !=
0) {
warn("Failed to initialize data buffer event");
goto fail;
}
if (os_event_init(&stream->send_thread_signaled_exit,
OS_EVENT_TYPE_MANUAL) != 0) {
warn("Failed to initialize socket exit event");
goto fail;
}
UNUSED_PARAMETER(settings);
return stream;
fail:
ffmpeg_encoded_output_destroy(stream);
return NULL;
}
static bool ffmpeg_encoded_output_start(void *data)
{
struct ffmpeg_encoded_output *stream = data;
if (!obs_output_can_begin_data_capture(stream->output, 0))
return false;
if (!obs_output_initialize_encoders(stream->output, 0))
return false;
os_atomic_set_bool(&stream->connecting, true);
return pthread_create(&stream->connect_thread, NULL, connect_thread,
stream) == 0;
}
#define MILLISECOND_DEN 1000
static int32_t get_ms_time(struct encoder_packet *packet, int64_t val)
{
return (int32_t)(val * MILLISECOND_DEN / packet->timebase_den);
}
static void ffmpeg_encoded_output_data(void *data,
struct encoder_packet *packet)
{
struct ffmpeg_encoded_output *stream = data;
struct encoder_packet new_packet;
bool added_packet = false;
if (disconnected(stream) || !active(stream))
return;
if (packet->type == OBS_ENCODER_VIDEO) {
if (!stream->got_first_video) {
stream->start_dts_offset =
get_ms_time(packet, packet->dts);
stream->got_first_video = true;
}
}
obs_encoder_packet_ref(&new_packet, packet);
pthread_mutex_lock(&stream->packets_mutex);
if (!disconnected(stream))
added_packet = (packet->type == OBS_ENCODER_VIDEO)
? add_video_packet(stream, &new_packet)
: add_packet(stream, &new_packet);
pthread_mutex_unlock(&stream->packets_mutex);
if (added_packet)
os_sem_post(stream->send_sem);
else
obs_encoder_packet_release(&new_packet);
}
static void ffmpeg_encoded_output_stop(void *data, uint64_t ts)
{
struct ffmpeg_encoded_output *stream = data;
if (stopping(stream) && ts != 0)
return;
if (connecting(stream))
pthread_join(stream->connect_thread, NULL);
stream->stop_ts = ts / 1000ULL;
if (ts)
stream->shutdown_timeout_ts =
ts +
(uint64_t)stream->max_shutdown_time_sec * 1000000000ULL;
if (active(stream)) {
os_event_signal(stream->stop_event);
if (stream->stop_ts == 0)
os_sem_post(stream->send_sem);
} else {
obs_output_signal_stop(stream->output, OBS_OUTPUT_SUCCESS);
}
}
static void ffmpeg_encoded_output_defaults(obs_data_t *defaults)
{
obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 700);
obs_data_set_default_int(defaults, OPT_PFRAME_DROP_THRESHOLD, 900);
obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 30);
}
static obs_properties_t *ffmpeg_encoded_output_properties(void *unused)
{
UNUSED_PARAMETER(unused);
obs_properties_t *props = obs_properties_create();
obs_properties_add_int(
props, OPT_DROP_THRESHOLD,
obs_module_text("FFmpegEncodedOutput.DropThreshold"), 200,
10000, 100);
return props;
}
static uint64_t ffmpeg_encoded_output_total_bytes_sent(void *data)
{
struct ffmpeg_encoded_output *stream = data;
return stream->total_bytes_sent;
}
static int ffmpeg_encoded_output_dropped_frames(void *data)
{
struct ffmpeg_encoded_output *stream = data;
return stream->dropped_frames;
}
static float ffmpeg_encoded_output_congestion(void *data)
{
struct ffmpeg_encoded_output *stream = data;
return stream->min_priority > 0 ? 1.0f : stream->congestion;
}
static int ffmpeg_encoded_output_connect_time(void *data)
{
struct ffmpeg_encoded_output *stream = data;
return proto_connect_time(stream);
}
struct obs_output_info ffmpeg_encoded_output_info = {
.id = "ffmpeg_encoded_output",
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE |
OBS_OUTPUT_MULTI_TRACK,
.encoded_video_codecs = "h264",
.encoded_audio_codecs = "aac",
.get_name = ffmpeg_encoded_output_getname,
.create = ffmpeg_encoded_output_create,
.destroy = ffmpeg_encoded_output_destroy,
.start = ffmpeg_encoded_output_start,
.stop = ffmpeg_encoded_output_stop,
.encoded_packet = ffmpeg_encoded_output_data,
.get_defaults = ffmpeg_encoded_output_defaults,
.get_properties = ffmpeg_encoded_output_properties,
.get_total_bytes = ffmpeg_encoded_output_total_bytes_sent,
.get_congestion = ffmpeg_encoded_output_congestion,
.get_connect_time_ms = ffmpeg_encoded_output_connect_time,
.get_dropped_frames = ffmpeg_encoded_output_dropped_frames};

View file

@ -0,0 +1,108 @@
/******************************************************************************
Copyright (C) 2019 Haivision Systems Inc.
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include <obs-module.h>
#include <obs-avc.h>
#include <util/platform.h>
#include <util/circlebuf.h>
#include <util/dstr.h>
#include <util/threading.h>
#include <inttypes.h>
#include "obs-ffmpeg-output.h"
#include "obs-ffmpeg-formats.h"
#define do_log(level, format, ...) \
blog(level, "[ffmpeg-encoded-output: '%s'] " format, \
obs_output_get_name(stream->output), ##__VA_ARGS__)
#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
#define OPT_DROP_THRESHOLD "drop_threshold_ms"
#define OPT_PFRAME_DROP_THRESHOLD "pframe_drop_threshold_ms"
#define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec"
#define OPT_LOWLATENCY_ENABLED "low_latency_mode_enabled"
//#define TEST_FRAMEDROPS
#ifdef TEST_FRAMEDROPS
#define DROPTEST_MAX_KBPS 3000
#define DROPTEST_MAX_BYTES (DROPTEST_MAX_KBPS * 1000 / 8)
struct droptest_info {
uint64_t ts;
size_t size;
};
#endif
struct ffmpeg_encoded_output {
obs_output_t *output;
pthread_mutex_t packets_mutex;
struct circlebuf packets;
bool sent_sps_pps;
bool got_first_video;
int64_t start_dts_offset;
volatile bool connecting;
pthread_t connect_thread;
volatile bool active;
volatile bool disconnected;
pthread_t send_thread;
int max_shutdown_time_sec;
os_sem_t *send_sem;
os_event_t *stop_event;
uint64_t stop_ts;
uint64_t shutdown_timeout_ts;
struct dstr path, key;
struct dstr username, password;
struct dstr encoder_name;
/* frame drop variables */
int64_t drop_threshold_usec;
int64_t pframe_drop_threshold_usec;
int min_priority;
float congestion;
int64_t last_dts_usec;
uint64_t total_bytes_sent;
int dropped_frames;
#ifdef TEST_FRAMEDROPS
struct circlebuf droptest_info;
size_t droptest_size;
#endif
uint8_t *write_buf;
size_t write_buf_len;
size_t write_buf_size;
pthread_mutex_t write_buf_mutex;
os_event_t *buffer_space_available_event;
os_event_t *buffer_has_data_event;
os_event_t *send_thread_signaled_exit;
struct ffmpeg_data ff_data;
};

View file

@ -581,6 +581,9 @@ static void *nvenc_create(obs_data_t *settings, obs_encoder_t *encoder)
goto fail;
}
if (obs_encoder_scaling_enabled(encoder)) {
goto fail;
}
if (!obs_nv12_tex_active()) {
goto fail;
}

View file

@ -570,5 +570,9 @@ struct obs_encoder_info nvenc_encoder_info = {
.get_extra_data = nvenc_extra_data,
.get_sei_data = nvenc_sei_data,
.get_video_info = nvenc_video_info,
#ifdef _WIN32
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL,
#else
.caps = OBS_ENCODER_CAP_DYN_BITRATE,
#endif
};

View file

@ -22,74 +22,10 @@
#include <util/darray.h>
#include <util/platform.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include "obs-ffmpeg-output.h"
#include "obs-ffmpeg-formats.h"
#include "closest-pixel-format.h"
#include "obs-ffmpeg-compat.h"
struct ffmpeg_cfg {
const char *url;
const char *format_name;
const char *format_mime_type;
const char *muxer_settings;
int gop_size;
int video_bitrate;
int audio_bitrate;
const char *video_encoder;
int video_encoder_id;
const char *audio_encoder;
int audio_encoder_id;
const char *video_settings;
const char *audio_settings;
int audio_mix_count;
int audio_tracks;
enum AVPixelFormat format;
enum AVColorRange color_range;
enum AVColorSpace color_space;
int scale_width;
int scale_height;
int width;
int height;
};
struct ffmpeg_data {
AVStream *video;
AVStream **audio_streams;
AVCodec *acodec;
AVCodec *vcodec;
AVFormatContext *output;
struct SwsContext *swscale;
int64_t total_frames;
AVFrame *vframe;
int frame_size;
uint64_t start_timestamp;
int64_t total_samples[MAX_AUDIO_MIXES];
uint32_t audio_samplerate;
enum audio_format audio_format;
size_t audio_planes;
size_t audio_size;
int num_audio_streams;
/* audio_tracks is a bitmask storing the indices of the mixes */
int audio_tracks;
struct circlebuf excess_frames[MAX_AUDIO_MIXES][MAX_AV_PLANES];
uint8_t *samples[MAX_AUDIO_MIXES][MAX_AV_PLANES];
AVFrame *aframe[MAX_AUDIO_MIXES];
struct ffmpeg_cfg config;
bool initialized;
char *last_error;
};
struct ffmpeg_output {
obs_output_t *output;
volatile bool active;
@ -279,11 +215,11 @@ static bool create_video_stream(struct ffmpeg_data *data)
data->config.video_encoder))
return false;
closest_format =
get_closest_format(data->config.format, data->vcodec->pix_fmts);
closest_format = avcodec_find_best_pix_fmt_of_list(
data->vcodec->pix_fmts, data->config.format, 0, NULL);
context = data->video->codec;
context->bit_rate = data->config.video_bitrate * 1000;
context->bit_rate = (int64_t)data->config.video_bitrate * 1000;
context->width = data->config.scale_width;
context->height = data->config.scale_height;
context->time_base = (AVRational){ovi.fps_den, ovi.fps_num};
@ -377,7 +313,7 @@ static bool create_audio_stream(struct ffmpeg_data *data, int idx)
data->audio_streams[idx] = stream;
context = data->audio_streams[idx]->codec;
context->bit_rate = data->config.audio_bitrate * 1000;
context->bit_rate = (int64_t)data->config.audio_bitrate * 1000;
context->time_base = (AVRational){1, aoi.samples_per_sec};
context->channels = get_audio_channels(aoi.speakers);
context->sample_rate = aoi.samples_per_sec;
@ -523,7 +459,7 @@ static void close_audio(struct ffmpeg_data *data)
}
}
static void ffmpeg_data_free(struct ffmpeg_data *data)
void ffmpeg_data_free(struct ffmpeg_data *data)
{
if (data->initialized)
av_write_trailer(data->output);
@ -583,8 +519,7 @@ static void set_encoder_ids(struct ffmpeg_data *data)
data->config.audio_encoder, data->config.audio_encoder_id);
}
static bool ffmpeg_data_init(struct ffmpeg_data *data,
struct ffmpeg_cfg *config)
bool ffmpeg_data_init(struct ffmpeg_data *data, struct ffmpeg_cfg *config)
{
bool is_rtmp = false;

View file

@ -0,0 +1,68 @@
#pragma once
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
struct ffmpeg_cfg {
const char *url;
const char *format_name;
const char *format_mime_type;
const char *muxer_settings;
int gop_size;
int video_bitrate;
int audio_bitrate;
const char *video_encoder;
int video_encoder_id;
const char *audio_encoder;
int audio_encoder_id;
const char *video_settings;
const char *audio_settings;
int audio_mix_count;
int audio_tracks;
enum AVPixelFormat format;
enum AVColorRange color_range;
enum AVColorSpace color_space;
int scale_width;
int scale_height;
int width;
int height;
};
struct ffmpeg_data {
AVStream *video;
AVStream **audio_streams;
AVCodec *acodec;
AVCodec *vcodec;
AVFormatContext *output;
struct SwsContext *swscale;
int64_t total_frames;
AVFrame *vframe;
int frame_size;
uint64_t start_timestamp;
int64_t total_samples[MAX_AUDIO_MIXES];
uint32_t audio_samplerate;
enum audio_format audio_format;
size_t audio_planes;
size_t audio_size;
int num_audio_streams;
/* audio_tracks is a bitmask storing the indices of the mixes */
int audio_tracks;
struct circlebuf excess_frames[MAX_AUDIO_MIXES][MAX_AV_PLANES];
uint8_t *samples[MAX_AUDIO_MIXES][MAX_AV_PLANES];
AVFrame *aframe[MAX_AUDIO_MIXES];
struct ffmpeg_cfg config;
bool initialized;
char *last_error;
};
bool ffmpeg_data_init(struct ffmpeg_data *data, struct ffmpeg_cfg *config);
void ffmpeg_data_free(struct ffmpeg_data *data);

View file

@ -57,8 +57,18 @@ struct ffmpeg_source {
bool restart_on_activate;
bool close_when_inactive;
bool seekable;
enum obs_media_state state;
obs_hotkey_pair_id play_pause_hotkey;
obs_hotkey_id stop_hotkey;
};
static void set_media_state(void *data, enum obs_media_state state)
{
struct ffmpeg_source *s = data;
s->state = state;
}
static bool is_local_file_modified(obs_properties_t *props,
obs_property_t *prop, obs_data_t *settings)
{
@ -251,6 +261,9 @@ static void media_stopped(void *opaque)
if (s->close_when_inactive && s->media_valid)
s->destroy_media = true;
}
set_media_state(s, OBS_MEDIA_STATE_ENDED);
obs_source_media_ended(s->source);
}
static void ffmpeg_source_open(struct ffmpeg_source *s)
@ -297,6 +310,8 @@ static void ffmpeg_source_start(struct ffmpeg_source *s)
mp_media_play(&s->media, s->is_looping);
if (s->is_local_file)
obs_source_show_preloaded_video(s->source);
set_media_state(s, OBS_MEDIA_STATE_PLAYING);
obs_source_media_started(s->source);
}
}
@ -370,11 +385,13 @@ static void restart_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(hotkey);
UNUSED_PARAMETER(pressed);
if (!pressed)
return;
struct ffmpeg_source *s = data;
if (obs_source_active(s->source))
ffmpeg_source_start(s);
obs_source_media_restart(s->source);
}
static void restart_proc(void *data, calldata_t *cd)
@ -430,6 +447,59 @@ static void get_nb_frames(void *data, calldata_t *cd)
calldata_set_int(cd, "num_frames", frames);
}
static bool ffmpeg_source_play_hotkey(void *data, obs_hotkey_pair_id id,
obs_hotkey_t *hotkey, bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(hotkey);
if (!pressed)
return false;
struct ffmpeg_source *s = data;
if (s->state == OBS_MEDIA_STATE_PLAYING ||
!obs_source_active(s->source))
return false;
obs_source_media_play_pause(s->source, false);
return true;
}
static bool ffmpeg_source_pause_hotkey(void *data, obs_hotkey_pair_id id,
obs_hotkey_t *hotkey, bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(hotkey);
if (!pressed)
return false;
struct ffmpeg_source *s = data;
if (s->state != OBS_MEDIA_STATE_PLAYING ||
!obs_source_active(s->source))
return false;
obs_source_media_play_pause(s->source, true);
return true;
}
static void ffmpeg_source_stop_hotkey(void *data, obs_hotkey_id id,
obs_hotkey_t *hotkey, bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(hotkey);
if (!pressed)
return;
struct ffmpeg_source *s = data;
if (obs_source_active(s->source))
obs_source_media_stop(s->source);
}
static void *ffmpeg_source_create(obs_data_t *settings, obs_source_t *source)
{
UNUSED_PARAMETER(settings);
@ -441,6 +511,16 @@ static void *ffmpeg_source_create(obs_data_t *settings, obs_source_t *source)
obs_module_text("RestartMedia"),
restart_hotkey, s);
s->play_pause_hotkey = obs_hotkey_pair_register_source(
s->source, "MediaSource.Play", obs_module_text("Play"),
"MediaSource.Pause", obs_module_text("Pause"),
ffmpeg_source_play_hotkey, ffmpeg_source_pause_hotkey, s, s);
s->stop_hotkey = obs_hotkey_register_source(source, "MediaSource.Stop",
obs_module_text("Stop"),
ffmpeg_source_stop_hotkey,
s);
proc_handler_t *ph = obs_source_get_proc_handler(source);
proc_handler_add(ph, "void restart()", restart_proc, s);
proc_handler_add(ph, "void get_duration(out int duration)",
@ -474,7 +554,7 @@ static void ffmpeg_source_activate(void *data)
struct ffmpeg_source *s = data;
if (s->restart_on_activate)
ffmpeg_source_start(s);
obs_source_media_restart(s->source);
}
static void ffmpeg_source_deactivate(void *data)
@ -491,11 +571,77 @@ static void ffmpeg_source_deactivate(void *data)
}
}
static void ffmpeg_source_play_pause(void *data, bool pause)
{
struct ffmpeg_source *s = data;
mp_media_play_pause(&s->media, pause);
if (pause)
set_media_state(s, OBS_MEDIA_STATE_PAUSED);
else
set_media_state(s, OBS_MEDIA_STATE_PLAYING);
}
static void ffmpeg_source_stop(void *data)
{
struct ffmpeg_source *s = data;
if (s->media_valid) {
mp_media_stop(&s->media);
obs_source_output_video(s->source, NULL);
set_media_state(s, OBS_MEDIA_STATE_STOPPED);
}
}
static void ffmpeg_source_restart(void *data)
{
struct ffmpeg_source *s = data;
if (obs_source_active(s->source))
ffmpeg_source_start(s);
set_media_state(s, OBS_MEDIA_STATE_PLAYING);
}
static int64_t ffmpeg_source_get_duration(void *data)
{
struct ffmpeg_source *s = data;
int64_t dur = 0;
if (s->media.fmt)
dur = s->media.fmt->duration / INT64_C(1000);
return dur;
}
static int64_t ffmpeg_source_get_time(void *data)
{
struct ffmpeg_source *s = data;
return mp_get_current_time(&s->media);
}
static void ffmpeg_source_set_time(void *data, int64_t ms)
{
struct ffmpeg_source *s = data;
mp_media_seek_to(&s->media, ms);
}
static enum obs_media_state ffmpeg_source_get_state(void *data)
{
struct ffmpeg_source *s = data;
return s->state;
}
struct obs_source_info ffmpeg_source = {
.id = "ffmpeg_source",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO |
OBS_SOURCE_DO_NOT_DUPLICATE,
OBS_SOURCE_DO_NOT_DUPLICATE |
OBS_SOURCE_CONTROLLABLE_MEDIA,
.get_name = ffmpeg_source_getname,
.create = ffmpeg_source_create,
.destroy = ffmpeg_source_destroy,
@ -505,4 +651,12 @@ struct obs_source_info ffmpeg_source = {
.deactivate = ffmpeg_source_deactivate,
.video_tick = ffmpeg_source_tick,
.update = ffmpeg_source_update,
.icon_type = OBS_ICON_TYPE_MEDIA,
.media_play_pause = ffmpeg_source_play_pause,
.media_restart = ffmpeg_source_restart,
.media_stop = ffmpeg_source_stop,
.media_get_duration = ffmpeg_source_get_duration,
.media_get_time = ffmpeg_source_get_time,
.media_set_time = ffmpeg_source_set_time,
.media_get_state = ffmpeg_source_get_state,
};

View file

@ -160,24 +160,68 @@ static bool vaapi_init_codec(struct vaapi_encoder *enc, const char *path)
return true;
}
/* "Allowed" options per Rate Control
* See FFMPEG libavcodec/vaapi_encode.c (vaapi_encode_rc_modes)
*/
typedef struct {
const char *name;
bool qp;
bool bitrate;
bool maxrate;
} rc_mode_t;
static const rc_mode_t *get_rc_mode(const char *name)
{
/* Set "allowed" options per Rate Control */
static const rc_mode_t RC_MODES[] = {
{.name = "CBR", .qp = false, .bitrate = true, .maxrate = false},
{.name = "CQP", .qp = true, .bitrate = false, .maxrate = false},
{.name = "VBR", .qp = false, .bitrate = true, .maxrate = true},
NULL};
const rc_mode_t *rc_mode = RC_MODES;
while (!!rc_mode && strcmp(rc_mode->name, name) != 0)
rc_mode++;
return rc_mode ? rc_mode : RC_MODES;
}
static bool vaapi_update(void *data, obs_data_t *settings)
{
struct vaapi_encoder *enc = data;
const char *device = obs_data_get_string(settings, "vaapi_device");
const char *rate_control =
obs_data_get_string(settings, "rate_control");
const rc_mode_t *rc_mode = get_rc_mode(rate_control);
bool cbr = strcmp(rc_mode->name, "CBR") == 0;
int profile = (int)obs_data_get_int(settings, "profile");
int bf = (int)obs_data_get_int(settings, "bf");
int level = (int)obs_data_get_int(settings, "level");
int bitrate = (int)obs_data_get_int(settings, "bitrate");
int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec");
int qp = (int)obs_data_get_int(settings, "qp");
int quality = (int)obs_data_get_int(settings, "quality");
int qp = rc_mode->qp ? (int)obs_data_get_int(settings, "qp") : 0;
av_opt_set_int(enc->context->priv_data, "qp", qp, 0);
av_opt_set_int(enc->context->priv_data, "quality", quality, 0);
int level = (int)obs_data_get_int(settings, "level");
int bitrate = rc_mode->bitrate
? (int)obs_data_get_int(settings, "bitrate")
: 0;
int maxrate = rc_mode->maxrate
? (int)obs_data_get_int(settings, "maxrate")
: 0;
int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec");
/* For Rate Control which allows maxrate, FFMPEG will give
* an error if maxrate > bitrate. To prevent that set maxrate
* to 0.
* For CBR, maxrate = bitrate
*/
if (cbr)
maxrate = bitrate;
else if (rc_mode->maxrate && maxrate && maxrate < bitrate)
maxrate = 0;
video_t *video = obs_encoder_video(enc->encoder);
const struct video_output_info *voi = video_output_get_info(video);
@ -193,7 +237,7 @@ static bool vaapi_update(void *data, obs_data_t *settings)
enc->context->max_b_frames = bf;
enc->context->level = level;
enc->context->bit_rate = bitrate * 1000;
enc->context->rc_max_rate = bitrate * 1000;
enc->context->rc_max_rate = maxrate * 1000;
enc->context->width = obs_encoder_get_width(enc->encoder);
enc->context->height = obs_encoder_get_height(enc->encoder);
@ -218,16 +262,17 @@ static bool vaapi_update(void *data, obs_data_t *settings)
info("settings:\n"
"\tdevice: %s\n"
"\tqp: %d\n"
"\tquality: %d\n"
"\trate_control: %s\n"
"\tprofile: %d\n"
"\tlevel: %d\n"
"\tqp: %d\n"
"\tbitrate: %d\n"
"\tmaxrate: %d\n"
"\tkeyint: %d\n"
"\twidth: %d\n"
"\theight: %d\n"
"\tb-frames: %d\n",
device, qp, quality, profile, level, bitrate,
device, rate_control, profile, level, qp, bitrate, maxrate,
enc->context->gop_size, enc->context->width, enc->context->height,
enc->context->max_b_frames);
@ -453,9 +498,28 @@ static void vaapi_defaults(obs_data_t *settings)
obs_data_set_default_int(settings, "bitrate", 2500);
obs_data_set_default_int(settings, "keyint_sec", 0);
obs_data_set_default_int(settings, "bf", 0);
obs_data_set_default_int(settings, "qp", 20);
obs_data_set_default_int(settings, "quality", 0);
obs_data_set_default_int(settings, "rendermode", 0);
obs_data_set_default_string(settings, "rate_control", "CBR");
obs_data_set_default_int(settings, "qp", 20);
obs_data_set_default_int(settings, "maxrate", 0);
}
static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p,
obs_data_t *settings)
{
UNUSED_PARAMETER(p);
const char *rate_control =
obs_data_get_string(settings, "rate_control");
const rc_mode_t *rc_mode = get_rc_mode(rate_control);
/* Set options visibility per Rate Control */
set_visible(ppts, "qp", rc_mode->qp);
set_visible(ppts, "bitrate", rc_mode->bitrate);
set_visible(ppts, "maxrate", rc_mode->maxrate);
return true;
}
static obs_properties_t *vaapi_properties(void *unused)
@ -496,14 +560,30 @@ static obs_properties_t *vaapi_properties(void *unused)
obs_property_list_add_int(list, "720p60/1080p30 (4.1)", 41);
obs_property_list_add_int(list, "1080p60 (4.2)", 42);
list = obs_properties_add_list(props, "rate_control",
obs_module_text("RateControl"),
OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_STRING);
obs_property_list_add_string(list, "CBR (default)", "CBR");
obs_property_list_add_string(list, "CQP", "CQP");
obs_property_list_add_string(list, "VBR", "VBR");
obs_property_set_modified_callback(list, rate_control_modified);
obs_property_t *p;
p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"),
0, 300000, 50);
obs_property_int_set_suffix(p, " Kbps");
p = obs_properties_add_int(
props, "maxrate", obs_module_text("MaxBitrate"), 0, 300000, 50);
obs_property_int_set_suffix(p, " Kbps");
obs_properties_add_int(props, "qp", "QP", 0, 51, 1);
obs_properties_add_int(props, "keyint_sec",
obs_module_text("Keyframe Interval (seconds)"),
0, 20, 1);
obs_module_text("KeyframeIntervalSec"), 0, 20,
1);
return props;
}

View file

@ -26,6 +26,7 @@ extern struct obs_output_info replay_buffer;
extern struct obs_encoder_info aac_encoder_info;
extern struct obs_encoder_info opus_encoder_info;
extern struct obs_encoder_info nvenc_encoder_info;
extern struct obs_output_info ffmpeg_encoded_output_info;
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 27, 100)
#define LIBAVUTIL_VAAPI_AVAILABLE
@ -40,42 +41,59 @@ extern struct obs_encoder_info vaapi_encoder_info;
static const char *nvenc_check_name = "nvenc_check";
#ifdef _WIN32
static const wchar_t *blacklisted_adapters[] = {
L"720M", L"730M", L"740M", L"745M", L"820M", L"830M",
L"840M", L"845M", L"920M", L"930M", L"940M", L"945M",
L"1030", L"MX110", L"MX130", L"MX150", L"MX230", L"MX250",
L"M520", L"M500", L"P500", L"K620M",
static const int blacklisted_adapters[] = {
0x1298, // GK208M [GeForce GT 720M]
0x1140, // GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M]
0x1293, // GK208M [GeForce GT 730M]
0x1290, // GK208M [GeForce GT 730M]
0x0fe1, // GK107M [GeForce GT 730M]
0x0fdf, // GK107M [GeForce GT 740M]
0x1294, // GK208M [GeForce GT 740M]
0x1292, // GK208M [GeForce GT 740M]
0x0fe2, // GK107M [GeForce GT 745M]
0x0fe3, // GK107M [GeForce GT 745M]
0x1140, // GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M]
0x0fed, // GK107M [GeForce 820M]
0x1340, // GM108M [GeForce 830M]
0x1393, // GM107M [GeForce 840M]
0x1341, // GM108M [GeForce 840M]
0x1398, // GM107M [GeForce 845M]
0x1390, // GM107M [GeForce 845M]
0x1344, // GM108M [GeForce 845M]
0x1299, // GK208BM [GeForce 920M]
0x134f, // GM108M [GeForce 920MX]
0x134e, // GM108M [GeForce 930MX]
0x1349, // GM108M [GeForce 930M]
0x1346, // GM108M [GeForce 930M]
0x179c, // GM107 [GeForce 940MX]
0x139c, // GM107M [GeForce 940M]
0x1347, // GM108M [GeForce 940M]
0x134d, // GM108M [GeForce 940MX]
0x134b, // GM108M [GeForce 940MX]
0x1399, // GM107M [GeForce 945M]
0x1348, // GM108M [GeForce 945M / 945A]
0x1d01, // GP108 [GeForce GT 1030]
0x0fc5, // GK107 [GeForce GT 1030]
0x174e, // GM108M [GeForce MX110]
0x174d, // GM108M [GeForce MX130]
0x1d10, // GP108M [GeForce MX150]
0x1d12, // GP108M [GeForce MX150]
0x1d11, // GP108M [GeForce MX230]
0x1d13, // GP108M [GeForce MX250]
0x1d52, // GP108BM [GeForce MX250]
0x137b, // GM108GLM [Quadro M520 Mobile]
0x1d33, // GP108GLM [Quadro P500 Mobile]
0x137a, // GM108GLM [Quadro K620M / Quadro M500M]
};
static const size_t num_blacklisted =
sizeof(blacklisted_adapters) / sizeof(blacklisted_adapters[0]);
static bool is_adapter(const wchar_t *name, const wchar_t *adapter)
{
const wchar_t *find = wstrstri(name, adapter);
if (!find) {
return false;
}
/* check before string for potential numeric mismatch */
if (find > name && iswdigit(find[-1]) && iswdigit(find[0])) {
return false;
}
/* check after string for potential numeric mismatch */
size_t len = wcslen(adapter);
if (iswdigit(find[len - 1]) && iswdigit(find[len])) {
return false;
}
return true;
}
static bool is_blacklisted(const wchar_t *name)
static bool is_blacklisted(const int device_id)
{
for (size_t i = 0; i < num_blacklisted; i++) {
const wchar_t *blacklisted_adapter = blacklisted_adapters[i];
if (is_adapter(name, blacklisted_adapter)) {
const int blacklisted_adapter = blacklisted_adapters[i];
if (device_id == blacklisted_adapter) {
return true;
}
}
@ -128,8 +146,8 @@ static bool nvenc_device_available(void)
continue;
}
if (wstrstri(desc.Description, L"nvidia") &&
!is_blacklisted(desc.Description)) {
// 0x10de = NVIDIA Corporation
if (desc.VendorId == 0x10de && !is_blacklisted(desc.DeviceId)) {
available = true;
goto finish;
}
@ -215,6 +233,7 @@ bool obs_module_load(void)
obs_register_output(&replay_buffer);
obs_register_encoder(&aac_encoder_info);
obs_register_encoder(&opus_encoder_info);
obs_register_output(&ffmpeg_encoded_output_info);
#ifndef __APPLE__
if (nvenc_supported()) {
blog(LOG_INFO, "NVENC supported");