提交 4dc38494 编写于 作者: J Jonathan Thomas

Adding new images for hw-accel dropdowns (preferences and export), integrating...

Adding new images for hw-accel dropdowns (preferences and export), integrating these images into the dialogs, and removing any videocodec from the Export dialog which is not supported by libopenshot.
上级 cc813ba1
......@@ -26,6 +26,7 @@ video editing and animation solutions to the world.
* Time-mapping and speed changes on clips (slow/fast, forward/backward, etc...)
* Audio mixing and editing
* Digital video effects, including brightness, gamma, hue, greyscale, chroma key, and many more!
* Experimental hardware encoding and decoding (VA-API, NVDEC, D3D9, D3D11, VTB)
## Getting Started
......
Observations for developers wanting to make hardware acceleration work:
All observations are for Linux.
HW accel is supported from ffmpeg version 3.2 (3.3 for nVidia drivers)
HW accel was removed for nVidia drivers in Ubuntu for ffmpeg 4.0 and up
I could not manage to built a version of ffmpeg 4.1 with the nVidia SDK
that worked with nVidia cards. There might be a problem in ffmpeg 4.0
and up that prohibits this.
Notice: The ffmpeg versions of Ubuntu and PPAs for Ubuntu schow the
same behaviour. ffmpeg 3 has working nVidia hardware acceleration while
ffmpeg 4.0 and up has no support for nVidia hardware acceleration
included.
The correct version of libva is needed (libva in Ubuntu 16.04 or libva2
in Ubuntu 18.04) for the AppImage to work with hardware acceleration.
A AppImage that works on both systems, libva and libva2, might be possible
when no libva is included in the AppImage.
vaapi is working for intel, AMD
vaapi is working for import only for nouveau (nouveau only has decode)
nVidia driver is working for export only.
If the computer has multiple grafic cards installed one can be used for
input and one for output. It is also possible to use one for input and
output, or use only one for input or output.
Decoding and encoding on the (AMD) GPU can be done on systems where ROCm
is installed and run. Possible use for GPU acceleration of effects, ...
This information can be wrong. If you find something new or an error
please communicate the finding.
---------------------------------------------------------------------------
DESPERATELY NEEDED: a way to compile ffmpeg 4.0 and up with working nVidia
hardware acceleration support on Ubuntu Linux!!!!!!!!!!!!!!!!!!!!!!!!!
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
width="66.696808"
height="114.07365"
sodipodi:docname="hw-accel.svg"
inkscape:export-filename="/home/jonathan/apps/openshot-qt-git/src/images/hw-accel-vaapi.png"
inkscape:export-xdpi="89.956078"
inkscape:export-ydpi="89.956078">
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs6">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective3600"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3628"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3656"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1855"
inkscape:window-height="1056"
id="namedview4"
showgrid="false"
inkscape:zoom="4.5101946"
inkscape:cx="73.147758"
inkscape:cy="-46.90567"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="g3339-367"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="g3339"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<rect
ry="7.9525819"
rx="7.9525819"
y="0.3305341"
x="0.3305341"
height="19.347975"
width="66.035736"
id="rect3345"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#00b310;stroke-width:0.6610682px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4147"
y="14.826028"
x="12.760928"
style="font-style:normal;font-weight:normal;font-size:13.2276535px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#00b310;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="14.826028"
x="12.760928"
id="tspan4149"
sodipodi:role="line">VA-API</tspan></text>
</g>
<g
id="g3339-3"
transform="translate(0,23.516151)"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<rect
ry="7.9525819"
rx="7.9525819"
y="0.3305341"
x="0.3305341"
height="19.347975"
width="66.035736"
id="rect3345-6"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#00b310;stroke-width:0.6610682px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4147-7"
y="14.826028"
x="9.6962223"
style="font-style:normal;font-weight:normal;font-size:13.2276535px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#00b310;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="14.826028"
x="9.6962223"
id="tspan4149-5"
sodipodi:role="line">NVDEC</tspan></text>
</g>
<g
id="g3339-35"
transform="translate(0,47.032303)"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<rect
ry="7.9525819"
rx="7.9525819"
y="0.3305341"
x="0.3305341"
height="19.347975"
width="66.035736"
id="rect3345-62"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#00b310;stroke-width:0.6610682px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<text
sodipodi:linespacing="125%"
id="text4147-9"
y="14.819569"
x="3.8832874"
style="font-style:normal;font-weight:normal;font-size:13.2276535px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#00b310;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><tspan
y="14.819569"
x="3.8832874"
id="tspan4149-1"
sodipodi:role="line">DIRECTX</tspan></text>
</g>
<g
id="g3339-36"
transform="translate(0,70.548454)"
inkscape:export-xdpi="89.956078"
inkscape:export-ydpi="89.956078">
<rect
ry="7.9525819"
rx="7.9525819"
y="0.3305341"
x="0.3305341"
height="19.347975"
width="66.035736"
id="rect3345-0"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#00b310;stroke-width:0.6610682px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4147-6"
y="14.826028"
x="20.66329"
style="font-style:normal;font-weight:normal;font-size:13.2276535px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#00b310;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="14.826028"
x="20.66329"
id="tspan4149-2"
sodipodi:role="line">VTB</tspan></text>
</g>
<g
id="g3339-367"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
transform="translate(0,94.06461)">
<rect
ry="7.9525819"
rx="7.9525819"
y="0.3305341"
x="0.3305341"
height="19.347975"
width="66.035736"
id="rect3345-5"
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:0.6610682px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<text
sodipodi:linespacing="125%"
id="text4147-3"
y="13.914113"
x="3.6082401"
style="font-style:normal;font-weight:normal;font-size:10.74023151px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#cfcfcf;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
xml:space="preserve"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><tspan
y="13.914113"
x="3.6082401"
id="tspan4149-56"
sodipodi:role="line">SOFTWARE</tspan></text>
</g>
</svg>
......@@ -24,21 +24,20 @@
You should have received a copy of the GNU General Public License
along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
"""
import time
import os
import functools
import locale
import os
import time
import xml.dom.minidom as xml
import functools
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import openshot # Python module for libopenshot (required video editing module installed separately)
from PyQt5.QtGui import QIcon
from classes import info, ui_util, settings
from classes import ui_util
from classes.app import get_app
from classes.query import File
from classes.logger import log
from classes.metrics import *
from classes.query import File
try:
import json
......@@ -77,9 +76,13 @@ class Export(QDialog):
self.settings_data = settings.get_settings().get_all_settings()
# Add buttons to interface
self.cancel_button = QPushButton(_('Cancel'))
self.export_button = QPushButton(_('Export Video'))
self.close_button = QPushButton(_('Done'))
self.buttonBox.addButton(self.close_button, QDialogButtonBox.RejectRole)
self.buttonBox.addButton(self.export_button, QDialogButtonBox.AcceptRole)
self.buttonBox.addButton(QPushButton(_('Cancel')), QDialogButtonBox.RejectRole)
self.buttonBox.addButton(self.cancel_button, QDialogButtonBox.RejectRole)
self.close_button.setVisible(False)
self.exporting = False
# Update FPS / Profile timer
......@@ -293,7 +296,10 @@ class Export(QDialog):
def updateProgressBar(self, path, start_frame, end_frame, current_frame):
"""Update progress bar during exporting"""
percentage_string = "%4.1f%% " % (( current_frame - start_frame ) / ( end_frame - start_frame ) * 100)
if end_frame - start_frame > 0:
percentage_string = "%4.1f%% " % (( current_frame - start_frame ) / ( end_frame - start_frame ) * 100)
else:
percentage_string = "100%"
self.progressExportVideo.setValue(current_frame)
self.progressExportVideo.setFormat(percentage_string)
self.setWindowTitle("%s %s" % (percentage_string, path))
......@@ -349,11 +355,6 @@ class Export(QDialog):
self.txtStartFrame.setValue(1)
self.txtEndFrame.setValue(self.timeline_length_int)
# Init progress bar
self.progressExportVideo.setMinimum(self.txtStartFrame.value())
self.progressExportVideo.setMaximum(self.txtEndFrame.value())
self.progressExportVideo.setValue(self.txtStartFrame.value())
# Calculate differences between editing/preview FPS and export FPS
current_fps = get_app().project.get(["fps"])
current_fps_float = float(current_fps["num"]) / float(current_fps["den"])
......@@ -374,6 +375,7 @@ class Export(QDialog):
# parse the xml files and get targets that match the project type
project_types = []
acceleration_types = {}
for preset_path in [info.EXPORT_PRESETS_PATH, info.USER_PRESETS_PATH]:
for file in os.listdir(preset_path):
xmldoc = xml.parse(os.path.join(preset_path, file))
......@@ -381,14 +383,32 @@ class Export(QDialog):
if _(type[0].childNodes[0].data) == selected_project:
titles = xmldoc.getElementsByTagName("title")
videocodecs = xmldoc.getElementsByTagName("videocodec")
for title in titles:
project_types.append(_(title.childNodes[0].data))
for codec in videocodecs:
codec_text = codec.childNodes[0].data
if codec_text == "h264_vaapi" and openshot.FFmpegWriter.IsValidCodec(codec_text):
acceleration_types[_(title.childNodes[0].data)] = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-vaapi.png"))
elif codec_text == "h264_nvenc" and openshot.FFmpegWriter.IsValidCodec(codec_text):
acceleration_types[_(title.childNodes[0].data)] = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-nvdec.png"))
elif codec_text == "h264_dxva2" and openshot.FFmpegWriter.IsValidCodec(codec_text):
acceleration_types[_(title.childNodes[0].data)] = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-dx.png"))
elif codec_text in ("h264_vtb", "h264_qsv") and openshot.FFmpegWriter.IsValidCodec(codec_text):
acceleration_types[_(title.childNodes[0].data)] = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-vtb.png"))
elif openshot.FFmpegWriter.IsValidCodec(codec_text):
acceleration_types[_(title.childNodes[0].data)] = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-none.png"))
# Add all targets for selected project type
preset_index = 0
selected_preset = 0
for item in sorted(project_types):
self.cboSimpleTarget.addItem(item, item)
icon = acceleration_types.get(item)
if icon:
self.cboSimpleTarget.setIconSize(QSize(60, 18))
self.cboSimpleTarget.addItem(icon, item, item)
else:
continue
# Find index of MP4/H.264
if item == _("MP4 (h.264)"):
......@@ -642,6 +662,22 @@ class Export(QDialog):
app = get_app()
_ = app._tr
# Init progress bar
self.progressExportVideo.setMinimum(self.txtStartFrame.value())
self.progressExportVideo.setMaximum(self.txtEndFrame.value())
self.progressExportVideo.setValue(self.txtStartFrame.value())
# Prompt error message
if self.txtStartFrame.value() == self.txtEndFrame.value():
msg = QMessageBox()
_ = get_app()._tr
msg.setWindowTitle(_("Export Error"))
msg.setText(_("Sorry, please select a valid range of frames to export"))
msg.exec_()
# Do nothing
return
# Disable controls
self.txtFileName.setEnabled(False)
self.txtExportFolder.setEnabled(False)
......@@ -699,7 +735,7 @@ class Export(QDialog):
"pixel_ratio": {"num": self.txtPixelRatioNum.value(), "den": self.txtPixelRatioDen.value()},
"video_bitrate": int(self.convert_to_bytes(self.txtVideoBitRate.text())),
"start_frame": self.txtStartFrame.value(),
"end_frame": self.txtEndFrame.value() + 1
"end_frame": self.txtEndFrame.value()
}
audio_settings = {"acodec": self.txtAudioCodec.text(),
......@@ -722,7 +758,7 @@ class Export(QDialog):
self.timeline.SetMaxSize(video_settings.get("width"), video_settings.get("height"))
# Set lossless cache settings (temporarily)
export_cache_object = openshot.CacheMemory(250)
export_cache_object = openshot.CacheMemory(500)
self.timeline.SetCache(export_cache_object)
# Rescale all keyframes and reload project
......@@ -778,7 +814,6 @@ class Export(QDialog):
w.Open()
# Notify window of export started
export_file_with_path = export_file_path
export_file_path = ""
get_app().window.ExportStarted.emit(export_file_path, video_settings.get("start_frame"), video_settings.get("end_frame"))
......@@ -787,7 +822,7 @@ class Export(QDialog):
start_frame_export = video_settings.get("start_frame")
end_frame_export = video_settings.get("end_frame")
# Write each frame in the selected range
for frame in range(video_settings.get("start_frame"), video_settings.get("end_frame")):
for frame in range(video_settings.get("start_frame"), video_settings.get("end_frame") + 1):
# Update progress bar (emit signal to main window)
if (frame % progressstep) == 0:
end_time_export = time.time()
......@@ -798,10 +833,11 @@ class Export(QDialog):
'minutes': (seconds_left / 60) % 60,
'seconds': seconds_left % 60,
'fps': fps_encode }
# Emit frame exported
get_app().window.ExportFrame.emit(export_file_path, video_settings.get("start_frame"), video_settings.get("end_frame"), frame)
# Process events (to show the progress bar moving)
QCoreApplication.processEvents()
# Process events (to show the progress bar moving)
QCoreApplication.processEvents()
# Write the frame object to the video
w.WriteFrame(self.timeline.GetFrame(frame))
......@@ -812,13 +848,10 @@ class Export(QDialog):
# Close writer
w.Close()
seconds_left = end_time_export - start_time_export
statistics_output_text = _("in %(hours)d:%(minutes)02d:%(seconds)02d (%(fps)5.2f FPS) \nat %(timee)s\nCodec : %(vcodec)s") % { 'hours' : seconds_left / 3600,
'minutes': (seconds_left / 60) % 60,
'seconds': seconds_left % 60,
'fps': fps_encode,
'timee': time.asctime(),
'vcodec': video_settings.get("vcodec") }
# Emit final exported frame
get_app().window.ExportFrame.emit(export_file_path, video_settings.get("start_frame"),
video_settings.get("end_frame"), frame)
except Exception as e:
# TODO: Find a better way to catch the error. This is the only way I have found that
......@@ -878,16 +911,26 @@ class Export(QDialog):
if self.keyframes_rescaled:
get_app().project.rescale_keyframes(self.original_fps_factor)
# Accept dialog
super(Export, self).accept()
# Handle end of export
if self.s.get("show_finished_window"):
msg = QMessageBox()
msg.setText(_("to %s\n%s") % (export_file_with_path , statistics_output_text))
msg.setWindowTitle("Export Video finished")
msg.setIcon(QMessageBox.Information)
msg.setStyleSheet("background-color: rgb(50, 127, 50); color: rgb(255, 255, 255)")
msg.exec_()
# Hide cancel and export buttons
self.cancel_button.setVisible(False)
self.export_button.setVisible(False)
# Reveal done button
self.close_button.setVisible(True)
# Make progress bar green (to indicate we are done)
from PyQt5.QtGui import QPalette
p = QPalette()
p.setColor(QPalette.Highlight, Qt.green)
self.progressExportVideo.setPalette(p)
# Raise the window
self.show()
else:
# Accept dialog
super(Export, self).accept()
def reject(self):
# Re-set OMP thread enabled flag
......
......@@ -32,7 +32,7 @@ import functools
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QKeySequence
from PyQt5.QtGui import QKeySequence, QIcon
from PyQt5 import uic
from classes import info, ui_util, settings, qt_types, updates
......@@ -219,14 +219,34 @@ class Preferences(QDialog):
# Add Default to top of list
value_list.insert(0, {"name":_("Default"), "value":"Default"})
# Add normal values
box_index = 0
for value_item in value_list:
k = value_item["name"]
v = value_item["value"]
# Override icons for certain values
# TODO: Find a more elegant way to do this
icon = None
if k == "Linux VA-API":
icon = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-vaapi.png"))
elif k == "Linux Nvidia NVDEC":
icon = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-nvdec.png"))
elif k == "Windows D3D9":
icon = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-dx.png"))
elif k == "Windows D3D11":
icon = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-dx.png"))
elif k == "MacOS":
icon = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-vtb.png"))
elif k == "No acceleration":
icon = QIcon(os.path.join(info.IMAGES_PATH, "hw-accel-none.png"))
# add dropdown item
widget.addItem(_(k), v)
if icon:
widget.setIconSize(QSize(60, 18))
widget.addItem(icon, _(k), v)
else:
widget.addItem(_(k), v)
# select dropdown (if default)
if v == param["value"]:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册