New upstream version 25.0.3+dfsg1
This commit is contained in:
parent
04fe0efc67
commit
8b2e5f2130
569 changed files with 62491 additions and 5875 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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="Записване на повторението"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ FFmpegOpus="FFmpeg‐Opus‐Kodierer"
|
|||
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="Look‐ahead"
|
||||
NVENC.LookAhead.ToolTip="Aktiviert dynamische B‐Frames.\n\nWenn deaktiviert, wird der Kodierer immer die Anzahl der B‐Frames verwenden, die in der „Max B‐Frames“‐Einstellung angegeben sind.\n\nWenn aktiviert, wird er die visuelle Qualität erhöhen, indem nur so viele B‐Frames verwendet werden wie benötigt, bis zum Maximum,\nzu den Kosten einer erhöhten GPU‐Nutzung."
|
||||
NVENC.Preset.llhq="Niedrige Latenz und Qualität"
|
||||
NVENC.Preset.llhp="Niedrige Latenz und Leistung"
|
||||
NVENC.LookAhead="Lookahead"
|
||||
NVENC.LookAhead.ToolTip="Aktiviert dynamische B‐Frames.\n\nWenn deaktiviert, wird der Kodierer immer die Anzahl der B‐Frames verwenden, die in der Einstellung „Max. B‐Frames“ angegeben sind.\n\nWenn aktiviert, wird die visuelle Qualität erhöht, indem nur so viele B‐Frames bis zum Maximum verwendet werden, wie benötigt.\n(Resultiert in eine höhere GPU‐Nutzung.)"
|
||||
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 GPU‐Nutzung."
|
||||
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 GPU‐Nutzung.)"
|
||||
NVENC.CQLevel="CQ‐Level"
|
||||
|
||||
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="YUV‐Farbmatrix"
|
||||
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="YUV‐Farbbereich"
|
||||
ColorRange.Auto="Automatisch"
|
||||
ColorRange.Partial="Teilweise"
|
||||
ColorRange.Full="Voll"
|
||||
RestartMedia="Medium neustarten"
|
||||
RestartMedia="Neustarten"
|
||||
SpeedPercentage="Geschwindigkeit"
|
||||
Seekable="Durchsuchbar"
|
||||
Play="Abspielen"
|
||||
Pause="Pausieren"
|
||||
Stop="Stoppen"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="Alle Mediendateien"
|
||||
MediaFileFilter.VideoFiles="Videodateien"
|
||||
MediaFileFilter.AudioFiles="Audiodateien"
|
||||
MediaFileFilter.AllFiles="Alle Dateien"
|
||||
|
||||
ReplayBuffer="Replay‐Puffer"
|
||||
ReplayBuffer.Save="Replay speichern"
|
||||
ReplayBuffer="Wiederholungspuffer"
|
||||
ReplayBuffer.Save="Wiederholung speichern"
|
||||
|
||||
HelperProcessFailed="Der Aufnahme‐Helfer‐Prozess kann nicht gestartet werden. Überprüfen Sie, ob OBS‐Dateien nicht von einer Drittanbieter‐Antiviren‐/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 Windows‐10‐Ransomware‐Schutz aktiviert ist, kann dies auch den Fehler auslösen. Versuchen Sie, den überwachten Ordnerzugriff in Windows‐Sicherheit → 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 Windows‐10‐Ransomware‐Schutz aktiviert ist, kann dies auch den Fehler auslösen. Versuchen Sie, den überwachten Ordnerzugriff in „Windows‐Sicherheit“ → „Viren‐ & Bedrohungsschutz“ auszuschalten."
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ ColorRange="Χώρος Χρωμάτων YUV"
|
|||
ColorRange.Auto="Αυτόματο"
|
||||
ColorRange.Partial="Μερικός"
|
||||
ColorRange.Full="Πλήρης"
|
||||
RestartMedia="Επανεκκίνηση Πολυμέσων"
|
||||
Seekable="Παρεχόμενη"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="Όλα τα αρχεία πολυμέσων"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ InputFormat="فرمت های ورودی"
|
|||
ColorRange.Auto="خودکار"
|
||||
ColorRange.Partial="جزئی"
|
||||
ColorRange.Full="کامل"
|
||||
RestartMedia="راه اندازی مجدد رسانه ها"
|
||||
Seekable="جستجوگر"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="تمامی فایل های رسانه"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 lorsqu’il 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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@ ColorRange="YUV 色範囲"
|
|||
ColorRange.Auto="自動"
|
||||
ColorRange.Partial="一部"
|
||||
ColorRange.Full="全部"
|
||||
RestartMedia="メディアを再開する"
|
||||
RestartMedia="再開"
|
||||
SpeedPercentage="速度"
|
||||
Seekable="シーク可能"
|
||||
Play="再生"
|
||||
Pause="一時停止"
|
||||
Stop="停止"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="すべてのメディアファイル"
|
||||
MediaFileFilter.VideoFiles="ビデオファイル"
|
||||
|
|
|
|||
|
|
@ -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="ვიდეოფაილები"
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@ ColorRange="YUV 색상 범위"
|
|||
ColorRange.Auto="자동"
|
||||
ColorRange.Partial="부분"
|
||||
ColorRange.Full="전체"
|
||||
RestartMedia="미디어 다시재생"
|
||||
RestartMedia="재시작"
|
||||
SpeedPercentage="속도"
|
||||
Seekable="탐색 가능"
|
||||
Play="재생"
|
||||
Pause="일시정지"
|
||||
Stop="중단"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="모든 미디어 파일"
|
||||
MediaFileFilter.VideoFiles="비디오 파일"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@ ColorRange="Цветовой диапазон YUV"
|
|||
ColorRange.Auto="Автоматически"
|
||||
ColorRange.Partial="Частичный"
|
||||
ColorRange.Full="Полный"
|
||||
RestartMedia="Перезапустить медиа"
|
||||
RestartMedia="Перезапустить"
|
||||
SpeedPercentage="Скорость"
|
||||
Seekable="Перематываемый"
|
||||
Play="Запустить"
|
||||
Pause="Приостановить"
|
||||
Stop="Остановить"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="Все медиа-файлы"
|
||||
MediaFileFilter.VideoFiles="Видеофайлы"
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ ColorRange="YUV опсег боја"
|
|||
ColorRange.Auto="Аутоматски"
|
||||
ColorRange.Partial="Делимични"
|
||||
ColorRange.Full="Потпуни"
|
||||
RestartMedia="Рестартуј медиј"
|
||||
Seekable="Претраживање"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="Све медија датотеке"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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ı"
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@ ColorRange="YUV, колірний діапазон"
|
|||
ColorRange.Auto="Автовизначення"
|
||||
ColorRange.Partial="Частковий"
|
||||
ColorRange.Full="Повний"
|
||||
RestartMedia="Перезапустити медіа"
|
||||
RestartMedia="Перезавантажити"
|
||||
SpeedPercentage="Швидкість"
|
||||
Seekable="HTTP з перемотуванням"
|
||||
Play="Відтворити"
|
||||
Pause="Призупинити"
|
||||
Stop="Зупинити"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="Файли мультимедіа"
|
||||
MediaFileFilter.VideoFiles="Відео"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@ ColorRange="YUV 颜色范围"
|
|||
ColorRange.Auto="自动"
|
||||
ColorRange.Partial="局部"
|
||||
ColorRange.Full="全部"
|
||||
RestartMedia="重新启动媒体"
|
||||
RestartMedia="重新开始"
|
||||
SpeedPercentage="速度"
|
||||
Seekable="可搜索"
|
||||
Play="播放"
|
||||
Pause="暂停"
|
||||
Stop="停止"
|
||||
|
||||
MediaFileFilter.AllMediaFiles="所有媒体文件"
|
||||
MediaFileFilter.VideoFiles="视频文件"
|
||||
|
|
|
|||
|
|
@ -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 安全性 > [病毒與威脅防護] 設定中的 [受控資料夾存取權] 關閉。"
|
||||
|
||||
|
|
|
|||
873
plugins/obs-ffmpeg/ffmpeg-encoded-output.c
Normal file
873
plugins/obs-ffmpeg/ffmpeg-encoded-output.c
Normal 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};
|
||||
108
plugins/obs-ffmpeg/ffmpeg-encoded-output.h
Normal file
108
plugins/obs-ffmpeg/ffmpeg-encoded-output.h
Normal 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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
68
plugins/obs-ffmpeg/obs-ffmpeg-output.h
Normal file
68
plugins/obs-ffmpeg/obs-ffmpeg-output.h
Normal 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);
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue