Skip to content

Commit

Permalink
Consolidate all information about what encoders and codecs are availa…
Browse files Browse the repository at this point in the history
…ble in the encoder libs

This means to add a new encoder, we only need to add the details in the required lib file which makes it more manageable.
  • Loading branch information
Josh5 committed Dec 29, 2023
1 parent 7ab62fd commit be3dded
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 105 deletions.
17 changes: 13 additions & 4 deletions lib/encoders/libx.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@


class LibxEncoder:
encoders = [
"libx264",
"libx265",
]
provides = {
"libx264": {
"codec": "h264",
"label": "CPU - libx264",
},
"libx265": {
"codec": "hevc",
"label": "CPU - libx265",
}
}

def __init__(self, settings):
self.settings = settings
Expand Down Expand Up @@ -66,6 +72,9 @@ def generate_filtergraphs():
"""
return []

def encoder_details(self, encoder):
return self.provides.get(encoder, {})

def args(self, stream_id):
stream_encoding = []

Expand Down
21 changes: 17 additions & 4 deletions lib/encoders/nvenc.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,16 @@ def get_configured_device(settings):


class NvencEncoder:
encoders = [
"h264_nvenc",
"hevc_nvenc",
]
provides = {
"h264_nvenc": {
"codec": "h264",
"label": "NVENC - h264_nvenc",
},
"hevc_nvenc": {
"codec": "hevc",
"label": "NVENC - hevc_nvenc",
}
}

def __init__(self, settings):
self.settings = settings
Expand Down Expand Up @@ -155,6 +161,13 @@ def generate_filtergraphs(software_filters, hw_smart_filters):
filters.append('scale_cuda={}:-1'.format(scale_values[0]))
return filters

def encoder_details(self, encoder):
hardware_devices = list_available_cuda_devices()
if not hardware_devices:
# Return no options. No hardware device was found
return {}
return self.provides.get(encoder, {})

def args(self, stream_info, stream_id):
generic_kwargs = {}
stream_encoding = []
Expand Down
17 changes: 13 additions & 4 deletions lib/encoders/qsv.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@


class QsvEncoder:
encoders = [
"h264_qsv",
"hevc_qsv",
]
provides = {
"h264_qsv": {
"codec": "h264",
"label": "QSV - h264_qsv",
},
"hevc_qsv": {
"codec": "hevc",
"label": "QSV - hevc_qsv",
}
}

def __init__(self, settings):
self.settings = settings
Expand Down Expand Up @@ -106,6 +112,9 @@ def generate_filtergraphs(settings, software_filters, hw_smart_filters):
filter_args.append('scale_qsv=w={}:h={}'.format(scale_values[0], scale_values[1]))
return generic_kwargs, advanced_kwargs, filter_args

def encoder_details(self, encoder):
return self.provides.get(encoder, {})

def args(self, stream_id):
stream_encoding = []

Expand Down
17 changes: 13 additions & 4 deletions lib/encoders/vaapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ def list_available_vaapi_devices():


class VaapiEncoder:
encoders = [
"h264_vaapi",
"hevc_vaapi",
]
provides = {
"h264_vaapi": {
"codec": "h264",
"label": "VAAPI - h264_vaapi",
},
"hevc_vaapi": {
"codec": "hevc",
"label": "VAAPI - hevc_vaapi",
}
}

def __init__(self, settings):
self.settings = settings
Expand Down Expand Up @@ -126,6 +132,9 @@ def generate_filtergraphs():
"""
return ["format=nv12|vaapi,hwupload"]

def encoder_details(self, encoder):
return self.provides.get(encoder, {})

def args(self, stream_id):
stream_encoding = []

