diff --git a/source/video_transcoder/changelog.md b/source/video_transcoder/changelog.md
index 3a5461095..deca8a99c 100644
--- a/source/video_transcoder/changelog.md
+++ b/source/video_transcoder/changelog.md
@@ -1,4 +1,7 @@
+**0.1.2**
+- Fix for plugin updates from versions older than 0.1.0
+
**0.1.1**
- Add support for the av1_qsv encoder
diff --git a/source/video_transcoder/info.json b/source/video_transcoder/info.json
index 0749f6bdc..545c92558 100644
--- a/source/video_transcoder/info.json
+++ b/source/video_transcoder/info.json
@@ -12,5 +12,5 @@
"on_worker_process": 1
},
"tags": "video,ffmpeg",
- "version": "0.1.1"
+ "version": "0.1.2"
}
diff --git a/source/video_transcoder/lib/encoders/libx.py b/source/video_transcoder/lib/encoders/libx.py
index fad22f0ee..94d601e1a 100644
--- a/source/video_transcoder/lib/encoders/libx.py
+++ b/source/video_transcoder/lib/encoders/libx.py
@@ -24,22 +24,23 @@
class LibxEncoder:
- provides = {
- "libx264": {
- "codec": "h264",
- "label": "CPU - libx264",
- },
- "libx265": {
- "codec": "hevc",
- "label": "CPU - libx265",
- },
- }
def __init__(self, settings):
self.settings = settings
- @staticmethod
- def options():
+ def provides(self):
+ return {
+ "libx264": {
+ "codec": "h264",
+ "label": "CPU - libx264",
+ },
+ "libx265": {
+ "codec": "hevc",
+ "label": "CPU - libx265",
+ },
+ }
+
+ def options(self):
return {
"preset": "slow",
"tune": "disabled",
@@ -49,12 +50,10 @@ def options():
"average_bitrate": "5",
}
- @staticmethod
- def generate_default_args(settings):
+ def generate_default_args(self):
"""
Generate a list of args for using a libx decoder
- :param settings:
:return:
"""
# No default args required
@@ -62,8 +61,7 @@ def generate_default_args(settings):
advanced_kwargs = {}
return generic_kwargs, advanced_kwargs
- @staticmethod
- def generate_filtergraphs():
+ def generate_filtergraphs(self):
"""
Generate the required filter for this encoder
No filters are required for libx encoders
@@ -73,7 +71,8 @@ def generate_filtergraphs():
return []
def encoder_details(self, encoder):
- return self.provides.get(encoder, {})
+ provides = self.provides()
+ return provides.get(encoder, {})
def args(self, stream_id):
stream_encoding = []
diff --git a/source/video_transcoder/lib/encoders/nvenc.py b/source/video_transcoder/lib/encoders/nvenc.py
index e022817b3..b817e5c0e 100644
--- a/source/video_transcoder/lib/encoders/nvenc.py
+++ b/source/video_transcoder/lib/encoders/nvenc.py
@@ -89,22 +89,23 @@ def get_configured_device(settings):
class NvencEncoder:
- provides = {
- "h264_nvenc": {
- "codec": "h264",
- "label": "NVENC - h264_nvenc",
- },
- "hevc_nvenc": {
- "codec": "hevc",
- "label": "NVENC - hevc_nvenc",
- },
- }
def __init__(self, settings):
self.settings = settings
- @staticmethod
- def options():
+ def provides(self):
+ return {
+ "h264_nvenc": {
+ "codec": "h264",
+ "label": "NVENC - h264_nvenc",
+ },
+ "hevc_nvenc": {
+ "codec": "hevc",
+ "label": "NVENC - hevc_nvenc",
+ },
+ }
+
+ def options(self):
return {
"nvenc_device": "none",
"nvenc_decoding_method": "cpu",
@@ -118,34 +119,31 @@ def options():
"nvenc_aq_strength": 8,
}
- @staticmethod
- def generate_default_args(settings):
+ def generate_default_args(self):
"""
Generate a list of args for using a NVENC decoder
REF: https://trac.ffmpeg.org/wiki/HWAccelIntro#NVDECCUVID
- :param settings:
:return:
"""
- hardware_device = get_configured_device(settings)
+ hardware_device = get_configured_device(self.settings)
generic_kwargs = {}
advanced_kwargs = {}
# Check if we are using a HW accelerated decoder also
- if settings.get_setting('nvenc_decoding_method') in ['cuda', 'nvdec', 'cuvid']:
+ if self.settings.get_setting('nvenc_decoding_method') in ['cuda', 'nvdec', 'cuvid']:
generic_kwargs = {
"-hwaccel_device": hardware_device.get('hwaccel_device'),
- "-hwaccel": settings.get_setting('nvenc_decoding_method'),
+ "-hwaccel": self.settings.get_setting('nvenc_decoding_method'),
"-init_hw_device": "cuda=hw",
"-filter_hw_device": "hw",
}
- if settings.get_setting('nvenc_decoding_method') in ['cuda', 'nvdec']:
+ if self.settings.get_setting('nvenc_decoding_method') in ['cuda', 'nvdec']:
generic_kwargs["-hwaccel_output_format"] = "cuda"
return generic_kwargs, advanced_kwargs
- @staticmethod
- def generate_filtergraphs(software_filters, hw_smart_filters):
+ def generate_filtergraphs(self, software_filters, hw_smart_filters):
"""
Generate the required filter for enabling NVENC HW acceleration
@@ -166,7 +164,8 @@ def encoder_details(self, encoder):
if not hardware_devices:
# Return no options. No hardware device was found
return {}
- return self.provides.get(encoder, {})
+ provides = self.provides()
+ return provides.get(encoder, {})
def args(self, stream_info, stream_id):
generic_kwargs = {}
diff --git a/source/video_transcoder/lib/encoders/qsv.py b/source/video_transcoder/lib/encoders/qsv.py
index 9c00ce259..eda247f67 100644
--- a/source/video_transcoder/lib/encoders/qsv.py
+++ b/source/video_transcoder/lib/encoders/qsv.py
@@ -33,26 +33,27 @@
class QsvEncoder:
- provides = {
- "h264_qsv": {
- "codec": "h264",
- "label": "QSV - h264_qsv",
- },
- "hevc_qsv": {
- "codec": "hevc",
- "label": "QSV - hevc_qsv",
- },
- "av1_qsv": {
- "codec": "av1",
- "label": "QSV - av1_qsv",
- },
- }
def __init__(self, settings):
self.settings = settings
- @staticmethod
- def options():
+ def provides(self):
+ return {
+ "h264_qsv": {
+ "codec": "h264",
+ "label": "QSV - h264_qsv",
+ },
+ "hevc_qsv": {
+ "codec": "hevc",
+ "label": "QSV - hevc_qsv",
+ },
+ "av1_qsv": {
+ "codec": "av1",
+ "label": "QSV - av1_qsv",
+ },
+ }
+
+ def options(self):
return {
"qsv_decoding_method": "cpu",
"qsv_preset": "slow",
@@ -63,8 +64,7 @@ def options():
"qsv_average_bitrate": "5",
}
- @staticmethod
- def generate_default_args(settings):
+ def generate_default_args(self):
"""
Generate a list of args for using a QSV decoder
@@ -79,7 +79,7 @@ def generate_default_args(settings):
}
advanced_kwargs = {}
# Check if we are using a HW accelerated decoder> Modify args as required
- if settings.get_setting('qsv_decoding_method') in ['qsv']:
+ if self.settings.get_setting('qsv_decoding_method') in ['qsv']:
generic_kwargs = {
"-hwaccel": "qsv",
"-hwaccel_output_format": "qsv",
@@ -88,8 +88,7 @@ def generate_default_args(settings):
}
return generic_kwargs, advanced_kwargs
- @staticmethod
- def generate_filtergraphs(settings, software_filters, hw_smart_filters):
+ def generate_filtergraphs(self, settings, software_filters, hw_smart_filters):
"""
Generate the required filter for enabling QSV HW acceleration
@@ -117,7 +116,8 @@ def generate_filtergraphs(settings, software_filters, hw_smart_filters):
return generic_kwargs, advanced_kwargs, filter_args
def encoder_details(self, encoder):
- return self.provides.get(encoder, {})
+ provides = self.provides()
+ return provides.get(encoder, {})
def args(self, stream_id):
stream_encoding = []
diff --git a/source/video_transcoder/lib/encoders/vaapi.py b/source/video_transcoder/lib/encoders/vaapi.py
index 78f721980..12618dbf8 100644
--- a/source/video_transcoder/lib/encoders/vaapi.py
+++ b/source/video_transcoder/lib/encoders/vaapi.py
@@ -47,22 +47,23 @@ def list_available_vaapi_devices():
class VaapiEncoder:
- provides = {
- "h264_vaapi": {
- "codec": "h264",
- "label": "VAAPI - h264_vaapi",
- },
- "hevc_vaapi": {
- "codec": "hevc",
- "label": "VAAPI - hevc_vaapi",
- },
- }
def __init__(self, settings):
self.settings = settings
- @staticmethod
- def options():
+ def provides(self):
+ return {
+ "h264_vaapi": {
+ "codec": "h264",
+ "label": "VAAPI - h264_vaapi",
+ },
+ "hevc_vaapi": {
+ "codec": "hevc",
+ "label": "VAAPI - hevc_vaapi",
+ },
+ }
+
+ def options(self):
return {
"vaapi_device": "none",
"vaapi_enabled_hw_decoding": True,
@@ -72,12 +73,10 @@ def options():
"vaapi_average_bitrate": "5",
}
- @staticmethod
- def generate_default_args(settings):
+ def generate_default_args(self):
"""
Generate a list of args for using a VAAPI decoder
- :param settings:
:return:
"""
# Set the hardware device
@@ -88,10 +87,10 @@ def generate_default_args(settings):
hardware_device = None
# If we have configured a hardware device
- if settings.get_setting('vaapi_device') not in ['none']:
+ if self.settings.get_setting('vaapi_device') not in ['none']:
# Attempt to match to that configured hardware device
for hw_device in hardware_devices:
- if settings.get_setting('vaapi_device') == hw_device.get('hwaccel_device'):
+ if self.settings.get_setting('vaapi_device') == hw_device.get('hwaccel_device'):
hardware_device = hw_device
break
# If no matching hardware device is set, then select the first one
@@ -99,7 +98,7 @@ def generate_default_args(settings):
hardware_device = hardware_devices[0]
# Check if we are using a VAAPI decoder also...
- if settings.get_setting('vaapi_enabled_hw_decoding'):
+ if self.settings.get_setting('vaapi_enabled_hw_decoding'):
# Set a named global device that can be used with various params
dev_id = 'vaapi0'
# Configure args such that when the input may or may not be able to be decoded with hardware we can do:
@@ -123,8 +122,7 @@ def generate_default_args(settings):
return generic_kwargs, advanced_kwargs
- @staticmethod
- def generate_filtergraphs():
+ def generate_filtergraphs(self):
"""
Generate the required filter for enabling VAAPI HW acceleration
@@ -133,7 +131,8 @@ def generate_filtergraphs():
return ["format=nv12|vaapi,hwupload"]
def encoder_details(self, encoder):
- return self.provides.get(encoder, {})
+ provides = self.provides()
+ return provides.get(encoder, {})
def args(self, stream_id):
stream_encoding = []
diff --git a/source/video_transcoder/lib/global_settings.py b/source/video_transcoder/lib/global_settings.py
index f57825d59..a68b25825 100644
--- a/source/video_transcoder/lib/global_settings.py
+++ b/source/video_transcoder/lib/global_settings.py
@@ -97,19 +97,6 @@ def __set_default_option(self, select_options, key, default_option=None):
if self.settings.get_setting(key) not in available_options:
self.settings.set_setting(key, default_option)
- @staticmethod
- def __is_nvidia_gpu_present():
- try:
- # Run the nvidia-smi command
- subprocess.run("nvidia-smi", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
- return True
- except FileNotFoundError:
- # nvidia-smi executable not found
- return False
- except subprocess.CalledProcessError:
- # nvidia-smi command failed, likely no NVIDIA GPU present
- return False
-
def get_mode_form_settings(self):
return {
"label": "Config mode",
diff --git a/source/video_transcoder/lib/plugin_stream_mapper.py b/source/video_transcoder/lib/plugin_stream_mapper.py
index 97481fa9b..4f0feede6 100644
--- a/source/video_transcoder/lib/plugin_stream_mapper.py
+++ b/source/video_transcoder/lib/plugin_stream_mapper.py
@@ -92,23 +92,12 @@ def set_default_values(self, settings, abspath, probe):
# Build hardware acceleration args based on encoder
# Note: these are not applied to advanced mode - advanced mode was returned above
- if self.settings.get_setting('video_encoder') in LibxEncoder.provides:
- generic_kwargs, advanced_kwargs = LibxEncoder.generate_default_args(self.settings)
- self.set_ffmpeg_generic_options(**generic_kwargs)
- self.set_ffmpeg_advanced_options(**advanced_kwargs)
- elif self.settings.get_setting('video_encoder') in QsvEncoder.provides:
- generic_kwargs, advanced_kwargs = QsvEncoder.generate_default_args(self.settings)
- self.set_ffmpeg_generic_options(**generic_kwargs)
- self.set_ffmpeg_advanced_options(**advanced_kwargs)
- elif self.settings.get_setting('video_encoder') in VaapiEncoder.provides:
- generic_kwargs, advanced_kwargs = VaapiEncoder.generate_default_args(self.settings)
- self.set_ffmpeg_generic_options(**generic_kwargs)
- self.set_ffmpeg_advanced_options(**advanced_kwargs)
- elif self.settings.get_setting('video_encoder') in NvencEncoder.provides:
- generic_kwargs, advanced_kwargs = NvencEncoder.generate_default_args(self.settings)
- self.set_ffmpeg_generic_options(**generic_kwargs)
- self.set_ffmpeg_advanced_options(**advanced_kwargs)
- # TODO: Disable any options not compatible with this encoder
+ for encoder_name in self.settings.encoders:
+ encoder_lib = self.settings.encoders.get(encoder_name)
+ if self.settings.get_setting('video_encoder') in encoder_lib.provides():
+ generic_kwargs, advanced_kwargs = encoder_lib.generate_default_args()
+ self.set_ffmpeg_generic_options(**generic_kwargs)
+ self.set_ffmpeg_advanced_options(**advanced_kwargs)
def scale_resolution(self, stream_info: dict):
def get_test_resolution(settings):
@@ -157,6 +146,12 @@ def build_filter_chain(self, stream_info, stream_id):
software_filters = []
hardware_filters = []
+ # Load encoder classes
+ libx_encoder = LibxEncoder(self.settings)
+ qsv_encoder = QsvEncoder(self.settings)
+ vaapi_encoder = VaapiEncoder(self.settings)
+ nvenc_encoder = NvencEncoder(self.settings)
+
# Apply smart filters first
required_hw_smart_filters = []
if self.settings.get_setting('apply_smart_filters'):
@@ -166,9 +161,9 @@ def build_filter_chain(self, stream_info, stream_id):
vid_width, vid_height = self.scale_resolution(stream_info)
if vid_width:
# Apply scale with only width to keep aspect ratio
- if self.settings.get_setting('video_encoder') in QsvEncoder.provides:
+ if self.settings.get_setting('video_encoder') in qsv_encoder.provides():
required_hw_smart_filters.append({'scale': [vid_width, vid_height]})
- elif self.settings.get_setting('video_encoder') in NvencEncoder.provides:
+ elif self.settings.get_setting('video_encoder') in nvenc_encoder.provides():
required_hw_smart_filters.append({'scale': [vid_width, vid_height]})
else:
software_filters.append('scale={}:-1'.format(vid_width))
@@ -180,23 +175,23 @@ def build_filter_chain(self, stream_info, stream_id):
software_filters.append(software_filter.strip())
# Check for hardware encoders that required video filters
- if self.settings.get_setting('video_encoder') in QsvEncoder.provides:
+ if self.settings.get_setting('video_encoder') in qsv_encoder.provides():
# Add filtergraph required for using QSV encoding
- generic_kwargs, advanced_kwargs, filter_args = QsvEncoder.generate_filtergraphs(self.settings, software_filters,
- required_hw_smart_filters)
+ generic_kwargs, advanced_kwargs, filter_args = qsv_encoder.generate_filtergraphs(self.settings, software_filters,
+ required_hw_smart_filters)
self.set_ffmpeg_generic_options(**generic_kwargs)
self.set_ffmpeg_advanced_options(**advanced_kwargs)
hardware_filters += filter_args
- elif self.settings.get_setting('video_encoder') in VaapiEncoder.provides:
+ elif self.settings.get_setting('video_encoder') in vaapi_encoder.provides():
# Add filtergraph required for using VAAPI encoding
- hardware_filters += VaapiEncoder.generate_filtergraphs()
+ hardware_filters += vaapi_encoder.generate_filtergraphs()
# If we are using software filters, then disable vaapi surfaces.
# Instead, output software frames
if software_filters:
self.set_ffmpeg_generic_options(**{'-hwaccel_output_format': 'nv12'})
- elif self.settings.get_setting('video_encoder') in NvencEncoder.provides:
+ elif self.settings.get_setting('video_encoder') in nvenc_encoder.provides():
# Add filtergraph required for using CUDA encoding
- hardware_filters += NvencEncoder.generate_filtergraphs(software_filters, required_hw_smart_filters)
+ hardware_filters += nvenc_encoder.generate_filtergraphs(software_filters, required_hw_smart_filters)
# If we are using software filters, then disable cuda surfaces.
# Instead, output software frames
if software_filters:
@@ -319,18 +314,20 @@ def custom_stream_mapping(self, stream_info: dict, stream_id: int):
'-c:{}'.format(stream_specifier), self.settings.get_setting('video_encoder'),
]
+ # Load encoder classes
+ libx_encoder = LibxEncoder(self.settings)
+ qsv_encoder = QsvEncoder(self.settings)
+ vaapi_encoder = VaapiEncoder(self.settings)
+ nvenc_encoder = NvencEncoder(self.settings)
+
# Add encoder args
- if self.settings.get_setting('video_encoder') in LibxEncoder.provides:
- qsv_encoder = LibxEncoder(self.settings)
- stream_encoding += qsv_encoder.args(stream_id)
- elif self.settings.get_setting('video_encoder') in QsvEncoder.provides:
- qsv_encoder = QsvEncoder(self.settings)
+ if self.settings.get_setting('video_encoder') in libx_encoder.provides():
+ stream_encoding += libx_encoder.args(stream_id)
+ elif self.settings.get_setting('video_encoder') in qsv_encoder.provides():
stream_encoding += qsv_encoder.args(stream_id)
- elif self.settings.get_setting('video_encoder') in VaapiEncoder.provides:
- vaapi_encoder = VaapiEncoder(self.settings)
+ elif self.settings.get_setting('video_encoder') in vaapi_encoder.provides():
stream_encoding += vaapi_encoder.args(stream_id)
- elif self.settings.get_setting('video_encoder') in NvencEncoder.provides:
- nvenc_encoder = NvencEncoder(self.settings)
+ elif self.settings.get_setting('video_encoder') in nvenc_encoder.provides():
generic_kwargs, stream_encoding_args = nvenc_encoder.args(stream_info, stream_id)
self.set_ffmpeg_generic_options(**generic_kwargs)
stream_encoding += stream_encoding_args
diff --git a/source/video_transcoder/plugin.py b/source/video_transcoder/plugin.py
index 32f1a9ec1..77b45441e 100644
--- a/source/video_transcoder/plugin.py
+++ b/source/video_transcoder/plugin.py
@@ -96,9 +96,10 @@ def __available_encoders(self):
VaapiEncoder,
NvencEncoder,
]
- for encoder_lib in encoder_libs:
- for encoder in encoder_lib.provides:
- return_encoders[encoder] = encoder_lib(self)
+ for encoder_class in encoder_libs:
+ encoder_lib = encoder_class(self)
+ for encoder in encoder_lib.provides():
+ return_encoders[encoder] = encoder_lib
return return_encoders
def __encoder_settings_object(self):
@@ -110,10 +111,10 @@ def __encoder_settings_object(self):
# Initial options forces the order they appear in the settings list
# We need this because some encoders have settings that
# Fetch all encoder settings from encoder libs
- libx_options = LibxEncoder.options()
- qsv_options = QsvEncoder.options()
- vaapi_options = VaapiEncoder.options()
- nvenc_options = NvencEncoder.options()
+ libx_options = LibxEncoder(self.settings).options()
+ qsv_options = QsvEncoder(self.settings).options()
+ vaapi_options = VaapiEncoder(self.settings).options()
+ nvenc_options = NvencEncoder(self.settings).options()
return {
**libx_options,
**qsv_options,