Expand Down
95 changes: 28 additions & 67 deletions lib/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@
If not, see <https://www.gnu.org/licenses/>.
"""
import os
import subprocess
import sys

from video_transcoder.lib import tools

supported_codecs = {
"h264": {
"label": "H264"
},
"hevc": {
"label": "HEVC/H265"
},
}


class GlobalSettings:

Expand Down Expand Up @@ -143,17 +150,15 @@ def get_video_codec_form_settings(self):
"label": "Video Codec",
"description": "Select the video codec that your video library should be.",
"input_type": "select",
"select_options": [
{
"value": "h264",
"label": "H264",
},
{
"value": "hevc",
"label": "HEVC/H265",
},
],
"select_options": [],
}
for key in supported_codecs:
values['select_options'].append(
{
"value": key,
"label": supported_codecs.get(key, {}).get('label'),
}
)
self.__set_default_option(values['select_options'], 'video_codec')
if self.settings.get_setting('mode') not in ['basic', 'standard', 'advanced']:
values["display"] = 'hidden'
Expand All @@ -177,63 +182,19 @@ def get_video_encoder_form_settings(self):
values = {
"label": "Video Encoder",
"input_type": "select",
"select_options": [
{
"value": "libx264",
"label": "CPU - libx264",
},
],
"select_options": [],
}
if self.settings.get_setting('video_codec') == 'h264':
values['select_options'] = [
{
"value": "libx264",
"label": "CPU - libx264",
},
for encoder_name in self.settings.encoders:
encoder_lib = self.settings.encoders.get(encoder_name)
encoder_details = encoder_lib.encoder_details(encoder_name)
if encoder_details.get('codec') != self.settings.get_setting('video_codec'):
continue
values['select_options'].append(
{
"value": "h264_qsv",
"label": "QSV - h264_qsv",
},
]
if os.name == 'posix' and sys.platform == 'linux':
values['select_options'] += [
{
"value": "h264_vaapi",
"label": "VAAPI - h264_vaapi",
},
]
if self.__is_nvidia_gpu_present():
values['select_options'] += [
{
"value": "h264_nvenc",
"label": "NVENC - h264_nvenc",
},
]
elif self.settings.get_setting('video_codec') == 'hevc':
values['select_options'] = [
{
"value": "libx265",
"label": "CPU - libx265",
},
{
"value": "hevc_qsv",
"label": "QSV - hevc_qsv",
},
]
if os.name == 'posix' and sys.platform == 'linux':
values['select_options'] += [
{
"value": "hevc_vaapi",
"label": "VAAPI - hevc_vaapi",
},
]
if self.__is_nvidia_gpu_present():
values['select_options'] += [
{
"value": "hevc_nvenc",
"label": "NVENC - hevc_nvenc",
},
]
"value": encoder_name,
"label": encoder_details.get('label'),
}
)
self.__set_default_option(values['select_options'], 'video_encoder')
if self.settings.get_setting('mode') not in ['basic', 'standard']:
values["display"] = 'hidden'
Expand Down
26 changes: 13 additions & 13 deletions lib/plugin_stream_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,19 @@ 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.encoders:
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.encoders:
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.encoders:
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.encoders:
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)
Expand Down Expand Up @@ -166,9 +166,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.encoders:
if self.settings.get_setting('video_encoder') in QsvEncoder.provides:
required_hw_smart_filters.append({'scale': [vid_width, vid_height]})
elif self.settings.get_setting('video_encoder') in NvencEncoder.encoders:
elif self.settings.get_setting('video_encoder') in NvencEncoder.provides:
required_hw_smart_filters.append({'scale': [vid_width, vid_height]})
else:
software_filters.append('scale={}:-1'.format(vid_width))
Expand All @@ -180,21 +180,21 @@ 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.encoders:
if self.settings.get_setting('video_encoder') in QsvEncoder.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)
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.encoders:
elif self.settings.get_setting('video_encoder') in VaapiEncoder.provides:
# Add filtergraph required for using VAAPI encoding
hardware_filters += VaapiEncoder.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.encoders:
elif self.settings.get_setting('video_encoder') in NvencEncoder.provides:
# Add filtergraph required for using CUDA encoding
hardware_filters += NvencEncoder.generate_filtergraphs(software_filters, required_hw_smart_filters)
# If we are using software filters, then disable cuda surfaces.
Expand Down Expand Up @@ -320,16 +320,16 @@ def custom_stream_mapping(self, stream_info: dict, stream_id: int):
]

# Add encoder args
if self.settings.get_setting('video_encoder') in LibxEncoder.encoders:
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.encoders:
elif self.settings.get_setting('video_encoder') in QsvEncoder.provides:
qsv_encoder = QsvEncoder(self.settings)
stream_encoding += qsv_encoder.args(stream_id)
elif self.settings.get_setting('video_encoder') in VaapiEncoder.encoders:
elif self.settings.get_setting('video_encoder') in VaapiEncoder.provides:
vaapi_encoder = VaapiEncoder(self.settings)
stream_encoding += vaapi_encoder.args(stream_id)
elif self.settings.get_setting('video_encoder') in NvencEncoder.encoders:
elif self.settings.get_setting('video_encoder') in NvencEncoder.provides:
nvenc_encoder = NvencEncoder(self.settings)
generic_kwargs, stream_encoding_args = nvenc_encoder.args(stream_info, stream_id)
self.set_ffmpeg_generic_options(**generic_kwargs)
Expand Down
23 changes: 14 additions & 9 deletions plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,7 @@ class Settings(PluginSettings):
def __init__(self, *args, **kwargs):
super(Settings, self).__init__(*args, **kwargs)
self.settings = self.__build_settings_object()
self.encoders = {
"libx265": LibxEncoder(self),
"libx264": LibxEncoder(self),
"hevc_qsv": QsvEncoder(self),
"h264_qsv": QsvEncoder(self),
"hevc_vaapi": VaapiEncoder(self),
"h264_vaapi": VaapiEncoder(self),
"h264_nvenc": NvencEncoder(self),
}
self.encoders = self.__available_encoders()
self.global_settings = GlobalSettings(self)
self.form_settings = self.__build_form_settings_object()

Expand Down Expand Up @@ -96,6 +88,19 @@ def __build_form_settings_object(self):
return_values[setting] = setting_form_settings
return return_values

def __available_encoders(self):
return_encoders = {}
encoder_libs = [
LibxEncoder,
QsvEncoder,
VaapiEncoder,
NvencEncoder,
]
for encoder_lib in encoder_libs:
for encoder in encoder_lib.provides:
return_encoders[encoder] = encoder_lib(self)
return return_encoders

def __encoder_settings_object(self):
"""
Returns a list of encoder settings for FFmpeg
Expand Down

0 comments on commit be3dded

Please sign in to comment.