change: change code style to Linux style except indent.
This commit is contained in:
+71
-103
@@ -1,148 +1,116 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# clang-format configuration file. Intended for clang-format >= 11.
|
||||||
|
#
|
||||||
|
# For more information, see:
|
||||||
|
#
|
||||||
|
# Documentation/dev-tools/clang-format.rst
|
||||||
|
# https://clang.llvm.org/docs/ClangFormat.html
|
||||||
|
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||||
|
#
|
||||||
---
|
---
|
||||||
Language: Cpp
|
AccessModifierOffset: -4
|
||||||
# BasedOnStyle: Google
|
|
||||||
AccessModifierOffset: -1
|
|
||||||
AlignAfterOpenBracket: Align
|
AlignAfterOpenBracket: Align
|
||||||
AlignConsecutiveAssignments: false
|
AlignConsecutiveAssignments: false
|
||||||
AlignConsecutiveDeclarations: false
|
AlignConsecutiveDeclarations: false
|
||||||
AlignEscapedNewlines: Left
|
AlignEscapedNewlines: Left
|
||||||
AlignOperands: true
|
AlignOperands: true
|
||||||
AlignTrailingComments: true
|
AlignTrailingComments: false
|
||||||
AllowAllParametersOfDeclarationOnNextLine: true
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
AllowShortBlocksOnASingleLine: false
|
AllowShortBlocksOnASingleLine: false
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
AllowShortFunctionsOnASingleLine: All
|
AllowShortFunctionsOnASingleLine: None
|
||||||
AllowShortIfStatementsOnASingleLine: true
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
AllowShortLoopsOnASingleLine: true
|
AllowShortLoopsOnASingleLine: false
|
||||||
AlwaysBreakAfterDefinitionReturnType: None
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
AlwaysBreakAfterReturnType: None
|
AlwaysBreakAfterReturnType: None
|
||||||
AlwaysBreakBeforeMultilineStrings: true
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
AlwaysBreakTemplateDeclarations: false
|
||||||
BinPackArguments: true
|
BinPackArguments: true
|
||||||
BinPackParameters: true
|
BinPackParameters: true
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
AfterClass: false
|
AfterClass: false
|
||||||
AfterControlStatement: false
|
AfterControlStatement: false
|
||||||
AfterEnum: false
|
AfterEnum: false
|
||||||
AfterFunction: false
|
AfterFunction: true
|
||||||
AfterNamespace: false
|
AfterNamespace: true
|
||||||
AfterObjCDeclaration: false
|
AfterObjCDeclaration: false
|
||||||
AfterStruct: false
|
AfterStruct: false
|
||||||
AfterUnion: false
|
AfterUnion: false
|
||||||
AfterExternBlock: false
|
AfterExternBlock: false
|
||||||
BeforeCatch: false
|
BeforeCatch: false
|
||||||
BeforeElse: false
|
BeforeElse: false
|
||||||
IndentBraces: false
|
IndentBraces: false
|
||||||
SplitEmptyFunction: true
|
SplitEmptyFunction: true
|
||||||
SplitEmptyRecord: true
|
SplitEmptyRecord: true
|
||||||
SplitEmptyNamespace: true
|
SplitEmptyNamespace: true
|
||||||
BreakBeforeBinaryOperators: None
|
BreakBeforeBinaryOperators: None
|
||||||
BreakBeforeBraces: Attach
|
BreakBeforeBraces: Custom
|
||||||
BreakBeforeInheritanceComma: false
|
BreakBeforeInheritanceComma: false
|
||||||
BreakInheritanceList: BeforeColon
|
BreakBeforeTernaryOperators: false
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializersBeforeComma: false
|
BreakConstructorInitializersBeforeComma: false
|
||||||
BreakConstructorInitializers: BeforeColon
|
BreakConstructorInitializers: BeforeComma
|
||||||
BreakAfterJavaFieldAnnotations: false
|
BreakAfterJavaFieldAnnotations: false
|
||||||
BreakStringLiterals: true
|
BreakStringLiterals: false
|
||||||
ColumnLimit: 120
|
ColumnLimit: 80
|
||||||
CommentPragmas: '^ IWYU pragma:'
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
CompactNamespaces: false
|
CompactNamespaces: false
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
ContinuationIndentWidth: 4
|
ContinuationIndentWidth: 4
|
||||||
Cpp11BracedListStyle: true
|
Cpp11BracedListStyle: false
|
||||||
DerivePointerAlignment: true
|
DerivePointerAlignment: false
|
||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
ExperimentalAutoDetectBinPacking: false
|
ExperimentalAutoDetectBinPacking: false
|
||||||
FixNamespaceComments: true
|
FixNamespaceComments: false
|
||||||
ForEachMacros:
|
IncludeBlocks: Preserve
|
||||||
- foreach
|
IncludeCategories:
|
||||||
- Q_FOREACH
|
- Regex: '.*'
|
||||||
- BOOST_FOREACH
|
Priority: 1
|
||||||
IncludeBlocks: Preserve
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
IncludeCategories:
|
IndentCaseLabels: false
|
||||||
- Regex: '^<ext/.*\.h>'
|
IndentGotoLabels: false
|
||||||
Priority: 2
|
|
||||||
- Regex: '^<.*\.h>'
|
|
||||||
Priority: 1
|
|
||||||
- Regex: '^<.*'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '.*'
|
|
||||||
Priority: 3
|
|
||||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
|
||||||
IndentCaseLabels: true
|
|
||||||
IndentPPDirectives: None
|
IndentPPDirectives: None
|
||||||
IndentWidth: 2
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
JavaScriptWrapImports: true
|
JavaScriptWrapImports: true
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
MacroBlockBegin: ''
|
MacroBlockBegin: ''
|
||||||
MacroBlockEnd: ''
|
MacroBlockEnd: ''
|
||||||
MaxEmptyLinesToKeep: 1
|
MaxEmptyLinesToKeep: 1
|
||||||
NamespaceIndentation: None
|
NamespaceIndentation: None
|
||||||
ObjCBinPackProtocolList: Never
|
ObjCBinPackProtocolList: Auto
|
||||||
ObjCBlockIndentWidth: 2
|
ObjCBlockIndentWidth: 4
|
||||||
ObjCSpaceAfterProperty: false
|
ObjCSpaceAfterProperty: true
|
||||||
ObjCSpaceBeforeProtocolList: true
|
ObjCSpaceBeforeProtocolList: true
|
||||||
PenaltyBreakAssignment: 2
|
|
||||||
PenaltyBreakBeforeFirstCallParameter: 1
|
# Taken from git's rules
|
||||||
PenaltyBreakComment: 300
|
PenaltyBreakAssignment: 10
|
||||||
PenaltyBreakFirstLessLess: 120
|
PenaltyBreakBeforeFirstCallParameter: 30
|
||||||
PenaltyBreakString: 1000
|
PenaltyBreakComment: 10
|
||||||
PenaltyBreakTemplateDeclaration: 10
|
PenaltyBreakFirstLessLess: 0
|
||||||
PenaltyExcessCharacter: 1000000
|
PenaltyBreakString: 10
|
||||||
PenaltyReturnTypeOnItsOwnLine: 200
|
PenaltyExcessCharacter: 100
|
||||||
PointerAlignment: Left
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
RawStringFormats:
|
|
||||||
- Language: Cpp
|
PointerAlignment: Right
|
||||||
Delimiters:
|
ReflowComments: false
|
||||||
- cc
|
SortIncludes: false
|
||||||
- CC
|
SortUsingDeclarations: false
|
||||||
- cpp
|
|
||||||
- Cpp
|
|
||||||
- CPP
|
|
||||||
- 'c++'
|
|
||||||
- 'C++'
|
|
||||||
CanonicalDelimiter: ''
|
|
||||||
BasedOnStyle: google
|
|
||||||
- Language: TextProto
|
|
||||||
Delimiters:
|
|
||||||
- pb
|
|
||||||
- PB
|
|
||||||
- proto
|
|
||||||
- PROTO
|
|
||||||
EnclosingFunctions:
|
|
||||||
- EqualsProto
|
|
||||||
- EquivToProto
|
|
||||||
- PARSE_PARTIAL_TEXT_PROTO
|
|
||||||
- PARSE_TEST_PROTO
|
|
||||||
- PARSE_TEXT_PROTO
|
|
||||||
- ParseTextOrDie
|
|
||||||
- ParseTextProtoOrDie
|
|
||||||
CanonicalDelimiter: ''
|
|
||||||
BasedOnStyle: google
|
|
||||||
ReflowComments: true
|
|
||||||
SortIncludes: true
|
|
||||||
SortUsingDeclarations: true
|
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
SpaceAfterTemplateKeyword: true
|
SpaceAfterTemplateKeyword: true
|
||||||
SpaceBeforeAssignmentOperators: true
|
SpaceBeforeAssignmentOperators: true
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: true
|
SpaceBeforeCtorInitializerColon: true
|
||||||
SpaceBeforeInheritanceColon: true
|
SpaceBeforeInheritanceColon: true
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: true
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
SpaceInEmptyParentheses: false
|
SpaceInEmptyParentheses: false
|
||||||
SpacesBeforeTrailingComments: 2
|
SpacesBeforeTrailingComments: 1
|
||||||
SpacesInAngles: false
|
SpacesInAngles: false
|
||||||
SpacesInContainerLiterals: true
|
SpacesInContainerLiterals: false
|
||||||
SpacesInCStyleCastParentheses: false
|
SpacesInCStyleCastParentheses: false
|
||||||
SpacesInParentheses: false
|
SpacesInParentheses: false
|
||||||
SpacesInSquareBrackets: false
|
SpacesInSquareBrackets: false
|
||||||
Standard: Auto
|
TabWidth: 4
|
||||||
TabWidth: 8
|
UseTab: Always
|
||||||
UseTab: Never
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
# Generated from CLion Inspection settings
|
||||||
|
---
|
||||||
|
Checks: '-*,
|
||||||
|
bugprone-argument-comment,
|
||||||
|
bugprone-assert-side-effect,
|
||||||
|
bugprone-bad-signal-to-kill-thread,
|
||||||
|
bugprone-branch-clone,
|
||||||
|
bugprone-copy-constructor-init,
|
||||||
|
bugprone-dangling-handle,
|
||||||
|
bugprone-dynamic-static-initializers,
|
||||||
|
bugprone-fold-init-type,
|
||||||
|
bugprone-forward-declaration-namespace,
|
||||||
|
bugprone-forwarding-reference-overload,
|
||||||
|
bugprone-inaccurate-erase,
|
||||||
|
bugprone-incorrect-roundings,
|
||||||
|
bugprone-integer-division,
|
||||||
|
bugprone-lambda-function-name,
|
||||||
|
bugprone-macro-parentheses,
|
||||||
|
bugprone-macro-repeated-side-effects,
|
||||||
|
bugprone-misplaced-operator-in-strlen-in-alloc,
|
||||||
|
bugprone-misplaced-pointer-arithmetic-in-alloc,
|
||||||
|
bugprone-misplaced-widening-cast,
|
||||||
|
bugprone-move-forwarding-reference,
|
||||||
|
bugprone-multiple-statement-macro,
|
||||||
|
bugprone-no-escape,
|
||||||
|
bugprone-parent-virtual-call,
|
||||||
|
bugprone-posix-return,
|
||||||
|
bugprone-reserved-identifier,
|
||||||
|
bugprone-sizeof-container,
|
||||||
|
bugprone-sizeof-expression,
|
||||||
|
bugprone-spuriously-wake-up-functions,
|
||||||
|
bugprone-string-constructor,
|
||||||
|
bugprone-string-integer-assignment,
|
||||||
|
bugprone-string-literal-with-embedded-nul,
|
||||||
|
bugprone-suspicious-enum-usage,
|
||||||
|
bugprone-suspicious-include,
|
||||||
|
bugprone-suspicious-memset-usage,
|
||||||
|
bugprone-suspicious-missing-comma,
|
||||||
|
bugprone-suspicious-semicolon,
|
||||||
|
bugprone-suspicious-string-compare,
|
||||||
|
bugprone-suspicious-memory-comparison,
|
||||||
|
bugprone-suspicious-realloc-usage,
|
||||||
|
bugprone-swapped-arguments,
|
||||||
|
bugprone-terminating-continue,
|
||||||
|
bugprone-throw-keyword-missing,
|
||||||
|
bugprone-too-small-loop-variable,
|
||||||
|
bugprone-undefined-memory-manipulation,
|
||||||
|
bugprone-undelegated-constructor,
|
||||||
|
bugprone-unhandled-self-assignment,
|
||||||
|
bugprone-unused-raii,
|
||||||
|
bugprone-unused-return-value,
|
||||||
|
bugprone-use-after-move,
|
||||||
|
bugprone-virtual-near-miss,
|
||||||
|
cert-dcl21-cpp,
|
||||||
|
cert-dcl58-cpp,
|
||||||
|
cert-err34-c,
|
||||||
|
cert-err52-cpp,
|
||||||
|
cert-err60-cpp,
|
||||||
|
cert-flp30-c,
|
||||||
|
cert-msc50-cpp,
|
||||||
|
cert-msc51-cpp,
|
||||||
|
cert-str34-c,
|
||||||
|
cppcoreguidelines-interfaces-global-init,
|
||||||
|
cppcoreguidelines-narrowing-conversions,
|
||||||
|
cppcoreguidelines-pro-type-member-init,
|
||||||
|
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||||
|
cppcoreguidelines-slicing,
|
||||||
|
google-default-arguments,
|
||||||
|
google-explicit-constructor,
|
||||||
|
google-runtime-operator,
|
||||||
|
hicpp-exception-baseclass,
|
||||||
|
hicpp-multiway-paths-covered,
|
||||||
|
misc-misplaced-const,
|
||||||
|
misc-new-delete-overloads,
|
||||||
|
misc-no-recursion,
|
||||||
|
misc-non-copyable-objects,
|
||||||
|
misc-throw-by-value-catch-by-reference,
|
||||||
|
misc-unconventional-assign-operator,
|
||||||
|
misc-uniqueptr-reset-release,
|
||||||
|
modernize-avoid-bind,
|
||||||
|
modernize-concat-nested-namespaces,
|
||||||
|
modernize-deprecated-headers,
|
||||||
|
modernize-deprecated-ios-base-aliases,
|
||||||
|
modernize-loop-convert,
|
||||||
|
modernize-make-shared,
|
||||||
|
modernize-make-unique,
|
||||||
|
modernize-pass-by-value,
|
||||||
|
modernize-raw-string-literal,
|
||||||
|
modernize-redundant-void-arg,
|
||||||
|
modernize-replace-auto-ptr,
|
||||||
|
modernize-replace-disallow-copy-and-assign-macro,
|
||||||
|
modernize-replace-random-shuffle,
|
||||||
|
modernize-return-braced-init-list,
|
||||||
|
modernize-shrink-to-fit,
|
||||||
|
modernize-unary-static-assert,
|
||||||
|
modernize-use-auto,
|
||||||
|
modernize-use-bool-literals,
|
||||||
|
modernize-use-emplace,
|
||||||
|
modernize-use-equals-default,
|
||||||
|
modernize-use-equals-delete,
|
||||||
|
modernize-use-nodiscard,
|
||||||
|
modernize-use-noexcept,
|
||||||
|
modernize-use-nullptr,
|
||||||
|
modernize-use-override,
|
||||||
|
modernize-use-transparent-functors,
|
||||||
|
modernize-use-uncaught-exceptions,
|
||||||
|
mpi-buffer-deref,
|
||||||
|
mpi-type-mismatch,
|
||||||
|
openmp-use-default-none,
|
||||||
|
performance-faster-string-find,
|
||||||
|
performance-for-range-copy,
|
||||||
|
performance-implicit-conversion-in-loop,
|
||||||
|
performance-inefficient-algorithm,
|
||||||
|
performance-inefficient-string-concatenation,
|
||||||
|
performance-inefficient-vector-operation,
|
||||||
|
performance-move-const-arg,
|
||||||
|
performance-move-constructor-init,
|
||||||
|
performance-no-automatic-move,
|
||||||
|
performance-noexcept-move-constructor,
|
||||||
|
performance-trivially-destructible,
|
||||||
|
performance-type-promotion-in-math-fn,
|
||||||
|
performance-unnecessary-copy-initialization,
|
||||||
|
performance-unnecessary-value-param,
|
||||||
|
portability-simd-intrinsics,
|
||||||
|
readability-avoid-const-params-in-decls,
|
||||||
|
readability-const-return-type,
|
||||||
|
readability-container-size-empty,
|
||||||
|
readability-convert-member-functions-to-static,
|
||||||
|
readability-delete-null-pointer,
|
||||||
|
readability-deleted-default,
|
||||||
|
readability-inconsistent-declaration-parameter-name,
|
||||||
|
readability-make-member-function-const,
|
||||||
|
readability-misleading-indentation,
|
||||||
|
readability-misplaced-array-index,
|
||||||
|
readability-non-const-parameter,
|
||||||
|
readability-redundant-control-flow,
|
||||||
|
readability-redundant-declaration,
|
||||||
|
readability-redundant-function-ptr-dereference,
|
||||||
|
readability-redundant-smartptr-get,
|
||||||
|
readability-redundant-string-cstr,
|
||||||
|
readability-redundant-string-init,
|
||||||
|
readability-simplify-subscript-expr,
|
||||||
|
readability-static-accessed-through-instance,
|
||||||
|
readability-static-definition-in-anonymous-namespace,
|
||||||
|
readability-string-compare,
|
||||||
|
readability-uniqueptr-delete-release,
|
||||||
|
readability-use-anyofallof'
|
||||||
+200
-171
@@ -28,290 +28,319 @@
|
|||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
AudioManager* AudioManager::instance_ = nullptr;
|
AudioManager *AudioManager::instance_ = nullptr;
|
||||||
|
|
||||||
void AudioManager::CreateInstance()
|
void AudioManager::CreateInstance()
|
||||||
{
|
{
|
||||||
if (instance_ == nullptr) {
|
if (instance_ == nullptr) {
|
||||||
instance_ = new AudioManager();
|
instance_ = new AudioManager();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::DestroyInstance()
|
void AudioManager::DestroyInstance()
|
||||||
{
|
{
|
||||||
delete instance_;
|
delete instance_;
|
||||||
instance_ = nullptr;
|
instance_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioManager *AudioManager::instance()
|
AudioManager *AudioManager::instance()
|
||||||
{
|
{
|
||||||
return instance_;
|
return instance_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::SetOutputNotifyInterval(int n)
|
void AudioManager::SetOutputNotifyInterval(int n)
|
||||||
{
|
{
|
||||||
output_buffer_->set_notify_interval(n);
|
output_buffer_->set_notify_interval(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
int OutputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
|
int OutputCallback(const void *input, void *output, unsigned long frameCount,
|
||||||
|
const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
|
PaStreamCallbackFlags statusFlags, void *userData)
|
||||||
{
|
{
|
||||||
PreviewAudioDevice *device = static_cast<PreviewAudioDevice*>(userData);
|
PreviewAudioDevice *device = static_cast<PreviewAudioDevice *>(userData);
|
||||||
|
|
||||||
qint64 max_read = frameCount * device->bytes_per_frame();
|
qint64 max_read = frameCount * device->bytes_per_frame();
|
||||||
qint64 read_count = device->read(reinterpret_cast<char*>(output), max_read);
|
qint64 read_count =
|
||||||
if (read_count < max_read) {
|
device->read(reinterpret_cast<char *>(output), max_read);
|
||||||
memset(reinterpret_cast<uint8_t*>(output) + read_count, 0, max_read - read_count);
|
if (read_count < max_read) {
|
||||||
}
|
memset(reinterpret_cast<uint8_t *>(output) + read_count, 0,
|
||||||
|
max_read - read_count);
|
||||||
|
}
|
||||||
|
|
||||||
return paContinue;
|
return paContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int InputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
|
int InputCallback(const void *input, void *output, unsigned long frameCount,
|
||||||
|
const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
|
PaStreamCallbackFlags statusFlags, void *userData)
|
||||||
{
|
{
|
||||||
FFmpegEncoder *f = static_cast<FFmpegEncoder*>(userData);
|
FFmpegEncoder *f = static_cast<FFmpegEncoder *>(userData);
|
||||||
|
|
||||||
AudioParams our_params = f->params().audio_params();
|
AudioParams our_params = f->params().audio_params();
|
||||||
our_params.set_format(f->params().audio_params().format().to_packed_equivalent());
|
our_params.set_format(
|
||||||
|
f->params().audio_params().format().to_packed_equivalent());
|
||||||
|
|
||||||
f->WriteAudioData(our_params, reinterpret_cast<const uint8_t**>(&input), frameCount);
|
f->WriteAudioData(our_params, reinterpret_cast<const uint8_t **>(&input),
|
||||||
|
frameCount);
|
||||||
|
|
||||||
return paContinue;
|
return paContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioManager::PushToOutput(const AudioParams ¶ms, const QByteArray &samples, QString *error)
|
bool AudioManager::PushToOutput(const AudioParams ¶ms,
|
||||||
|
const QByteArray &samples, QString *error)
|
||||||
{
|
{
|
||||||
if (output_device_ == paNoDevice) {
|
if (output_device_ == paNoDevice) {
|
||||||
if (error) *error = tr("No output device is set");
|
if (error)
|
||||||
return false;
|
*error = tr("No output device is set");
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (output_params_ != params || output_stream_ == nullptr) {
|
if (output_params_ != params || output_stream_ == nullptr) {
|
||||||
output_params_ = params;
|
output_params_ = params;
|
||||||
|
|
||||||
CloseOutputStream();
|
CloseOutputStream();
|
||||||
|
|
||||||
PaStreamParameters p = GetPortAudioParams(params, output_device_);
|
PaStreamParameters p = GetPortAudioParams(params, output_device_);
|
||||||
|
|
||||||
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_);
|
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p,
|
||||||
if (r != paNoError) {
|
output_params_.sample_rate(),
|
||||||
// Unhandled error
|
paFramesPerBufferUnspecified, paNoFlag,
|
||||||
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
|
OutputCallback, output_buffer_);
|
||||||
if (error) *error = Pa_GetErrorText(r);
|
if (r != paNoError) {
|
||||||
return false;
|
// Unhandled error
|
||||||
}
|
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
|
||||||
|
if (error)
|
||||||
|
*error = Pa_GetErrorText(r);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
|
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
output_buffer_->write(samples);
|
output_buffer_->write(samples);
|
||||||
|
|
||||||
if (!Pa_IsStreamActive(output_stream_)) {
|
if (!Pa_IsStreamActive(output_stream_)) {
|
||||||
Pa_StartStream(output_stream_);
|
Pa_StartStream(output_stream_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::ClearBufferedOutput()
|
void AudioManager::ClearBufferedOutput()
|
||||||
{
|
{
|
||||||
output_buffer_->clear();
|
output_buffer_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
PaSampleFormat AudioManager::GetPortAudioSampleFormat(SampleFormat fmt)
|
PaSampleFormat AudioManager::GetPortAudioSampleFormat(SampleFormat fmt)
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case SampleFormat::U8:
|
case SampleFormat::U8:
|
||||||
case SampleFormat::U8P:
|
case SampleFormat::U8P:
|
||||||
return paUInt8;
|
return paUInt8;
|
||||||
case SampleFormat::S16:
|
case SampleFormat::S16:
|
||||||
case SampleFormat::S16P:
|
case SampleFormat::S16P:
|
||||||
return paInt16;
|
return paInt16;
|
||||||
case SampleFormat::S32:
|
case SampleFormat::S32:
|
||||||
case SampleFormat::S32P:
|
case SampleFormat::S32P:
|
||||||
return paInt32;
|
return paInt32;
|
||||||
case SampleFormat::F32:
|
case SampleFormat::F32:
|
||||||
case SampleFormat::F32P:
|
case SampleFormat::F32P:
|
||||||
return paFloat32;
|
return paFloat32;
|
||||||
case SampleFormat::S64:
|
case SampleFormat::S64:
|
||||||
case SampleFormat::S64P:
|
case SampleFormat::S64P:
|
||||||
case SampleFormat::F64:
|
case SampleFormat::F64:
|
||||||
case SampleFormat::F64P:
|
case SampleFormat::F64P:
|
||||||
case SampleFormat::INVALID:
|
case SampleFormat::INVALID:
|
||||||
case SampleFormat::COUNT:
|
case SampleFormat::COUNT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::CloseOutputStream()
|
void AudioManager::CloseOutputStream()
|
||||||
{
|
{
|
||||||
if (output_stream_) {
|
if (output_stream_) {
|
||||||
if (Pa_IsStreamActive(output_stream_)) {
|
if (Pa_IsStreamActive(output_stream_)) {
|
||||||
StopOutput();
|
StopOutput();
|
||||||
}
|
}
|
||||||
Pa_CloseStream(output_stream_);
|
Pa_CloseStream(output_stream_);
|
||||||
output_stream_ = nullptr;
|
output_stream_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::StopOutput()
|
void AudioManager::StopOutput()
|
||||||
{
|
{
|
||||||
// Abort the stream so playback stops immediately
|
// Abort the stream so playback stops immediately
|
||||||
if (output_stream_) {
|
if (output_stream_) {
|
||||||
Pa_AbortStream(output_stream_);
|
Pa_AbortStream(output_stream_);
|
||||||
ClearBufferedOutput();
|
ClearBufferedOutput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::SetOutputDevice(PaDeviceIndex device)
|
void AudioManager::SetOutputDevice(PaDeviceIndex device)
|
||||||
{
|
{
|
||||||
if (device == paNoDevice) {
|
if (device == paNoDevice) {
|
||||||
qInfo() << "No output device found";
|
qInfo() << "No output device found";
|
||||||
} else {
|
} else {
|
||||||
qInfo() << "Setting output audio device to" << Pa_GetDeviceInfo(device)->name;
|
qInfo() << "Setting output audio device to"
|
||||||
}
|
<< Pa_GetDeviceInfo(device)->name;
|
||||||
|
}
|
||||||
|
|
||||||
output_device_ = device;
|
output_device_ = device;
|
||||||
|
|
||||||
CloseOutputStream();
|
CloseOutputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::SetInputDevice(PaDeviceIndex device)
|
void AudioManager::SetInputDevice(PaDeviceIndex device)
|
||||||
{
|
{
|
||||||
if (device == paNoDevice) {
|
if (device == paNoDevice) {
|
||||||
qInfo() << "No input device found";
|
qInfo() << "No input device found";
|
||||||
} else {
|
} else {
|
||||||
qInfo() << "Setting input audio device to" << Pa_GetDeviceInfo(device)->name;
|
qInfo() << "Setting input audio device to"
|
||||||
}
|
<< Pa_GetDeviceInfo(device)->name;
|
||||||
|
}
|
||||||
|
|
||||||
input_device_ = device;
|
input_device_ = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::HardReset()
|
void AudioManager::HardReset()
|
||||||
{
|
{
|
||||||
CloseOutputStream();
|
CloseOutputStream();
|
||||||
Pa_Terminate();
|
Pa_Terminate();
|
||||||
Pa_Initialize();
|
Pa_Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioManager::StartRecording(const EncodingParams ¶ms, QString *error_str)
|
bool AudioManager::StartRecording(const EncodingParams ¶ms,
|
||||||
|
QString *error_str)
|
||||||
{
|
{
|
||||||
if (input_device_ == paNoDevice) {
|
if (input_device_ == paNoDevice) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_encoder_ = new FFmpegEncoder(params);
|
input_encoder_ = new FFmpegEncoder(params);
|
||||||
if (!input_encoder_->Open()) {
|
if (!input_encoder_->Open()) {
|
||||||
qCritical() << "Failed to open encoder for recording";
|
qCritical() << "Failed to open encoder for recording";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PaStreamParameters p = GetPortAudioParams(params.audio_params(), input_device_);
|
PaStreamParameters p =
|
||||||
|
GetPortAudioParams(params.audio_params(), input_device_);
|
||||||
|
|
||||||
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_);
|
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr,
|
||||||
if (r == paNoError) {
|
params.audio_params().sample_rate(),
|
||||||
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
|
paFramesPerBufferUnspecified, paNoFlag,
|
||||||
r = Pa_StartStream(input_stream_);
|
InputCallback, input_encoder_);
|
||||||
if (r == paNoError) {
|
if (r == paNoError) {
|
||||||
return true;
|
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
|
||||||
}
|
r = Pa_StartStream(input_stream_);
|
||||||
}
|
if (r == paNoError) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (error_str) {
|
if (error_str) {
|
||||||
*error_str = Pa_GetErrorText(r);
|
*error_str = Pa_GetErrorText(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
StopRecording();
|
StopRecording();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::StopRecording()
|
void AudioManager::StopRecording()
|
||||||
{
|
{
|
||||||
if (input_stream_) {
|
if (input_stream_) {
|
||||||
if (Pa_IsStreamActive(input_stream_)) {
|
if (Pa_IsStreamActive(input_stream_)) {
|
||||||
Pa_StopStream(input_stream_);
|
Pa_StopStream(input_stream_);
|
||||||
}
|
}
|
||||||
Pa_CloseStream(input_stream_);
|
Pa_CloseStream(input_stream_);
|
||||||
|
|
||||||
input_stream_ = nullptr;
|
input_stream_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_encoder_) {
|
if (input_encoder_) {
|
||||||
input_encoder_->Close();
|
input_encoder_->Close();
|
||||||
delete input_encoder_;
|
delete input_encoder_;
|
||||||
input_encoder_ = nullptr;
|
input_encoder_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device)
|
PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device)
|
||||||
{
|
{
|
||||||
QString entry = is_output_device ? QStringLiteral("AudioOutput") : QStringLiteral("AudioInput");
|
QString entry = is_output_device ? QStringLiteral("AudioOutput") :
|
||||||
|
QStringLiteral("AudioInput");
|
||||||
|
|
||||||
return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(), is_output_device);
|
return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(),
|
||||||
|
is_output_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s, bool is_output_device)
|
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s,
|
||||||
|
bool is_output_device)
|
||||||
{
|
{
|
||||||
if (!s.isEmpty()) {
|
if (!s.isEmpty()) {
|
||||||
for (PaDeviceIndex i=0, end=Pa_GetDeviceCount(); i<end; i++) {
|
for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) {
|
||||||
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
|
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
|
||||||
|
|
||||||
if (((is_output_device && device->maxOutputChannels) || (!is_output_device && device->maxInputChannels))
|
if (((is_output_device && device->maxOutputChannels) ||
|
||||||
&& !s.compare(device->name)) {
|
(!is_output_device && device->maxInputChannels)) &&
|
||||||
return i;
|
!s.compare(device->name)) {
|
||||||
}
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return is_output_device ? Pa_GetDefaultOutputDevice() : Pa_GetDefaultInputDevice();
|
return is_output_device ? Pa_GetDefaultOutputDevice() :
|
||||||
|
Pa_GetDefaultInputDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams ¶ms, PaDeviceIndex device)
|
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams ¶ms,
|
||||||
|
PaDeviceIndex device)
|
||||||
{
|
{
|
||||||
PaStreamParameters p;
|
PaStreamParameters p;
|
||||||
|
|
||||||
p.channelCount = params.channel_count();
|
p.channelCount = params.channel_count();
|
||||||
p.device = device;
|
p.device = device;
|
||||||
p.hostApiSpecificStreamInfo = nullptr;
|
p.hostApiSpecificStreamInfo = nullptr;
|
||||||
p.sampleFormat = GetPortAudioSampleFormat(params.format());
|
p.sampleFormat = GetPortAudioSampleFormat(params.format());
|
||||||
p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
|
p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioManager::AudioManager() :
|
AudioManager::AudioManager()
|
||||||
output_stream_(nullptr),
|
: output_stream_(nullptr)
|
||||||
input_stream_(nullptr),
|
, input_stream_(nullptr)
|
||||||
input_encoder_(nullptr)
|
, input_encoder_(nullptr)
|
||||||
{
|
{
|
||||||
#ifdef PA_HAS_JACK
|
#ifdef PA_HAS_JACK
|
||||||
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
|
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
|
||||||
// a QString converted to UTF-8)
|
// a QString converted to UTF-8)
|
||||||
PaJack_SetClientName("Olive");
|
PaJack_SetClientName("Olive");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Pa_Initialize();
|
Pa_Initialize();
|
||||||
|
|
||||||
// Get device from config
|
// Get device from config
|
||||||
PaDeviceIndex output_device = FindConfigDeviceByName(true);
|
PaDeviceIndex output_device = FindConfigDeviceByName(true);
|
||||||
PaDeviceIndex input_device = FindConfigDeviceByName(false);
|
PaDeviceIndex input_device = FindConfigDeviceByName(false);
|
||||||
|
|
||||||
SetOutputDevice(output_device);
|
SetOutputDevice(output_device);
|
||||||
SetInputDevice(input_device);
|
SetInputDevice(input_device);
|
||||||
|
|
||||||
output_buffer_ = new PreviewAudioDevice(this);
|
output_buffer_ = new PreviewAudioDevice(this);
|
||||||
output_buffer_->open(PreviewAudioDevice::ReadWrite);
|
output_buffer_->open(PreviewAudioDevice::ReadWrite);
|
||||||
connect(output_buffer_, &PreviewAudioDevice::Notify, this, &AudioManager::OutputNotify);
|
connect(output_buffer_, &PreviewAudioDevice::Notify, this,
|
||||||
|
&AudioManager::OutputNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioManager::~AudioManager()
|
AudioManager::~AudioManager()
|
||||||
{
|
{
|
||||||
CloseOutputStream();
|
CloseOutputStream();
|
||||||
|
|
||||||
Pa_Terminate();
|
Pa_Terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-42
@@ -33,7 +33,8 @@
|
|||||||
#include "render/audioplaybackcache.h"
|
#include "render/audioplaybackcache.h"
|
||||||
#include "render/previewaudiodevice.h"
|
#include "render/previewaudiodevice.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Audio input and output management class
|
* @brief Audio input and output management class
|
||||||
@@ -41,74 +42,76 @@ namespace olive {
|
|||||||
* Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to
|
* Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to
|
||||||
* the rest of the system.
|
* the rest of the system.
|
||||||
*/
|
*/
|
||||||
class AudioManager : public QObject
|
class AudioManager : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
static void CreateInstance();
|
static void CreateInstance();
|
||||||
static void DestroyInstance();
|
static void DestroyInstance();
|
||||||
|
|
||||||
static AudioManager* instance();
|
static AudioManager *instance();
|
||||||
|
|
||||||
void SetOutputNotifyInterval(int n);
|
void SetOutputNotifyInterval(int n);
|
||||||
|
|
||||||
bool PushToOutput(const AudioParams ¶ms, const QByteArray& samples, QString *error = nullptr);
|
bool PushToOutput(const AudioParams ¶ms, const QByteArray &samples,
|
||||||
|
QString *error = nullptr);
|
||||||
|
|
||||||
void ClearBufferedOutput();
|
void ClearBufferedOutput();
|
||||||
|
|
||||||
void StopOutput();
|
void StopOutput();
|
||||||
|
|
||||||
PaDeviceIndex GetOutputDevice() const
|
PaDeviceIndex GetOutputDevice() const
|
||||||
{
|
{
|
||||||
return output_device_;
|
return output_device_;
|
||||||
}
|
}
|
||||||
|
|
||||||
PaDeviceIndex GetInputDevice() const
|
PaDeviceIndex GetInputDevice() const
|
||||||
{
|
{
|
||||||
return input_device_;
|
return input_device_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOutputDevice(PaDeviceIndex device);
|
void SetOutputDevice(PaDeviceIndex device);
|
||||||
|
|
||||||
void SetInputDevice(PaDeviceIndex device);
|
void SetInputDevice(PaDeviceIndex device);
|
||||||
|
|
||||||
void HardReset();
|
void HardReset();
|
||||||
|
|
||||||
bool StartRecording(const EncodingParams ¶ms, QString *error_str = nullptr);
|
bool StartRecording(const EncodingParams ¶ms,
|
||||||
|
QString *error_str = nullptr);
|
||||||
|
|
||||||
void StopRecording();
|
void StopRecording();
|
||||||
|
|
||||||
static PaDeviceIndex FindConfigDeviceByName(bool is_output_device);
|
static PaDeviceIndex FindConfigDeviceByName(bool is_output_device);
|
||||||
static PaDeviceIndex FindDeviceByName(const QString &s, bool is_output_device);
|
static PaDeviceIndex FindDeviceByName(const QString &s,
|
||||||
|
bool is_output_device);
|
||||||
|
|
||||||
static PaStreamParameters GetPortAudioParams(const AudioParams &p, PaDeviceIndex device);
|
static PaStreamParameters GetPortAudioParams(const AudioParams &p,
|
||||||
|
PaDeviceIndex device);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void OutputNotify();
|
void OutputNotify();
|
||||||
|
|
||||||
void OutputParamsChanged();
|
void OutputParamsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioManager();
|
AudioManager();
|
||||||
|
|
||||||
virtual ~AudioManager() override;
|
virtual ~AudioManager() override;
|
||||||
|
|
||||||
static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt);
|
static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt);
|
||||||
|
|
||||||
void CloseOutputStream();
|
void CloseOutputStream();
|
||||||
|
|
||||||
static AudioManager* instance_;
|
static AudioManager *instance_;
|
||||||
|
|
||||||
PaDeviceIndex output_device_;
|
PaDeviceIndex output_device_;
|
||||||
PaStream *output_stream_;
|
PaStream *output_stream_;
|
||||||
AudioParams output_params_;
|
AudioParams output_params_;
|
||||||
PreviewAudioDevice *output_buffer_;
|
PreviewAudioDevice *output_buffer_;
|
||||||
|
|
||||||
PaDeviceIndex input_device_;
|
PaDeviceIndex input_device_;
|
||||||
PaStream *input_stream_;
|
PaStream *input_stream_;
|
||||||
|
|
||||||
FFmpegEncoder *input_encoder_;
|
|
||||||
|
|
||||||
|
FFmpegEncoder *input_encoder_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+226
-207
@@ -29,279 +29,298 @@ extern "C" {
|
|||||||
|
|
||||||
#include "common/ffmpegutils.h"
|
#include "common/ffmpegutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
AudioProcessor::AudioProcessor()
|
AudioProcessor::AudioProcessor()
|
||||||
{
|
{
|
||||||
filter_graph_ = nullptr;
|
filter_graph_ = nullptr;
|
||||||
in_frame_ = nullptr;
|
in_frame_ = nullptr;
|
||||||
out_frame_ = nullptr;
|
out_frame_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioProcessor::~AudioProcessor()
|
AudioProcessor::~AudioProcessor()
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double tempo)
|
bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to,
|
||||||
|
double tempo)
|
||||||
{
|
{
|
||||||
if (filter_graph_) {
|
if (filter_graph_) {
|
||||||
qWarning() << "Tried to open a processor that was already open";
|
qWarning() << "Tried to open a processor that was already open";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_graph_ = avfilter_graph_alloc();
|
filter_graph_ = avfilter_graph_alloc();
|
||||||
if (!filter_graph_) {
|
if (!filter_graph_) {
|
||||||
qCritical() << "Failed to allocate filter graph";
|
qCritical() << "Failed to allocate filter graph";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
|
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
|
||||||
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
|
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
|
||||||
|
|
||||||
// Set up audio buffer args
|
// Set up audio buffer args
|
||||||
char filter_args[200];
|
char filter_args[200];
|
||||||
snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
|
snprintf(
|
||||||
1,
|
filter_args, 200,
|
||||||
from.sample_rate(),
|
"time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
|
||||||
from.sample_rate(),
|
1, from.sample_rate(), from.sample_rate(), from_fmt_,
|
||||||
from_fmt_,
|
from.channel_layout().u.mask);
|
||||||
from.channel_layout().u.mask);
|
|
||||||
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
// Create buffersrc (input)
|
// Create buffersrc (input)
|
||||||
r = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("abuffer"), "in", filter_args, nullptr, filter_graph_);
|
r = avfilter_graph_create_filter(&buffersrc_ctx_,
|
||||||
if (r < 0) {
|
avfilter_get_by_name("abuffer"), "in",
|
||||||
qCritical() << "Failed to create buffersrc:" << r;
|
filter_args, nullptr, filter_graph_);
|
||||||
Close();
|
if (r < 0) {
|
||||||
return false;
|
qCritical() << "Failed to create buffersrc:" << r;
|
||||||
}
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Store "previous" filter for linking
|
// Store "previous" filter for linking
|
||||||
AVFilterContext *previous_filter = buffersrc_ctx_;
|
AVFilterContext *previous_filter = buffersrc_ctx_;
|
||||||
|
|
||||||
// Create tempo
|
// Create tempo
|
||||||
bool create_tempo;
|
bool create_tempo;
|
||||||
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
|
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
|
||||||
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
|
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
|
||||||
// those boundaries, we need to daisychain more than one together.
|
// those boundaries, we need to daisychain more than one together.
|
||||||
double base = (tempo > 1.0) ? 2.0 : 0.5;
|
double base = (tempo > 1.0) ? 2.0 : 0.5;
|
||||||
double speed_log = log(tempo) / log(base);
|
double speed_log = log(tempo) / log(base);
|
||||||
|
|
||||||
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
|
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
|
||||||
int whole = std::floor(speed_log);
|
int whole = std::floor(speed_log);
|
||||||
|
|
||||||
// Set speed_log to the remainder
|
// Set speed_log to the remainder
|
||||||
speed_log -= whole;
|
speed_log -= whole;
|
||||||
|
|
||||||
for (int i=0;i<=whole;i++) {
|
for (int i = 0; i <= whole; i++) {
|
||||||
double filter_tempo = (i == whole) ? std::pow(base, speed_log) : base;
|
double filter_tempo = (i == whole) ? std::pow(base, speed_log) :
|
||||||
/*
|
base;
|
||||||
|
/*
|
||||||
if (qFuzzyCompare(filter_tempo, 1.0)) {
|
if (qFuzzyCompare(filter_tempo, 1.0)) {
|
||||||
// This filter would do nothing
|
// This filter would do nothing
|
||||||
continue;
|
continue;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
previous_filter = CreateTempoFilter(filter_graph_,
|
previous_filter =
|
||||||
previous_filter,
|
CreateTempoFilter(filter_graph_, previous_filter, filter_tempo);
|
||||||
filter_tempo);
|
|
||||||
|
|
||||||
if (!previous_filter) {
|
if (!previous_filter) {
|
||||||
qCritical() << "Failed to create audio tempo filter";
|
qCritical() << "Failed to create audio tempo filter";
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create conversion filter
|
// Create conversion filter
|
||||||
auto ch1=from.channel_layout();
|
auto ch1 = from.channel_layout();
|
||||||
auto ch2=to.channel_layout();
|
auto ch2 = to.channel_layout();
|
||||||
if (from.sample_rate() != to.sample_rate() || av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format()
|
if (from.sample_rate() != to.sample_rate() ||
|
||||||
|| (to.format().is_planar() && create_tempo)) { // Tempo processor automatically converts to packed,
|
av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format() ||
|
||||||
// so if the desired output is planar, it'll need
|
(to.format().is_planar() &&
|
||||||
// to be converted
|
create_tempo)) { // Tempo processor automatically converts to packed,
|
||||||
snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
|
// so if the desired output is planar, it'll need
|
||||||
av_get_sample_fmt_name(to_fmt_),
|
// to be converted
|
||||||
to.sample_rate(),
|
snprintf(filter_args, 200,
|
||||||
to.channel_layout().u.mask);
|
"sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
|
||||||
|
av_get_sample_fmt_name(to_fmt_), to.sample_rate(),
|
||||||
|
to.channel_layout().u.mask);
|
||||||
|
|
||||||
AVFilterContext *c;
|
AVFilterContext *c;
|
||||||
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"), "fmt", filter_args, nullptr, filter_graph_);
|
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"),
|
||||||
if (r < 0) {
|
"fmt", filter_args, nullptr,
|
||||||
qCritical() << "Failed to create format conversion filter:" << r << filter_args;
|
filter_graph_);
|
||||||
Close();
|
if (r < 0) {
|
||||||
return false;
|
qCritical() << "Failed to create format conversion filter:" << r
|
||||||
}
|
<< filter_args;
|
||||||
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
r = avfilter_link(previous_filter, 0, c, 0);
|
r = avfilter_link(previous_filter, 0, c, 0);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to link filters:" << r;
|
qCritical() << "Failed to link filters:" << r;
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
previous_filter = c;
|
previous_filter = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create buffersink (output)
|
// Create buffersink (output)
|
||||||
r = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph_);
|
r = avfilter_graph_create_filter(&buffersink_ctx_,
|
||||||
if (r < 0) {
|
avfilter_get_by_name("abuffersink"), "out",
|
||||||
qCritical() << "Failed to create buffersink:" << r;
|
nullptr, nullptr, filter_graph_);
|
||||||
Close();
|
if (r < 0) {
|
||||||
return false;
|
qCritical() << "Failed to create buffersink:" << r;
|
||||||
}
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
|
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to link filters:" << r;
|
qCritical() << "Failed to link filters:" << r;
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char *dump = avfilter_graph_dump(filter_graph_, nullptr);
|
char *dump = avfilter_graph_dump(filter_graph_, nullptr);
|
||||||
qDebug() << dump;
|
qDebug() << dump;
|
||||||
av_free(dump);
|
av_free(dump);
|
||||||
r = avfilter_graph_config(filter_graph_, nullptr);
|
r = avfilter_graph_config(filter_graph_, nullptr);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to configure graph:" << r;
|
qCritical() << "Failed to configure graph:" << r;
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
in_frame_ = av_frame_alloc();
|
in_frame_ = av_frame_alloc();
|
||||||
if (in_frame_) {
|
if (in_frame_) {
|
||||||
in_frame_->sample_rate = from.sample_rate();
|
in_frame_->sample_rate = from.sample_rate();
|
||||||
in_frame_->format = from_fmt_;
|
in_frame_->format = from_fmt_;
|
||||||
in_frame_->ch_layout = from.channel_layout();
|
in_frame_->ch_layout = from.channel_layout();
|
||||||
in_frame_->pts = 0;
|
in_frame_->pts = 0;
|
||||||
} else {
|
} else {
|
||||||
qCritical() << "Failed to allocate input frame";
|
qCritical() << "Failed to allocate input frame";
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_frame_ = av_frame_alloc();
|
out_frame_ = av_frame_alloc();
|
||||||
if (!out_frame_) {
|
if (!out_frame_) {
|
||||||
qCritical() << "Failed to allocate output frame";
|
qCritical() << "Failed to allocate output frame";
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
from_ = from;
|
from_ = from;
|
||||||
to_ = to;
|
to_ = to;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProcessor::Close()
|
void AudioProcessor::Close()
|
||||||
{
|
{
|
||||||
if (filter_graph_) {
|
if (filter_graph_) {
|
||||||
avfilter_graph_free(&filter_graph_);
|
avfilter_graph_free(&filter_graph_);
|
||||||
filter_graph_ = nullptr;
|
filter_graph_ = nullptr;
|
||||||
buffersrc_ctx_ = nullptr;
|
buffersrc_ctx_ = nullptr;
|
||||||
buffersink_ctx_ = nullptr;
|
buffersink_ctx_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_frame_) {
|
if (in_frame_) {
|
||||||
av_frame_free(&in_frame_);
|
av_frame_free(&in_frame_);
|
||||||
in_frame_ = nullptr;
|
in_frame_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out_frame_) {
|
if (out_frame_) {
|
||||||
av_frame_free(&out_frame_);
|
av_frame_free(&out_frame_);
|
||||||
out_frame_ = nullptr;
|
out_frame_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output)
|
int AudioProcessor::Convert(float **in, int nb_in_samples,
|
||||||
|
AudioProcessor::Buffer *output)
|
||||||
{
|
{
|
||||||
if (!IsOpen()) {
|
if (!IsOpen()) {
|
||||||
qCritical() << "Tried to convert on closed processor";
|
qCritical() << "Tried to convert on closed processor";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
if (in && nb_in_samples) {
|
if (in && nb_in_samples) {
|
||||||
// Set frame parameters
|
// Set frame parameters
|
||||||
in_frame_->nb_samples = nb_in_samples;
|
in_frame_->nb_samples = nb_in_samples;
|
||||||
for (int i=0; i<from_.channel_count(); i++) {
|
for (int i = 0; i < from_.channel_count(); i++) {
|
||||||
in_frame_->data[i] = reinterpret_cast<uint8_t*>(in[i]);
|
in_frame_->data[i] = reinterpret_cast<uint8_t *>(in[i]);
|
||||||
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
|
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_, AV_BUFFERSRC_FLAG_KEEP_REF);
|
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_,
|
||||||
if (r < 0) {
|
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
qCritical() << "Failed to add frame to buffersrc:" << r;
|
if (r < 0) {
|
||||||
return r;
|
qCritical() << "Failed to add frame to buffersrc:" << r;
|
||||||
}
|
return r;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (output) {
|
if (output) {
|
||||||
int nb_channels = to_.channel_count();
|
int nb_channels = to_.channel_count();
|
||||||
|
|
||||||
if (to_.format().is_packed()) {
|
if (to_.format().is_packed()) {
|
||||||
nb_channels = 1;
|
nb_channels = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioProcessor::Buffer &result = *output;
|
AudioProcessor::Buffer &result = *output;
|
||||||
result.resize(nb_channels);
|
result.resize(nb_channels);
|
||||||
|
|
||||||
int byte_offset = 0;
|
int byte_offset = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
av_frame_unref(out_frame_);
|
av_frame_unref(out_frame_);
|
||||||
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
|
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (r == AVERROR(EAGAIN)) {
|
if (r == AVERROR(EAGAIN)) {
|
||||||
r = 0;
|
r = 0;
|
||||||
} else {
|
} else {
|
||||||
// Handle unexpected error
|
// Handle unexpected error
|
||||||
qCritical() << "Failed to pull from buffersink:" << r;
|
qCritical() << "Failed to pull from buffersink:" << r;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nb_bytes = out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
|
int nb_bytes =
|
||||||
if (to_.format().is_packed()) {
|
out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
|
||||||
nb_bytes *= to_.channel_count();
|
if (to_.format().is_packed()) {
|
||||||
}
|
nb_bytes *= to_.channel_count();
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i<nb_channels; i++) {
|
for (int i = 0; i < nb_channels; i++) {
|
||||||
result[i].resize(byte_offset + nb_bytes);
|
result[i].resize(byte_offset + nb_bytes);
|
||||||
memcpy(result[i].data() + byte_offset, out_frame_->data[i], nb_bytes);
|
memcpy(result[i].data() + byte_offset, out_frame_->data[i],
|
||||||
}
|
nb_bytes);
|
||||||
byte_offset += nb_bytes;
|
}
|
||||||
}
|
byte_offset += nb_bytes;
|
||||||
av_frame_unref(out_frame_);
|
}
|
||||||
}
|
av_frame_unref(out_frame_);
|
||||||
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProcessor::Flush()
|
void AudioProcessor::Flush()
|
||||||
{
|
{
|
||||||
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
|
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr,
|
||||||
if (r < 0) {
|
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
qCritical() << "Failed to flush:" << r;
|
if (r < 0) {
|
||||||
}
|
qCritical() << "Failed to flush:" << r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilterContext* link, const double &tempo)
|
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph *graph,
|
||||||
|
AVFilterContext *link,
|
||||||
|
const double &tempo)
|
||||||
{
|
{
|
||||||
// Set up tempo param, which is taken as a C string
|
// Set up tempo param, which is taken as a C string
|
||||||
char speed_param[20];
|
char speed_param[20];
|
||||||
snprintf(speed_param, 20, "%f", tempo);
|
snprintf(speed_param, 20, "%f", tempo);
|
||||||
|
|
||||||
AVFilterContext* tempo_ctx = nullptr;
|
AVFilterContext *tempo_ctx = nullptr;
|
||||||
|
|
||||||
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0
|
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"),
|
||||||
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) {
|
"atempo", speed_param, nullptr,
|
||||||
return tempo_ctx;
|
graph) >= 0 &&
|
||||||
}
|
avfilter_link(link, 0, tempo_ctx, 0) == 0) {
|
||||||
|
return tempo_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-25
@@ -31,52 +31,63 @@ extern "C" {
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
class AudioProcessor
|
class AudioProcessor {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
AudioProcessor();
|
AudioProcessor();
|
||||||
|
|
||||||
~AudioProcessor();
|
~AudioProcessor();
|
||||||
|
|
||||||
DISABLE_COPY_MOVE(AudioProcessor)
|
DISABLE_COPY_MOVE(AudioProcessor)
|
||||||
|
|
||||||
bool Open(const AudioParams &from, const AudioParams &to, double tempo = 1.0);
|
bool Open(const AudioParams &from, const AudioParams &to,
|
||||||
|
double tempo = 1.0);
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
bool IsOpen() const { return filter_graph_; }
|
bool IsOpen() const
|
||||||
|
{
|
||||||
|
return filter_graph_;
|
||||||
|
}
|
||||||
|
|
||||||
using Buffer = QVector<QByteArray>;
|
using Buffer = QVector<QByteArray>;
|
||||||
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
|
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
|
||||||
|
|
||||||
void Flush();
|
void Flush();
|
||||||
|
|
||||||
const AudioParams &from() const { return from_; }
|
const AudioParams &from() const
|
||||||
const AudioParams &to() const { return to_; }
|
{
|
||||||
|
return from_;
|
||||||
|
}
|
||||||
|
const AudioParams &to() const
|
||||||
|
{
|
||||||
|
return to_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static AVFilterContext* CreateTempoFilter(AVFilterGraph *graph, AVFilterContext *link, const double& tempo);
|
static AVFilterContext *CreateTempoFilter(AVFilterGraph *graph,
|
||||||
|
AVFilterContext *link,
|
||||||
|
const double &tempo);
|
||||||
|
|
||||||
AVFilterGraph* filter_graph_;
|
AVFilterGraph *filter_graph_;
|
||||||
|
|
||||||
AVFilterContext* buffersrc_ctx_;
|
AVFilterContext *buffersrc_ctx_;
|
||||||
|
|
||||||
AVFilterContext* buffersink_ctx_;
|
AVFilterContext *buffersink_ctx_;
|
||||||
|
|
||||||
AudioParams from_;
|
AudioParams from_;
|
||||||
AVSampleFormat from_fmt_;
|
AVSampleFormat from_fmt_;
|
||||||
|
|
||||||
AudioParams to_;
|
AudioParams to_;
|
||||||
AVSampleFormat to_fmt_;
|
AVSampleFormat to_fmt_;
|
||||||
|
|
||||||
AVFrame *in_frame_;
|
AVFrame *in_frame_;
|
||||||
|
|
||||||
AVFrame *out_frame_;
|
|
||||||
|
|
||||||
|
AVFrame *out_frame_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+371
-315
@@ -25,480 +25,536 @@
|
|||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8);
|
const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8);
|
||||||
const rational AudioVisualWaveform::kMaximumSampleRate = 1024;
|
const rational AudioVisualWaveform::kMaximumSampleRate = 1024;
|
||||||
|
|
||||||
AudioVisualWaveform::AudioVisualWaveform() :
|
AudioVisualWaveform::AudioVisualWaveform()
|
||||||
channels_(0)
|
: channels_(0)
|
||||||
{
|
{
|
||||||
for (rational i=kMinimumSampleRate; i<=kMaximumSampleRate; i*=2) {
|
for (rational i = kMinimumSampleRate; i <= kMaximumSampleRate; i *= 2) {
|
||||||
mipmapped_data_.insert({i, Sample()});
|
mipmapped_data_.insert({ i, Sample() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational &start, double target_rate, Sample& data, size_t &start_index, size_t &samples_length)
|
void AudioVisualWaveform::OverwriteSamplesFromBuffer(
|
||||||
|
const SampleBuffer &samples, int sample_rate, const rational &start,
|
||||||
|
double target_rate, Sample &data, size_t &start_index,
|
||||||
|
size_t &samples_length)
|
||||||
{
|
{
|
||||||
start_index = time_to_samples(start, target_rate);
|
start_index = time_to_samples(start, target_rate);
|
||||||
samples_length = time_to_samples(static_cast<double>(samples.sample_count()) / static_cast<double>(sample_rate), target_rate);
|
samples_length =
|
||||||
|
time_to_samples(static_cast<double>(samples.sample_count()) /
|
||||||
|
static_cast<double>(sample_rate),
|
||||||
|
target_rate);
|
||||||
|
|
||||||
size_t end_index = start_index + samples_length;
|
size_t end_index = start_index + samples_length;
|
||||||
if (data.size() < end_index) {
|
if (data.size() < end_index) {
|
||||||
data.resize(end_index);
|
data.resize(end_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
double chunk_size = double(sample_rate) / double(target_rate);
|
double chunk_size = double(sample_rate) / double(target_rate);
|
||||||
|
|
||||||
for (size_t i=0; i<samples_length; i+=channels_) {
|
for (size_t i = 0; i < samples_length; i += channels_) {
|
||||||
size_t src_start = qRound((double(i) * chunk_size)) / channels_;
|
size_t src_start = qRound((double(i) * chunk_size)) / channels_;
|
||||||
size_t src_end = qMin(size_t(qRound64((double(i + channels_) * chunk_size))) / channels_, samples.sample_count());
|
size_t src_end = qMin(
|
||||||
|
size_t(qRound64((double(i + channels_) * chunk_size))) / channels_,
|
||||||
|
samples.sample_count());
|
||||||
|
|
||||||
Sample summary = SumSamples(samples,
|
Sample summary = SumSamples(samples, src_start, src_end - src_start);
|
||||||
src_start,
|
|
||||||
src_end - src_start);
|
|
||||||
|
|
||||||
memcpy(&data.data()[i + start_index],
|
memcpy(&data.data()[i + start_index], summary.data(),
|
||||||
summary.data(),
|
summary.size() * sizeof(SamplePerChannel));
|
||||||
summary.size() * sizeof(SamplePerChannel));
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::Sample &input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational &start, double output_rate, AudioVisualWaveform::Sample &output_data)
|
void AudioVisualWaveform::OverwriteSamplesFromMipmap(
|
||||||
|
const AudioVisualWaveform::Sample &input, double input_sample_rate,
|
||||||
|
size_t &input_start, size_t &input_length, const rational &start,
|
||||||
|
double output_rate, AudioVisualWaveform::Sample &output_data)
|
||||||
{
|
{
|
||||||
size_t start_index = time_to_samples(start, output_rate);
|
size_t start_index = time_to_samples(start, output_rate);
|
||||||
size_t samples_length = time_to_samples(static_cast<double>(input_length / channels_) / input_sample_rate, output_rate);
|
size_t samples_length = time_to_samples(
|
||||||
|
static_cast<double>(input_length / channels_) / input_sample_rate,
|
||||||
|
output_rate);
|
||||||
|
|
||||||
size_t end_index = start_index + samples_length;
|
size_t end_index = start_index + samples_length;
|
||||||
if (output_data.size() < end_index) {
|
if (output_data.size() < end_index) {
|
||||||
output_data.resize(end_index);
|
output_data.resize(end_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
|
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
|
||||||
size_t chunk_size = input_sample_rate / output_rate;
|
size_t chunk_size = input_sample_rate / output_rate;
|
||||||
|
|
||||||
for (size_t i=0; i<samples_length; i+=channels_) {
|
for (size_t i = 0; i < samples_length; i += channels_) {
|
||||||
Sample summary = ReSumSamples(&input.data()[input_start + (i*chunk_size)], chunk_size * channels_, channels_);
|
Sample summary =
|
||||||
|
ReSumSamples(&input.data()[input_start + (i * chunk_size)],
|
||||||
|
chunk_size * channels_, channels_);
|
||||||
|
|
||||||
memcpy(&output_data.data()[i + start_index],
|
memcpy(&output_data.data()[i + start_index], summary.data(),
|
||||||
summary.data(),
|
summary.size() * sizeof(SamplePerChannel));
|
||||||
summary.size() * sizeof(SamplePerChannel));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
input_start = start_index;
|
input_start = start_index;
|
||||||
input_length = samples_length;
|
input_length = samples_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::ValidateVirtualStart(const rational &new_start)
|
void AudioVisualWaveform::ValidateVirtualStart(const rational &new_start)
|
||||||
{
|
{
|
||||||
if (length_ == 0) {
|
if (length_ == 0) {
|
||||||
virtual_start_ = new_start;
|
virtual_start_ = new_start;
|
||||||
} else if (virtual_start_ > new_start) {
|
} else if (virtual_start_ > new_start) {
|
||||||
TrimIn(new_start - virtual_start_);
|
TrimIn(new_start - virtual_start_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational &start)
|
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples,
|
||||||
|
int sample_rate,
|
||||||
|
const rational &start)
|
||||||
{
|
{
|
||||||
if (!channels_) {
|
if (!channels_) {
|
||||||
qWarning() << "Failed to write samples - channel count is zero";
|
qWarning() << "Failed to write samples - channel count is zero";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidateVirtualStart(start);
|
ValidateVirtualStart(start);
|
||||||
|
|
||||||
// Process the largest mipmap directly for the samples
|
// Process the largest mipmap directly for the samples
|
||||||
auto current_mipmap = mipmapped_data_.rbegin();
|
auto current_mipmap = mipmapped_data_.rbegin();
|
||||||
size_t input_start, input_length;
|
size_t input_start, input_length;
|
||||||
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_, current_mipmap->first.toDouble(), current_mipmap->second, input_start, input_length);
|
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_,
|
||||||
|
current_mipmap->first.toDouble(),
|
||||||
|
current_mipmap->second, input_start,
|
||||||
|
input_length);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// For each smaller mipmap, we just process from the mipmap before it, making each one
|
// For each smaller mipmap, we just process from the mipmap before it, making each one
|
||||||
// exponentially faster to create
|
// exponentially faster to create
|
||||||
auto previous_mipmap = current_mipmap;
|
auto previous_mipmap = current_mipmap;
|
||||||
current_mipmap++;
|
current_mipmap++;
|
||||||
if (current_mipmap == mipmapped_data_.rend()) {
|
if (current_mipmap == mipmapped_data_.rend()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
OverwriteSamplesFromMipmap(previous_mipmap->second, previous_mipmap->first.toDouble(),
|
OverwriteSamplesFromMipmap(
|
||||||
input_start, input_length, start - virtual_start_, current_mipmap->first.toDouble(),
|
previous_mipmap->second, previous_mipmap->first.toDouble(),
|
||||||
current_mipmap->second);
|
input_start, input_length, start - virtual_start_,
|
||||||
}
|
current_mipmap->first.toDouble(), current_mipmap->second);
|
||||||
|
}
|
||||||
|
|
||||||
rational sample_length(samples.sample_count(), sample_rate);
|
rational sample_length(samples.sample_count(), sample_rate);
|
||||||
length_ = qMax(length_, start + sample_length);
|
length_ = qMax(length_, start + sample_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const rational &dest, const rational& offset, const rational& length)
|
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums,
|
||||||
|
const rational &dest,
|
||||||
|
const rational &offset,
|
||||||
|
const rational &length)
|
||||||
{
|
{
|
||||||
ValidateVirtualStart(dest);
|
ValidateVirtualStart(dest);
|
||||||
|
|
||||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||||
rational rate = it->first;
|
rational rate = it->first;
|
||||||
|
|
||||||
Sample& our_arr = it->second;
|
Sample &our_arr = it->second;
|
||||||
const Sample& their_arr = sums.mipmapped_data_.at(rate);
|
const Sample &their_arr = sums.mipmapped_data_.at(rate);
|
||||||
|
|
||||||
double rate_dbl = rate.toDouble();
|
double rate_dbl = rate.toDouble();
|
||||||
|
|
||||||
// Get our destination sample
|
// Get our destination sample
|
||||||
size_t our_start_index = time_to_samples(dest - virtual_start_, rate_dbl);
|
size_t our_start_index =
|
||||||
|
time_to_samples(dest - virtual_start_, rate_dbl);
|
||||||
|
|
||||||
// Get our source sample
|
// Get our source sample
|
||||||
size_t their_start_index = time_to_samples(offset, rate_dbl);
|
size_t their_start_index = time_to_samples(offset, rate_dbl);
|
||||||
if (their_start_index >= their_arr.size()) {
|
if (their_start_index >= their_arr.size()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine how much we're copying
|
// Determine how much we're copying
|
||||||
size_t copy_len = their_arr.size() - their_start_index;
|
size_t copy_len = their_arr.size() - their_start_index;
|
||||||
if (!length.isNull()) {
|
if (!length.isNull()) {
|
||||||
copy_len = qMin(copy_len, time_to_samples(length, rate_dbl));
|
copy_len = qMin(copy_len, time_to_samples(length, rate_dbl));
|
||||||
if (copy_len == 0) {
|
if (copy_len == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine end index of our array
|
// Determine end index of our array
|
||||||
size_t end_index = our_start_index + copy_len;
|
size_t end_index = our_start_index + copy_len;
|
||||||
if (our_arr.size() < end_index) {
|
if (our_arr.size() < end_index) {
|
||||||
our_arr.resize(end_index);
|
our_arr.resize(end_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel),
|
memcpy(reinterpret_cast<char *>(our_arr.data()) +
|
||||||
reinterpret_cast<const char*>(their_arr.data()) + their_start_index * sizeof(SamplePerChannel),
|
our_start_index * sizeof(SamplePerChannel),
|
||||||
copy_len * sizeof(SamplePerChannel));
|
reinterpret_cast<const char *>(their_arr.data()) +
|
||||||
}
|
their_start_index * sizeof(SamplePerChannel),
|
||||||
|
copy_len * sizeof(SamplePerChannel));
|
||||||
|
}
|
||||||
|
|
||||||
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset : length));
|
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset :
|
||||||
|
length));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational &length)
|
void AudioVisualWaveform::OverwriteSilence(const rational &start,
|
||||||
|
const rational &length)
|
||||||
{
|
{
|
||||||
ValidateVirtualStart(start);
|
ValidateVirtualStart(start);
|
||||||
|
|
||||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||||
rational rate = it->first;
|
rational rate = it->first;
|
||||||
|
|
||||||
Sample& our_arr = it->second;
|
Sample &our_arr = it->second;
|
||||||
|
|
||||||
double rate_dbl = rate.toDouble();
|
double rate_dbl = rate.toDouble();
|
||||||
|
|
||||||
// Get our destination sample
|
// Get our destination sample
|
||||||
size_t our_start_index = time_to_samples(start - virtual_start_, rate_dbl);
|
size_t our_start_index =
|
||||||
size_t our_length_index = time_to_samples(length, rate_dbl);
|
time_to_samples(start - virtual_start_, rate_dbl);
|
||||||
size_t our_end_index = our_start_index + our_length_index;
|
size_t our_length_index = time_to_samples(length, rate_dbl);
|
||||||
|
size_t our_end_index = our_start_index + our_length_index;
|
||||||
|
|
||||||
if (our_arr.size() < our_end_index) {
|
if (our_arr.size() < our_end_index) {
|
||||||
our_arr.resize(our_end_index);
|
our_arr.resize(our_end_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel), 0, our_length_index * sizeof(SamplePerChannel));
|
memset(reinterpret_cast<char *>(our_arr.data()) +
|
||||||
}
|
our_start_index * sizeof(SamplePerChannel),
|
||||||
|
0, our_length_index * sizeof(SamplePerChannel));
|
||||||
|
}
|
||||||
|
|
||||||
length_ = qMax(length_, start + length);
|
length_ = qMax(length_, start + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::TrimIn(rational length)
|
void AudioVisualWaveform::TrimIn(rational length)
|
||||||
{
|
{
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual_start_ += length;
|
virtual_start_ += length;
|
||||||
|
|
||||||
bool negative = (length < 0);
|
bool negative = (length < 0);
|
||||||
if (negative) {
|
if (negative) {
|
||||||
length = -length;
|
length = -length;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||||
rational rate = it->first;
|
rational rate = it->first;
|
||||||
double rate_dbl = rate.toDouble();
|
double rate_dbl = rate.toDouble();
|
||||||
Sample& data = it->second;
|
Sample &data = it->second;
|
||||||
|
|
||||||
size_t chop_length = time_to_samples(length, rate_dbl);
|
size_t chop_length = time_to_samples(length, rate_dbl);
|
||||||
if (chop_length == 0) {
|
if (chop_length == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!negative) {
|
if (!negative) {
|
||||||
data = Sample(data.begin() + chop_length, data.end());
|
data = Sample(data.begin() + chop_length, data.end());
|
||||||
} else {
|
} else {
|
||||||
data.insert(data.begin(), chop_length, SamplePerChannel());
|
data.insert(data.begin(), chop_length, SamplePerChannel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
length_ = qMax(rational(0), length_ - length);
|
length_ = qMax(rational(0), length_ - length);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const
|
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const
|
||||||
{
|
{
|
||||||
AudioVisualWaveform mid = *this;
|
AudioVisualWaveform mid = *this;
|
||||||
|
|
||||||
mid.TrimIn(offset - virtual_start_);
|
mid.TrimIn(offset - virtual_start_);
|
||||||
|
|
||||||
return mid;
|
return mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset, const rational &length) const
|
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset,
|
||||||
|
const rational &length) const
|
||||||
{
|
{
|
||||||
AudioVisualWaveform mid = *this;
|
AudioVisualWaveform mid = *this;
|
||||||
|
|
||||||
mid.TrimRange(offset - virtual_start_, length);
|
mid.TrimRange(offset - virtual_start_, length);
|
||||||
|
|
||||||
return mid;
|
return mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::Resize(const rational &length)
|
void AudioVisualWaveform::Resize(const rational &length)
|
||||||
{
|
{
|
||||||
if (length_ == length) {
|
if (length_ == length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||||
rational rate = it->first;
|
rational rate = it->first;
|
||||||
double rate_dbl = rate.toDouble();
|
double rate_dbl = rate.toDouble();
|
||||||
Sample& data = it->second;
|
Sample &data = it->second;
|
||||||
|
|
||||||
size_t chop_length = time_to_samples(length, rate_dbl);
|
size_t chop_length = time_to_samples(length, rate_dbl);
|
||||||
|
|
||||||
data.resize(chop_length);
|
data.resize(chop_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
length_ = length;
|
length_ = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::TrimRange(const rational &in, const rational &length)
|
void AudioVisualWaveform::TrimRange(const rational &in, const rational &length)
|
||||||
{
|
{
|
||||||
TrimIn(in);
|
TrimIn(in);
|
||||||
Resize(length);
|
Resize(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const rational &start, const rational &length) const
|
AudioVisualWaveform::Sample
|
||||||
|
AudioVisualWaveform::GetSummaryFromTime(const rational &start,
|
||||||
|
const rational &length) const
|
||||||
{
|
{
|
||||||
// Find mipmap that requires
|
// Find mipmap that requires
|
||||||
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
|
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
|
||||||
|
|
||||||
double rate_dbl = using_mipmap->first.toDouble();
|
double rate_dbl = using_mipmap->first.toDouble();
|
||||||
|
|
||||||
size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl);
|
size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl);
|
||||||
size_t sample_length = time_to_samples(length, rate_dbl);
|
size_t sample_length = time_to_samples(length, rate_dbl);
|
||||||
|
|
||||||
const Sample &mipmap_data = using_mipmap->second;
|
const Sample &mipmap_data = using_mipmap->second;
|
||||||
|
|
||||||
// Determine if the array actually has this sample
|
// Determine if the array actually has this sample
|
||||||
sample_length = qMin(sample_length, mipmap_data.size() - start_sample);
|
sample_length = qMin(sample_length, mipmap_data.size() - start_sample);
|
||||||
|
|
||||||
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
|
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
|
||||||
// array and nothing can be returned.
|
// array and nothing can be returned.
|
||||||
if (sample_length > 0) {
|
if (sample_length > 0) {
|
||||||
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length, channels_);
|
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length,
|
||||||
}
|
channels_);
|
||||||
|
}
|
||||||
|
|
||||||
// Return null samples
|
// Return null samples
|
||||||
return AudioVisualWaveform::Sample(channel_count(), {0, 0});
|
return AudioVisualWaveform::Sample(channel_count(), { 0, 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &max_val)
|
void ExpandMinMaxChannel(const float *a, size_t length, float &min_val,
|
||||||
|
float &max_val)
|
||||||
{
|
{
|
||||||
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
||||||
// SSE optimized
|
// SSE optimized
|
||||||
|
|
||||||
// load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits)
|
// load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits)
|
||||||
__m128 max = _mm_loadu_ps(a);
|
__m128 max = _mm_loadu_ps(a);
|
||||||
__m128 min = _mm_loadu_ps(a);
|
__m128 min = _mm_loadu_ps(a);
|
||||||
|
|
||||||
// loop over 'a' and compare current elements with min and max 4 by 4.
|
// loop over 'a' and compare current elements with min and max 4 by 4.
|
||||||
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
|
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
|
||||||
for(size_t i = 4; i < length-4; i+=4) {
|
for (size_t i = 4; i < length - 4; i += 4) {
|
||||||
__m128 cur = _mm_loadu_ps(a + i);
|
__m128 cur = _mm_loadu_ps(a + i);
|
||||||
max = _mm_max_ps(max, cur);
|
max = _mm_max_ps(max, cur);
|
||||||
min = _mm_min_ps(min, cur);
|
min = _mm_min_ps(min, cur);
|
||||||
}
|
}
|
||||||
// so we read the last 4 (or less) elements in a safe manner.
|
// so we read the last 4 (or less) elements in a safe manner.
|
||||||
__m128 cur = _mm_loadu_ps(a + length - 4);
|
__m128 cur = _mm_loadu_ps(a + length - 4);
|
||||||
max = _mm_max_ps(max, cur);
|
max = _mm_max_ps(max, cur);
|
||||||
min = _mm_min_ps(min, cur);
|
min = _mm_min_ps(min, cur);
|
||||||
// this potentially overlaps up to the last 3 elements but it's not an issue.
|
// this potentially overlaps up to the last 3 elements but it's not an issue.
|
||||||
|
|
||||||
// min and max will contain 4 min and max. To get the absolute min and max
|
// min and max will contain 4 min and max. To get the absolute min and max
|
||||||
// we need to compare the 4 values over themselves by shuffling each time.
|
// we need to compare the 4 values over themselves by shuffling each time.
|
||||||
for (size_t i = 0; i < 3; i++) {
|
for (size_t i = 0; i < 3; i++) {
|
||||||
max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93));
|
max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93));
|
||||||
min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93));
|
min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93));
|
||||||
}
|
}
|
||||||
// now min and max contain 4 identical items each representing min and max value respectively.
|
// now min and max contain 4 identical items each representing min and max value respectively.
|
||||||
|
|
||||||
// and we store the first one into a float variable.
|
// and we store the first one into a float variable.
|
||||||
_mm_store_ss(&max_val, max);
|
_mm_store_ss(&max_val, max);
|
||||||
_mm_store_ss(&min_val, min);
|
_mm_store_ss(&min_val, min);
|
||||||
// I bet you don't find annotated low level code very often.
|
// I bet you don't find annotated low level code very often.
|
||||||
#else
|
#else
|
||||||
// Standard unoptimized function
|
// Standard unoptimized function
|
||||||
for (size_t i=0; i<length; i++) {
|
for (size_t i = 0; i < length; i++) {
|
||||||
min_val = std::min(min_val, a[i]);
|
min_val = std::min(min_val, a[i]);
|
||||||
max_val = std::max(max_val, a[i]);
|
max_val = std::max(max_val, a[i]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index, size_t length)
|
AudioVisualWaveform::Sample
|
||||||
|
AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index,
|
||||||
|
size_t length)
|
||||||
{
|
{
|
||||||
int channels = samples.audio_params().channel_count();
|
int channels = samples.audio_params().channel_count();
|
||||||
AudioVisualWaveform::Sample summed_samples(channels);
|
AudioVisualWaveform::Sample summed_samples(channels);
|
||||||
|
|
||||||
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) {
|
for (int channel = 0; channel < samples.audio_params().channel_count();
|
||||||
ExpandMinMaxChannel(samples.data(channel) + start_index, length, summed_samples[channel].min, summed_samples[channel].max);
|
channel++) {
|
||||||
}
|
ExpandMinMaxChannel(samples.data(channel) + start_index, length,
|
||||||
|
summed_samples[channel].min,
|
||||||
|
summed_samples[channel].max);
|
||||||
|
}
|
||||||
|
|
||||||
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
|
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
|
||||||
// for (size_t i=start_index; i<end_index; i++) {
|
// for (size_t i=start_index; i<end_index; i++) {
|
||||||
// ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]);
|
// ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return summed_samples;
|
return summed_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioVisualWaveform::Sample AudioVisualWaveform::ReSumSamples(const SamplePerChannel* samples,
|
AudioVisualWaveform::Sample
|
||||||
size_t nb_samples,
|
AudioVisualWaveform::ReSumSamples(const SamplePerChannel *samples,
|
||||||
int nb_channels)
|
size_t nb_samples, int nb_channels)
|
||||||
{
|
{
|
||||||
AudioVisualWaveform::Sample summed_samples(nb_channels);
|
AudioVisualWaveform::Sample summed_samples(nb_channels);
|
||||||
|
|
||||||
for (size_t i=0;i<nb_samples;i+=nb_channels) {
|
for (size_t i = 0; i < nb_samples; i += nb_channels) {
|
||||||
for (int j=0;j<nb_channels;j++) {
|
for (int j = 0; j < nb_channels; j++) {
|
||||||
const AudioVisualWaveform::SamplePerChannel& sample = samples[i + j];
|
const AudioVisualWaveform::SamplePerChannel &sample =
|
||||||
|
samples[i + j];
|
||||||
|
|
||||||
if (sample.min < summed_samples[j].min) {
|
if (sample.min < summed_samples[j].min) {
|
||||||
summed_samples[j].min = sample.min;
|
summed_samples[j].min = sample.min;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample.max > summed_samples[j].max) {
|
if (sample.max > summed_samples[j].max) {
|
||||||
summed_samples[j].max = sample.max;
|
summed_samples[j].max = sample.max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return summed_samples;
|
return summed_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> inline int round_away_from_zero(T t)
|
||||||
inline int round_away_from_zero(T t)
|
|
||||||
{
|
{
|
||||||
return (t < 0) ? std::floor(t) : std::ceil(t);
|
return (t < 0) ? std::floor(t) : std::ceil(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample& sample, int x, int y, int height, bool rectified)
|
void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample &sample,
|
||||||
|
int x, int y, int height, bool rectified)
|
||||||
{
|
{
|
||||||
if (sample.empty()) {
|
if (sample.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int channel_height = height / sample.size();
|
int channel_height = height / sample.size();
|
||||||
int channel_half_height = channel_height / 2;
|
int channel_half_height = channel_height / 2;
|
||||||
|
|
||||||
for (size_t i=0;i<sample.size();i++) {
|
for (size_t i = 0; i < sample.size(); i++) {
|
||||||
float max = qMin(sample.at(i).max, 1.0f);
|
float max = qMin(sample.at(i).max, 1.0f);
|
||||||
float min = qMax(sample.at(i).min, -1.0f);
|
float min = qMax(sample.at(i).min, -1.0f);
|
||||||
|
|
||||||
if (rectified) {
|
if (rectified) {
|
||||||
int channel_bottom = y + channel_height * (i + 1);
|
int channel_bottom = y + channel_height * (i + 1);
|
||||||
|
|
||||||
int diff = round_away_from_zero((max - min) * channel_half_height);
|
int diff = round_away_from_zero((max - min) * channel_half_height);
|
||||||
|
|
||||||
painter->drawLine(x,
|
painter->drawLine(x, channel_bottom - diff, x, channel_bottom);
|
||||||
channel_bottom - diff,
|
} else {
|
||||||
x,
|
int channel_mid = y + channel_height * i + channel_half_height;
|
||||||
channel_bottom);
|
|
||||||
} else {
|
|
||||||
int channel_mid = y + channel_height * i + channel_half_height;
|
|
||||||
|
|
||||||
// We subtract the sample so that positive Y values go up on the screen rather than down,
|
// We subtract the sample so that positive Y values go up on the screen rather than down,
|
||||||
// which is how waveforms are usually rendered
|
// which is how waveforms are usually rendered
|
||||||
painter->drawLine(x,
|
painter->drawLine(
|
||||||
channel_mid - round_away_from_zero(min * static_cast<float>(channel_half_height)),
|
x,
|
||||||
x,
|
channel_mid -
|
||||||
channel_mid - round_away_from_zero(max * static_cast<float>(channel_half_height)));
|
round_away_from_zero(
|
||||||
}
|
min * static_cast<float>(channel_half_height)),
|
||||||
}
|
x,
|
||||||
|
channel_mid -
|
||||||
|
round_away_from_zero(
|
||||||
|
max * static_cast<float>(channel_half_height)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, const double& scale, const AudioVisualWaveform &samples, const rational& start_time)
|
void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect &rect,
|
||||||
|
const double &scale,
|
||||||
|
const AudioVisualWaveform &samples,
|
||||||
|
const rational &start_time)
|
||||||
{
|
{
|
||||||
if (samples.mipmapped_data_.empty()) {
|
if (samples.mipmapped_data_.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto using_mipmap = samples.GetMipmapForScale(scale);
|
auto using_mipmap = samples.GetMipmapForScale(scale);
|
||||||
|
|
||||||
rational rate = using_mipmap->first;
|
rational rate = using_mipmap->first;
|
||||||
double rate_dbl = rate.toDouble();
|
double rate_dbl = rate.toDouble();
|
||||||
const Sample& arr = using_mipmap->second;
|
const Sample &arr = using_mipmap->second;
|
||||||
|
|
||||||
size_t start_sample_index = samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl);
|
size_t start_sample_index =
|
||||||
|
samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl);
|
||||||
|
|
||||||
if (start_sample_index >= arr.size()) {
|
if (start_sample_index >= arr.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t next_sample_index = start_sample_index;
|
size_t next_sample_index = start_sample_index;
|
||||||
size_t sample_index;
|
size_t sample_index;
|
||||||
|
|
||||||
Sample summary;
|
Sample summary;
|
||||||
size_t summary_index = -1;
|
size_t summary_index = -1;
|
||||||
|
|
||||||
const QRect& viewport = painter->viewport();
|
const QRect &viewport = painter->viewport();
|
||||||
QPoint top_left = painter->transform().map(viewport.topLeft());
|
QPoint top_left = painter->transform().map(viewport.topLeft());
|
||||||
|
|
||||||
size_t start = qMax(rect.x(), -top_left.x());
|
size_t start = qMax(rect.x(), -top_left.x());
|
||||||
size_t end = qMin(rect.right(), -top_left.x() + viewport.width());
|
size_t end = qMin(rect.right(), -top_left.x() + viewport.width());
|
||||||
|
|
||||||
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
|
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
|
||||||
|
|
||||||
for (size_t i=start;i<end;i++) {
|
for (size_t i = start; i < end; i++) {
|
||||||
sample_index = next_sample_index;
|
sample_index = next_sample_index;
|
||||||
|
|
||||||
if (sample_index == arr.size()) {
|
if (sample_index == arr.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_sample_index = std::min(arr.size(),
|
next_sample_index = std::min(
|
||||||
size_t(start_sample_index + std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) / scale) * samples.channel_count()));
|
arr.size(),
|
||||||
|
size_t(start_sample_index +
|
||||||
|
std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) /
|
||||||
|
scale) *
|
||||||
|
samples.channel_count()));
|
||||||
|
|
||||||
if (summary_index != sample_index) {
|
if (summary_index != sample_index) {
|
||||||
summary = AudioVisualWaveform::ReSumSamples(&arr.at(sample_index),
|
summary = AudioVisualWaveform::ReSumSamples(
|
||||||
qMax(size_t(samples.channel_count()), next_sample_index - sample_index),
|
&arr.at(sample_index),
|
||||||
samples.channel_count());
|
qMax(size_t(samples.channel_count()),
|
||||||
summary_index = sample_index;
|
next_sample_index - sample_index),
|
||||||
}
|
samples.channel_count());
|
||||||
|
summary_index = sample_index;
|
||||||
|
}
|
||||||
|
|
||||||
DrawSample(painter, summary, i, rect.y(), rect.height(), rectified);
|
DrawSample(painter, summary, i, rect.y(), rect.height(), rectified);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioVisualWaveform::time_to_samples(const rational &time, double sample_rate) const
|
size_t AudioVisualWaveform::time_to_samples(const rational &time,
|
||||||
|
double sample_rate) const
|
||||||
{
|
{
|
||||||
return time_to_samples(time.toDouble(), sample_rate);
|
return time_to_samples(time.toDouble(), sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioVisualWaveform::time_to_samples(const double &time, double sample_rate) const
|
size_t AudioVisualWaveform::time_to_samples(const double &time,
|
||||||
|
double sample_rate) const
|
||||||
{
|
{
|
||||||
return std::floor(time * sample_rate) * channels_;
|
return std::floor(time * sample_rate) * channels_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<rational, AudioVisualWaveform::Sample>::const_iterator AudioVisualWaveform::GetMipmapForScale(double scale) const
|
std::map<rational, AudioVisualWaveform::Sample>::const_iterator
|
||||||
|
AudioVisualWaveform::GetMipmapForScale(double scale) const
|
||||||
{
|
{
|
||||||
// Find largest mipmap for this scale (or the largest if we don't find one sufficient)
|
// Find largest mipmap for this scale (or the largest if we don't find one sufficient)
|
||||||
for (auto it=mipmapped_data_.cbegin(); it!=mipmapped_data_.cend(); it++) {
|
for (auto it = mipmapped_data_.cbegin(); it != mipmapped_data_.cend();
|
||||||
if (it->first.toDouble() >= scale) {
|
it++) {
|
||||||
return it;
|
if (it->first.toDouble() >= scale) {
|
||||||
}
|
return it;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We don't have a mipmap large enough for this scale, so just return the largest we have
|
// We don't have a mipmap large enough for this scale, so just return the largest we have
|
||||||
return std::prev(mipmapped_data_.cend());
|
return std::prev(mipmapped_data_.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
@@ -37,38 +38,39 @@ using namespace core;
|
|||||||
*/
|
*/
|
||||||
class AudioVisualWaveform {
|
class AudioVisualWaveform {
|
||||||
public:
|
public:
|
||||||
AudioVisualWaveform();
|
AudioVisualWaveform();
|
||||||
|
|
||||||
struct SamplePerChannel {
|
struct SamplePerChannel {
|
||||||
float min;
|
float min;
|
||||||
float max;
|
float max;
|
||||||
};
|
};
|
||||||
|
|
||||||
using Sample = std::vector<SamplePerChannel>;
|
using Sample = std::vector<SamplePerChannel>;
|
||||||
|
|
||||||
int channel_count() const
|
int channel_count() const
|
||||||
{
|
{
|
||||||
return channels_;
|
return channels_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_channel_count(int channels)
|
void set_channel_count(int channels)
|
||||||
{
|
{
|
||||||
channels_ = channels;
|
channels_ = channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rational& length() const
|
const rational &length() const
|
||||||
{
|
{
|
||||||
return length_;
|
return length_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Writes samples into the visual waveform buffer
|
* @brief Writes samples into the visual waveform buffer
|
||||||
*
|
*
|
||||||
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
|
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
|
||||||
*/
|
*/
|
||||||
void OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational& start = 0);
|
void OverwriteSamples(const SampleBuffer &samples, int sample_rate,
|
||||||
|
const rational &start = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Replaces sums at a certain range in this visual waveform
|
* @brief Replaces sums at a certain range in this visual waveform
|
||||||
*
|
*
|
||||||
* @param sums
|
* @param sums
|
||||||
@@ -87,53 +89,70 @@ public:
|
|||||||
*
|
*
|
||||||
* Maximum length of `sums` to overwrite with.
|
* Maximum length of `sums` to overwrite with.
|
||||||
*/
|
*/
|
||||||
void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = 0, const rational &length = 0);
|
void OverwriteSums(const AudioVisualWaveform &sums, const rational &dest,
|
||||||
|
const rational &offset = 0, const rational &length = 0);
|
||||||
|
|
||||||
void OverwriteSilence(const rational &start, const rational &length);
|
void OverwriteSilence(const rational &start, const rational &length);
|
||||||
|
|
||||||
void TrimIn(rational length);
|
void TrimIn(rational length);
|
||||||
|
|
||||||
AudioVisualWaveform Mid(const rational &offset) const;
|
AudioVisualWaveform Mid(const rational &offset) const;
|
||||||
AudioVisualWaveform Mid(const rational &offset, const rational &length) const;
|
AudioVisualWaveform Mid(const rational &offset,
|
||||||
|
const rational &length) const;
|
||||||
|
|
||||||
void Resize(const rational &length);
|
void Resize(const rational &length);
|
||||||
|
|
||||||
void TrimRange(const rational &in, const rational &length);
|
void TrimRange(const rational &in, const rational &length);
|
||||||
|
|
||||||
Sample GetSummaryFromTime(const rational& start, const rational& length) const;
|
Sample GetSummaryFromTime(const rational &start,
|
||||||
|
const rational &length) const;
|
||||||
|
|
||||||
static Sample SumSamples(const SampleBuffer &samples, size_t start_index, size_t length);
|
static Sample SumSamples(const SampleBuffer &samples, size_t start_index,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
static Sample ReSumSamples(const SamplePerChannel *samples, size_t nb_samples, int nb_channels);
|
static Sample ReSumSamples(const SamplePerChannel *samples,
|
||||||
|
size_t nb_samples, int nb_channels);
|
||||||
|
|
||||||
static void DrawSample(QPainter* painter, const Sample &sample, int x, int y, int height, bool rectified);
|
static void DrawSample(QPainter *painter, const Sample &sample, int x,
|
||||||
|
int y, int height, bool rectified);
|
||||||
|
|
||||||
static void DrawWaveform(QPainter* painter, const QRect &rect, const double &scale, const AudioVisualWaveform& samples, const rational &start_time);
|
static void DrawWaveform(QPainter *painter, const QRect &rect,
|
||||||
|
const double &scale,
|
||||||
|
const AudioVisualWaveform &samples,
|
||||||
|
const rational &start_time);
|
||||||
|
|
||||||
// Must be a power of 2
|
// Must be a power of 2
|
||||||
static const rational kMinimumSampleRate;
|
static const rational kMinimumSampleRate;
|
||||||
static const rational kMaximumSampleRate;
|
static const rational kMaximumSampleRate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational& start, double target_rate, Sample &data, size_t &start_index, size_t &samples_length);
|
void OverwriteSamplesFromBuffer(const SampleBuffer &samples,
|
||||||
|
int sample_rate, const rational &start,
|
||||||
|
double target_rate, Sample &data,
|
||||||
|
size_t &start_index,
|
||||||
|
size_t &samples_length);
|
||||||
|
|
||||||
void OverwriteSamplesFromMipmap(const Sample& input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational& start, double output_rate, Sample &output_data);
|
void OverwriteSamplesFromMipmap(const Sample &input,
|
||||||
|
double input_sample_rate,
|
||||||
|
size_t &input_start, size_t &input_length,
|
||||||
|
const rational &start, double output_rate,
|
||||||
|
Sample &output_data);
|
||||||
|
|
||||||
size_t time_to_samples(const rational& time, double sample_rate) const;
|
size_t time_to_samples(const rational &time, double sample_rate) const;
|
||||||
size_t time_to_samples(const double& time, double sample_rate) const;
|
size_t time_to_samples(const double &time, double sample_rate) const;
|
||||||
|
|
||||||
std::map<rational, Sample>::const_iterator GetMipmapForScale(double scale) const;
|
std::map<rational, Sample>::const_iterator
|
||||||
|
GetMipmapForScale(double scale) const;
|
||||||
|
|
||||||
void ValidateVirtualStart(const rational &new_start);
|
void ValidateVirtualStart(const rational &new_start);
|
||||||
|
|
||||||
rational virtual_start_;
|
rational virtual_start_;
|
||||||
|
|
||||||
int channels_;
|
int channels_;
|
||||||
|
|
||||||
std::map<rational, Sample> mipmapped_data_;
|
std::map<rational, Sample> mipmapped_data_;
|
||||||
|
|
||||||
rational length_;
|
|
||||||
|
|
||||||
|
rational length_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,11 @@
|
|||||||
|
|
||||||
#include "cliexportmanager.h"
|
#include "cliexportmanager.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
CLIExportManager::CLIExportManager()
|
CLIExportManager::CLIExportManager()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
|
|
||||||
#include "task/export/export.h"
|
#include "task/export/export.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CLIExportManager : public QObject
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CLIExportManager : public QObject {
|
||||||
public:
|
public:
|
||||||
CLIExportManager();
|
CLIExportManager();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,85 +22,87 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) :
|
|
||||||
QObject(parent),
|
|
||||||
title_(title),
|
|
||||||
progress_(-1),
|
|
||||||
drawn_(false)
|
|
||||||
{
|
{
|
||||||
SetProgress(0);
|
|
||||||
|
CLIProgressDialog::CLIProgressDialog(const QString &title, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, title_(title)
|
||||||
|
, progress_(-1)
|
||||||
|
, drawn_(false)
|
||||||
|
{
|
||||||
|
SetProgress(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLIProgressDialog::Update()
|
void CLIProgressDialog::Update()
|
||||||
{
|
{
|
||||||
if (drawn_) {
|
if (drawn_) {
|
||||||
// We've been here before, do a carriage return back to the start of the terminal line
|
// We've been here before, do a carriage return back to the start of the terminal line
|
||||||
std::cout << "\r";
|
std::cout << "\r";
|
||||||
} else {
|
} else {
|
||||||
drawn_ = true;
|
drawn_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Get real column count
|
// FIXME: Get real column count
|
||||||
int columns = 80;
|
int columns = 80;
|
||||||
|
|
||||||
int title_columns = columns / 2 - 1;
|
int title_columns = columns / 2 - 1;
|
||||||
|
|
||||||
// Print "title" text
|
// Print "title" text
|
||||||
QString sized_title = title_;
|
QString sized_title = title_;
|
||||||
|
|
||||||
if (title_.size() > title_columns) {
|
if (title_.size() > title_columns) {
|
||||||
sized_title = title_.left(title_columns - 3).append(QStringLiteral("..."));
|
sized_title =
|
||||||
} else {
|
title_.left(title_columns - 3).append(QStringLiteral("..."));
|
||||||
sized_title = title_;
|
} else {
|
||||||
}
|
sized_title = title_;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << sized_title.toUtf8().constData();
|
std::cout << sized_title.toUtf8().constData();
|
||||||
|
|
||||||
// Pad out the rest of the title area if necessary
|
// Pad out the rest of the title area if necessary
|
||||||
for (int i=sized_title.size(); i<title_columns; i++) {
|
for (int i = sized_title.size(); i < title_columns; i++) {
|
||||||
std::cout << " ";
|
std::cout << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters
|
// Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters
|
||||||
int progress_bar_columns = columns / 2 - 7;
|
int progress_bar_columns = columns / 2 - 7;
|
||||||
|
|
||||||
std::cout << "[";
|
std::cout << "[";
|
||||||
|
|
||||||
// Get UI bar progress
|
// Get UI bar progress
|
||||||
int bar_prog = qRound(progress_ * progress_bar_columns);
|
int bar_prog = qRound(progress_ * progress_bar_columns);
|
||||||
|
|
||||||
// Draw filled in bar
|
// Draw filled in bar
|
||||||
for (int i=0;i<bar_prog;i++) {
|
for (int i = 0; i < bar_prog; i++) {
|
||||||
std::cout << "=";
|
std::cout << "=";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw empty space
|
// Draw empty space
|
||||||
for (int i=bar_prog;i<progress_bar_columns;i++) {
|
for (int i = bar_prog; i < progress_bar_columns; i++) {
|
||||||
std::cout << " ";
|
std::cout << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "] ";
|
std::cout << "] ";
|
||||||
|
|
||||||
if (progress_ < 100) {
|
if (progress_ < 100) {
|
||||||
std::cout << " ";
|
std::cout << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progress_ < 10) {
|
if (progress_ < 10) {
|
||||||
std::cout << " ";
|
std::cout << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
|
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLIProgressDialog::SetProgress(double p)
|
void CLIProgressDialog::SetProgress(double p)
|
||||||
{
|
{
|
||||||
if (progress_ != p) {
|
if (progress_ != p) {
|
||||||
progress_ = p;
|
progress_ = p;
|
||||||
|
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,25 +26,24 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CLIProgressDialog : public QObject
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CLIProgressDialog : public QObject {
|
||||||
public:
|
public:
|
||||||
CLIProgressDialog(const QString &title, QObject* parent = nullptr);
|
CLIProgressDialog(const QString &title, QObject *parent = nullptr);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetProgress(double p);
|
void SetProgress(double p);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
QString title_;
|
QString title_;
|
||||||
|
|
||||||
double progress_;
|
double progress_;
|
||||||
|
|
||||||
bool drawn_;
|
|
||||||
|
|
||||||
|
bool drawn_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,19 @@
|
|||||||
|
|
||||||
#include "clitaskdialog.h"
|
#include "clitaskdialog.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) :
|
|
||||||
CLIProgressDialog(task->GetTitle(), parent),
|
|
||||||
task_(task)
|
|
||||||
{
|
{
|
||||||
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
|
|
||||||
|
CLITaskDialog::CLITaskDialog(Task *task, QObject *parent)
|
||||||
|
: CLIProgressDialog(task->GetTitle(), parent)
|
||||||
|
, task_(task)
|
||||||
|
{
|
||||||
|
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLITaskDialog::Run()
|
bool CLITaskDialog::Run()
|
||||||
{
|
{
|
||||||
return task_->Start();
|
return task_->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,19 +24,18 @@
|
|||||||
#include "cli/cliprogress/cliprogressdialog.h"
|
#include "cli/cliprogress/cliprogressdialog.h"
|
||||||
#include "task/task.h"
|
#include "task/task.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CLITaskDialog : public CLIProgressDialog
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
CLITaskDialog(Task *task, QObject* parent = nullptr);
|
|
||||||
|
|
||||||
bool Run();
|
class CLITaskDialog : public CLIProgressDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
CLITaskDialog(Task *task, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
bool Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Task* task_;
|
Task *task_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,123 +4,137 @@
|
|||||||
|
|
||||||
#include "task/taskmanager.h"
|
#include "task/taskmanager.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
ConformManager *ConformManager::instance_ = nullptr;
|
ConformManager *ConformManager::instance_ = nullptr;
|
||||||
|
|
||||||
ConformManager::Conform ConformManager::GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms, bool wait)
|
ConformManager::Conform ConformManager::GetConformState(
|
||||||
|
const QString &decoder_id, const QString &cache_path,
|
||||||
|
const Decoder::CodecStream &stream, const AudioParams ¶ms, bool wait)
|
||||||
{
|
{
|
||||||
// Mutex because we'll need to check the status of a conform task
|
// Mutex because we'll need to check the status of a conform task
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
// Return existing conform if exists
|
// Return existing conform if exists
|
||||||
QVector<QString> filenames = GetConformedFilename(cache_path, stream, params);
|
QVector<QString> filenames =
|
||||||
if (AllConformsExist(filenames)) {
|
GetConformedFilename(cache_path, stream, params);
|
||||||
return {kConformExists, filenames, nullptr};
|
if (AllConformsExist(filenames)) {
|
||||||
}
|
return { kConformExists, filenames, nullptr };
|
||||||
|
}
|
||||||
|
|
||||||
ConformTask *conforming_task = nullptr;
|
ConformTask *conforming_task = nullptr;
|
||||||
|
|
||||||
foreach (const ConformData &data, conforming_) {
|
foreach (const ConformData &data, conforming_) {
|
||||||
if (data.stream == stream && data.params == params) {
|
if (data.stream == stream && data.params == params) {
|
||||||
// Already creating conform in a task
|
// Already creating conform in a task
|
||||||
conforming_task = data.task;
|
conforming_task = data.task;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!conforming_task) {
|
if (!conforming_task) {
|
||||||
// Not conforming yet, create a task to do so
|
// Not conforming yet, create a task to do so
|
||||||
|
|
||||||
// We conform to a different filename until it's done to make it clear even across sessions
|
// We conform to a different filename until it's done to make it clear even across sessions
|
||||||
// whether this conform is ready or not
|
// whether this conform is ready or not
|
||||||
QVector<QString> working_filenames = filenames;
|
QVector<QString> working_filenames = filenames;
|
||||||
for (int i=0; i<working_filenames.size(); i++) {
|
for (int i = 0; i < working_filenames.size(); i++) {
|
||||||
working_filenames[i].append(QStringLiteral(".working"));
|
working_filenames[i].append(QStringLiteral(".working"));
|
||||||
}
|
}
|
||||||
|
|
||||||
conforming_task = new ConformTask(decoder_id, stream, params, working_filenames);
|
conforming_task =
|
||||||
connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished);
|
new ConformTask(decoder_id, stream, params, working_filenames);
|
||||||
conforming_task->moveToThread(TaskManager::instance()->thread());
|
connect(conforming_task, &ConformTask::Finished, this,
|
||||||
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task));
|
&ConformManager::ConformTaskFinished);
|
||||||
|
conforming_task->moveToThread(TaskManager::instance()->thread());
|
||||||
|
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask",
|
||||||
|
Qt::QueuedConnection,
|
||||||
|
Q_ARG(Task *, conforming_task));
|
||||||
|
|
||||||
conforming_.append({stream, params, conforming_task, working_filenames, filenames});
|
conforming_.append(
|
||||||
}
|
{ stream, params, conforming_task, working_filenames, filenames });
|
||||||
|
}
|
||||||
|
|
||||||
if (wait) {
|
if (wait) {
|
||||||
do {
|
do {
|
||||||
conform_done_condition_.wait(&mutex_);
|
conform_done_condition_.wait(&mutex_);
|
||||||
} while (!AllConformsExist(filenames));
|
} while (!AllConformsExist(filenames));
|
||||||
return {kConformExists, filenames, nullptr};
|
return { kConformExists, filenames, nullptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
return {kConformGenerating, QVector<QString>(), conforming_task};
|
return { kConformGenerating, QVector<QString>(), conforming_task };
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms)
|
QVector<QString>
|
||||||
|
ConformManager::GetConformedFilename(const QString &cache_path,
|
||||||
|
const Decoder::CodecStream &stream,
|
||||||
|
const AudioParams ¶ms)
|
||||||
{
|
{
|
||||||
QVector<QString> filenames(params.channel_count());
|
QVector<QString> filenames(params.channel_count());
|
||||||
|
|
||||||
for (int i=0; i<filenames.size(); i++) {
|
for (int i = 0; i < filenames.size(); i++) {
|
||||||
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
|
QString index_fn =
|
||||||
QString::number(stream.stream()),
|
QStringLiteral("%1-%2.%3.%4.%5.%6.pcm")
|
||||||
QString::number(params.sample_rate()),
|
.arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
|
||||||
QString::number(params.format()),
|
QString::number(stream.stream()),
|
||||||
QString::number(params.channel_layout().u.mask),
|
QString::number(params.sample_rate()),
|
||||||
QString::number(i));
|
QString::number(params.format()),
|
||||||
|
QString::number(params.channel_layout().u.mask),
|
||||||
|
QString::number(i));
|
||||||
|
|
||||||
filenames[i] = QDir(cache_path).filePath(index_fn);
|
filenames[i] = QDir(cache_path).filePath(index_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filenames;
|
return filenames;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConformManager::AllConformsExist(const QVector<QString> &filenames)
|
bool ConformManager::AllConformsExist(const QVector<QString> &filenames)
|
||||||
{
|
{
|
||||||
foreach (const QString &fn, filenames) {
|
foreach (const QString &fn, filenames) {
|
||||||
if (!QFileInfo::exists(fn)) {
|
if (!QFileInfo::exists(fn)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
|
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
ConformData data;
|
ConformData data;
|
||||||
|
|
||||||
// Remove conform data from list
|
// Remove conform data from list
|
||||||
for (int i=0; i<conforming_.size(); i++) {
|
for (int i = 0; i < conforming_.size(); i++) {
|
||||||
const ConformData &c = conforming_.at(i);
|
const ConformData &c = conforming_.at(i);
|
||||||
if (c.task == task) {
|
if (c.task == task) {
|
||||||
data = c;
|
data = c;
|
||||||
conforming_.removeAt(i);
|
conforming_.removeAt(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (succeeded) {
|
if (succeeded) {
|
||||||
// Move file to standard conform name, making it clear this conform is ready for use
|
// Move file to standard conform name, making it clear this conform is ready for use
|
||||||
for (int i=0; i<data.finished_filename.size(); i++) {
|
for (int i = 0; i < data.finished_filename.size(); i++) {
|
||||||
const QString &finished = data.finished_filename.at(i);
|
const QString &finished = data.finished_filename.at(i);
|
||||||
const QString &working = data.working_filename.at(i);
|
const QString &working = data.working_filename.at(i);
|
||||||
|
|
||||||
QFile::remove(finished);
|
QFile::remove(finished);
|
||||||
QFile::rename(working, finished);
|
QFile::rename(working, finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
conform_done_condition_.wakeAll();
|
conform_done_condition_.wakeAll();
|
||||||
locker.unlock();
|
locker.unlock();
|
||||||
emit ConformReady();
|
emit ConformReady();
|
||||||
} else {
|
} else {
|
||||||
// Failed, just delete the working filename if exists
|
// Failed, just delete the working filename if exists
|
||||||
for (int i=0; i<data.working_filename.size(); i++) {
|
for (int i = 0; i < data.working_filename.size(); i++) {
|
||||||
QFile::remove(data.working_filename.at(i));
|
QFile::remove(data.working_filename.at(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-55
@@ -7,82 +7,84 @@
|
|||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "task/conform/conform.h"
|
#include "task/conform/conform.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ConformManager : public QObject
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class ConformManager : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
static void CreateInstance()
|
static void CreateInstance()
|
||||||
{
|
{
|
||||||
if (!instance_) {
|
if (!instance_) {
|
||||||
instance_ = new ConformManager();
|
instance_ = new ConformManager();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DestroyInstance()
|
static void DestroyInstance()
|
||||||
{
|
{
|
||||||
delete instance_;
|
delete instance_;
|
||||||
instance_ = nullptr;
|
instance_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ConformManager *instance()
|
static ConformManager *instance()
|
||||||
{
|
{
|
||||||
return instance_;
|
return instance_;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConformState {
|
enum ConformState { kConformExists, kConformGenerating };
|
||||||
kConformExists,
|
|
||||||
kConformGenerating
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Conform {
|
struct Conform {
|
||||||
ConformState state;
|
ConformState state;
|
||||||
QVector<QString> filenames;
|
QVector<QString> filenames;
|
||||||
ConformTask *task;
|
ConformTask *task;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get conform state, and start conforming if no conform exists
|
* @brief Get conform state, and start conforming if no conform exists
|
||||||
*
|
*
|
||||||
* Thread-safe.
|
* Thread-safe.
|
||||||
*/
|
*/
|
||||||
Conform GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms, bool wait);
|
Conform GetConformState(const QString &decoder_id,
|
||||||
|
const QString &cache_path,
|
||||||
|
const Decoder::CodecStream &stream,
|
||||||
|
const AudioParams ¶ms, bool wait);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ConformReady();
|
void ConformReady();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ConformManager() = default;
|
ConformManager() = default;
|
||||||
|
|
||||||
static ConformManager *instance_;
|
static ConformManager *instance_;
|
||||||
|
|
||||||
QMutex mutex_;
|
QMutex mutex_;
|
||||||
|
|
||||||
QWaitCondition conform_done_condition_;
|
QWaitCondition conform_done_condition_;
|
||||||
|
|
||||||
struct ConformData {
|
struct ConformData {
|
||||||
Decoder::CodecStream stream;
|
Decoder::CodecStream stream;
|
||||||
AudioParams params;
|
AudioParams params;
|
||||||
ConformTask *task;
|
ConformTask *task;
|
||||||
QVector<QString> working_filename;
|
QVector<QString> working_filename;
|
||||||
QVector<QString> finished_filename;
|
QVector<QString> finished_filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
QVector<ConformData> conforming_;
|
QVector<ConformData> conforming_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the destination filename of an audio stream conformed to a set of parameters
|
* @brief Get the destination filename of an audio stream conformed to a set of parameters
|
||||||
*/
|
*/
|
||||||
static QVector<QString> GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms);
|
static QVector<QString>
|
||||||
|
GetConformedFilename(const QString &cache_path,
|
||||||
|
const Decoder::CodecStream &stream,
|
||||||
|
const AudioParams ¶ms);
|
||||||
|
|
||||||
static bool AllConformsExist(const QVector<QString> &filenames);
|
static bool AllConformsExist(const QVector<QString> &filenames);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ConformTaskFinished(Task *task, bool succeeded);
|
void ConformTaskFinished(Task *task, bool succeeded);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace olive
|
||||||
|
|
||||||
#endif // CONFORMMANAGER_H
|
#endif // CONFORMMANAGER_H
|
||||||
|
|||||||
+215
-185
@@ -33,151 +33,162 @@
|
|||||||
#include "node/project.h"
|
#include "node/project.h"
|
||||||
#include "task/taskmanager.h"
|
#include "task/taskmanager.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
const rational Decoder::kAnyTimecode = RATIONAL_MIN;
|
const rational Decoder::kAnyTimecode = RATIONAL_MIN;
|
||||||
|
|
||||||
Decoder::Decoder() :
|
Decoder::Decoder()
|
||||||
cached_texture_(nullptr)
|
: cached_texture_(nullptr)
|
||||||
{
|
{
|
||||||
UpdateLastAccessed();
|
UpdateLastAccessed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Decoder::IncrementAccessTime(qint64 t)
|
void Decoder::IncrementAccessTime(qint64 t)
|
||||||
{
|
{
|
||||||
last_accessed_ += t;
|
last_accessed_ += t;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decoder::Open(const CodecStream &stream)
|
bool Decoder::Open(const CodecStream &stream)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
UpdateLastAccessed();
|
UpdateLastAccessed();
|
||||||
|
|
||||||
if (stream_.IsValid()) {
|
if (stream_.IsValid()) {
|
||||||
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
|
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
|
||||||
if (stream_ == stream) {
|
if (stream_ == stream) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Tried to open a decoder that was already open with another stream";
|
qWarning()
|
||||||
return false;
|
<< "Tried to open a decoder that was already open with another stream";
|
||||||
}
|
return false;
|
||||||
} else {
|
}
|
||||||
// Stream was not open, try opening it now
|
} else {
|
||||||
if (!stream.IsValid()) {
|
// Stream was not open, try opening it now
|
||||||
// Cannot open null stream
|
if (!stream.IsValid()) {
|
||||||
qCritical() << "Decoder attempted to open null stream";
|
// Cannot open null stream
|
||||||
return false;
|
qCritical() << "Decoder attempted to open null stream";
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!stream.Exists()) {
|
if (!stream.Exists()) {
|
||||||
// Cannot open file that doesn't exist
|
// Cannot open file that doesn't exist
|
||||||
qCritical() << "Decoder attempted to open file that doesn't exist";
|
qCritical() << "Decoder attempted to open file that doesn't exist";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set stream
|
// Set stream
|
||||||
stream_ = stream;
|
stream_ = stream;
|
||||||
|
|
||||||
// Try open internal
|
// Try open internal
|
||||||
if (OpenInternal()) {
|
if (OpenInternal()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// Unset stream
|
// Unset stream
|
||||||
qCritical() << "Failed to open" << stream_.filename() << "stream" << stream_.stream();
|
qCritical() << "Failed to open" << stream_.filename() << "stream"
|
||||||
CloseInternal();
|
<< stream_.stream();
|
||||||
stream_.Reset();
|
CloseInternal();
|
||||||
return false;
|
stream_.Reset();
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
|
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
UpdateLastAccessed();
|
UpdateLastAccessed();
|
||||||
|
|
||||||
if (!stream_.IsValid()) {
|
if (!stream_.IsValid()) {
|
||||||
qCritical() << "Can't retrieve video on a closed decoder";
|
qCritical() << "Can't retrieve video on a closed decoder";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SupportsVideo()) {
|
if (!SupportsVideo()) {
|
||||||
qCritical() << "Decoder doesn't support video";
|
qCritical() << "Decoder doesn't support video";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.cancelled && p.cancelled->IsCancelled()) {
|
if (p.cancelled && p.cancelled->IsCancelled()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cached_texture_ && cached_time_ == p.time && cached_divider_ == p.divider) {
|
if (cached_texture_ && cached_time_ == p.time &&
|
||||||
return cached_texture_;
|
cached_divider_ == p.divider) {
|
||||||
}
|
return cached_texture_;
|
||||||
|
}
|
||||||
|
|
||||||
cached_texture_ = RetrieveVideoInternal(p);
|
cached_texture_ = RetrieveVideoInternal(p);
|
||||||
cached_time_ = p.time;
|
cached_time_ = p.time;
|
||||||
cached_divider_ = p.divider;
|
cached_divider_ = p.divider;
|
||||||
|
|
||||||
return cached_texture_;
|
return cached_texture_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode)
|
Decoder::RetrieveAudioStatus
|
||||||
|
Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
|
||||||
|
const AudioParams ¶ms, const QString &cache_path,
|
||||||
|
LoopMode loop_mode, RenderMode::Mode mode)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
UpdateLastAccessed();
|
UpdateLastAccessed();
|
||||||
|
|
||||||
if (!stream_.IsValid()) {
|
if (!stream_.IsValid()) {
|
||||||
qCritical() << "Can't retrieve audio on a closed decoder";
|
qCritical() << "Can't retrieve audio on a closed decoder";
|
||||||
return kInvalid;
|
return kInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SupportsAudio()) {
|
if (!SupportsAudio()) {
|
||||||
qCritical() << "Decoder doesn't support audio";
|
qCritical() << "Decoder doesn't support audio";
|
||||||
return kInvalid;
|
return kInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get conform state from ConformManager
|
// Get conform state from ConformManager
|
||||||
ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
|
ConformManager::Conform conform =
|
||||||
if (conform.state == ConformManager::kConformGenerating) {
|
ConformManager::instance()->GetConformState(
|
||||||
// If we need the task, it's available in `conform.task`
|
id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
|
||||||
return kWaitingForConform;
|
if (conform.state == ConformManager::kConformGenerating) {
|
||||||
}
|
// If we need the task, it's available in `conform.task`
|
||||||
|
return kWaitingForConform;
|
||||||
|
}
|
||||||
|
|
||||||
// See if we got the conform
|
// See if we got the conform
|
||||||
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) {
|
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode,
|
||||||
return kOK;
|
params)) {
|
||||||
} else {
|
return kOK;
|
||||||
return kUnknownError;
|
} else {
|
||||||
}
|
return kUnknownError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 Decoder::GetLastAccessedTime()
|
qint64 Decoder::GetLastAccessedTime()
|
||||||
{
|
{
|
||||||
return last_accessed_;
|
return last_accessed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Decoder::Close()
|
void Decoder::Close()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
UpdateLastAccessed();
|
UpdateLastAccessed();
|
||||||
|
|
||||||
cached_texture_ = nullptr;
|
cached_texture_ = nullptr;
|
||||||
|
|
||||||
if (stream_.IsValid()) {
|
if (stream_.IsValid()) {
|
||||||
CloseInternal();
|
CloseInternal();
|
||||||
stream_.Reset();
|
stream_.Reset();
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Tried to close a decoder that wasn't open";
|
qWarning() << "Tried to close a decoder that wasn't open";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams ¶ms, CancelAtom *cancelled)
|
bool Decoder::ConformAudio(const QVector<QString> &output_filenames,
|
||||||
|
const AudioParams ¶ms, CancelAtom *cancelled)
|
||||||
{
|
{
|
||||||
return ConformAudioInternal(output_filenames, params, cancelled);
|
return ConformAudioInternal(output_filenames, params, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -186,159 +197,178 @@ bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const Audio
|
|||||||
|
|
||||||
QVector<DecoderPtr> Decoder::ReceiveListOfAllDecoders()
|
QVector<DecoderPtr> Decoder::ReceiveListOfAllDecoders()
|
||||||
{
|
{
|
||||||
QVector<DecoderPtr> decoders;
|
QVector<DecoderPtr> decoders;
|
||||||
|
|
||||||
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
|
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
|
||||||
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
|
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
|
||||||
decoders.append(std::make_shared<OIIODecoder>());
|
decoders.append(std::make_shared<OIIODecoder>());
|
||||||
decoders.append(std::make_shared<FFmpegDecoder>());
|
decoders.append(std::make_shared<FFmpegDecoder>());
|
||||||
|
|
||||||
return decoders;
|
return decoders;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderPtr Decoder::CreateFromID(const QString &id)
|
DecoderPtr Decoder::CreateFromID(const QString &id)
|
||||||
{
|
{
|
||||||
if (id.isEmpty()) {
|
if (id.isEmpty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create list to iterate through
|
// Create list to iterate through
|
||||||
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
|
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
|
||||||
|
|
||||||
foreach (DecoderPtr d, decoder_list) {
|
foreach (DecoderPtr d, decoder_list) {
|
||||||
if (d->id() == id) {
|
if (d->id() == id) {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
|
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
|
||||||
{
|
{
|
||||||
if (duration != AV_NOPTS_VALUE && duration != 0) {
|
if (duration != AV_NOPTS_VALUE && duration != 0) {
|
||||||
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(duration));
|
emit IndexProgress(static_cast<double>(ts) /
|
||||||
}
|
static_cast<double>(duration));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Decoder::TransformImageSequenceFileName(const QString &filename, const int64_t& number)
|
QString Decoder::TransformImageSequenceFileName(const QString &filename,
|
||||||
|
const int64_t &number)
|
||||||
{
|
{
|
||||||
int digit_count = GetImageSequenceDigitCount(filename);
|
int digit_count = GetImageSequenceDigitCount(filename);
|
||||||
|
|
||||||
QFileInfo file_info(filename);
|
QFileInfo file_info(filename);
|
||||||
|
|
||||||
QString original_basename = file_info.completeBaseName();
|
QString original_basename = file_info.completeBaseName();
|
||||||
|
|
||||||
QString new_basename = original_basename.left(original_basename.size() - digit_count)
|
QString new_basename =
|
||||||
.append(QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
|
original_basename.left(original_basename.size() - digit_count)
|
||||||
|
.append(
|
||||||
|
QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
|
||||||
|
|
||||||
return file_info.dir().filePath(file_info.fileName().replace(original_basename, new_basename));
|
return file_info.dir().filePath(
|
||||||
|
file_info.fileName().replace(original_basename, new_basename));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Decoder::GetImageSequenceDigitCount(const QString &filename)
|
int Decoder::GetImageSequenceDigitCount(const QString &filename)
|
||||||
{
|
{
|
||||||
QString basename = QFileInfo(filename).completeBaseName();
|
QString basename = QFileInfo(filename).completeBaseName();
|
||||||
|
|
||||||
// See if basename contains a number at the end
|
// See if basename contains a number at the end
|
||||||
int digit_count = 0;
|
int digit_count = 0;
|
||||||
|
|
||||||
for (int i=basename.size()-1;i>=0;i--) {
|
for (int i = basename.size() - 1; i >= 0; i--) {
|
||||||
if (basename.at(i).isDigit()) {
|
if (basename.at(i).isDigit()) {
|
||||||
digit_count++;
|
digit_count++;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return digit_count;
|
return digit_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t Decoder::GetImageSequenceIndex(const QString &filename)
|
int64_t Decoder::GetImageSequenceIndex(const QString &filename)
|
||||||
{
|
{
|
||||||
int digit_count = GetImageSequenceDigitCount(filename);
|
int digit_count = GetImageSequenceDigitCount(filename);
|
||||||
|
|
||||||
QFileInfo file_info(filename);
|
QFileInfo file_info(filename);
|
||||||
|
|
||||||
QString original_basename = file_info.completeBaseName();
|
QString original_basename = file_info.completeBaseName();
|
||||||
|
|
||||||
QString number_only = original_basename.mid(original_basename.size() - digit_count);
|
QString number_only =
|
||||||
|
original_basename.mid(original_basename.size() - digit_count);
|
||||||
|
|
||||||
return number_only.toLongLong();
|
return number_only.toLongLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p)
|
Q_UNUSED(p)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams ¶ms, CancelAtom *cancelled)
|
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames,
|
||||||
|
const AudioParams ¶ms,
|
||||||
|
CancelAtom *cancelled)
|
||||||
{
|
{
|
||||||
Q_UNUSED(filenames)
|
Q_UNUSED(filenames)
|
||||||
Q_UNUSED(cancelled)
|
Q_UNUSED(cancelled)
|
||||||
Q_UNUSED(params)
|
Q_UNUSED(params)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
|
bool Decoder::RetrieveAudioFromConform(
|
||||||
|
SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames,
|
||||||
|
TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
|
||||||
{
|
{
|
||||||
PlanarFileDevice input;
|
PlanarFileDevice input;
|
||||||
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
||||||
// Offset range by audio start offset
|
// Offset range by audio start offset
|
||||||
range -= GetAudioStartOffset();
|
range -= GetAudioStartOffset();
|
||||||
|
|
||||||
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
|
qint64 read_index = input_params.time_to_bytes(range.in()) /
|
||||||
qint64 write_index = 0;
|
input_params.channel_count();
|
||||||
|
qint64 write_index = 0;
|
||||||
|
|
||||||
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel();
|
const qint64 buffer_length_in_bytes =
|
||||||
|
sample_buffer.sample_count() *
|
||||||
|
input_params.bytes_per_sample_per_channel();
|
||||||
|
|
||||||
while (write_index < buffer_length_in_bytes) {
|
while (write_index < buffer_length_in_bytes) {
|
||||||
if (loop_mode == LoopMode::kLoopModeLoop) {
|
if (loop_mode == LoopMode::kLoopModeLoop) {
|
||||||
while (read_index >= input.size()) {
|
while (read_index >= input.size()) {
|
||||||
read_index -= input.size();
|
read_index -= input.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (read_index < 0) {
|
while (read_index < 0) {
|
||||||
read_index += input.size();
|
read_index += input.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 write_count = 0;
|
qint64 write_count = 0;
|
||||||
|
|
||||||
if (read_index < 0) {
|
if (read_index < 0) {
|
||||||
// Reading before 0, write silence here until audio data would actually start
|
// Reading before 0, write silence here until audio data would actually start
|
||||||
write_count = qMin(-read_index, buffer_length_in_bytes);
|
write_count = qMin(-read_index, buffer_length_in_bytes);
|
||||||
sample_buffer.silence_bytes(write_index, write_index + write_count);
|
sample_buffer.silence_bytes(write_index,
|
||||||
} else if (read_index >= input.size()) {
|
write_index + write_count);
|
||||||
// Reading after data length, write silence until the end of the buffer
|
} else if (read_index >= input.size()) {
|
||||||
write_count = buffer_length_in_bytes - write_index;
|
// Reading after data length, write silence until the end of the buffer
|
||||||
sample_buffer.silence_bytes(write_index, write_index + write_count);
|
write_count = buffer_length_in_bytes - write_index;
|
||||||
} else {
|
sample_buffer.silence_bytes(write_index,
|
||||||
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
|
write_index + write_count);
|
||||||
input.seek(read_index);
|
} else {
|
||||||
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index);
|
write_count = qMin(input.size() - read_index,
|
||||||
}
|
buffer_length_in_bytes - write_index);
|
||||||
|
input.seek(read_index);
|
||||||
|
input.read(reinterpret_cast<char **>(
|
||||||
|
sample_buffer.to_raw_ptrs().data()),
|
||||||
|
write_count, write_index);
|
||||||
|
}
|
||||||
|
|
||||||
read_index += write_count;
|
read_index += write_count;
|
||||||
write_index += write_count;
|
write_index += write_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.close();
|
input.close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Decoder::UpdateLastAccessed()
|
void Decoder::UpdateLastAccessed()
|
||||||
{
|
{
|
||||||
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
|
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint qHash(Decoder::CodecStream stream, uint seed)
|
uint qHash(Decoder::CodecStream stream, uint seed)
|
||||||
{
|
{
|
||||||
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^ qHash(stream.block(), seed);
|
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^
|
||||||
|
qHash(stream.block(), seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+143
-126
@@ -36,12 +36,17 @@ extern "C" {
|
|||||||
#include "render/cancelatom.h"
|
#include "render/cancelatom.h"
|
||||||
#include "render/rendermodes.h"
|
#include "render/rendermodes.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class Decoder;
|
class Decoder;
|
||||||
using DecoderPtr = std::shared_ptr<Decoder>;
|
using DecoderPtr = std::shared_ptr<Decoder>;
|
||||||
|
|
||||||
#define DECODER_DEFAULT_DESTRUCTOR(x) virtual ~x() override {CloseInternal();}
|
#define DECODER_DEFAULT_DESTRUCTOR(x) \
|
||||||
|
virtual ~x() override \
|
||||||
|
{ \
|
||||||
|
CloseInternal(); \
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A decoder's is the main class for bringing external media into Olive
|
* @brief A decoder's is the main class for bringing external media into Olive
|
||||||
@@ -59,89 +64,88 @@ using DecoderPtr = std::shared_ptr<Decoder>;
|
|||||||
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
|
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
|
||||||
* to be utilized in the rest of the rendering pipeline.
|
* to be utilized in the rest of the rendering pipeline.
|
||||||
*/
|
*/
|
||||||
class Decoder : public QObject
|
class Decoder : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
enum RetrieveState {
|
enum RetrieveState { kReady, kFailedToOpen, kIndexUnavailable };
|
||||||
kReady,
|
|
||||||
kFailedToOpen,
|
|
||||||
kIndexUnavailable
|
|
||||||
};
|
|
||||||
|
|
||||||
Decoder();
|
Decoder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Unique decoder ID
|
* @brief Unique decoder ID
|
||||||
*/
|
*/
|
||||||
virtual QString id() const = 0;
|
virtual QString id() const = 0;
|
||||||
|
|
||||||
virtual bool SupportsVideo(){return false;}
|
virtual bool SupportsVideo()
|
||||||
virtual bool SupportsAudio(){return false;}
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual bool SupportsAudio()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void IncrementAccessTime(qint64 t);
|
void IncrementAccessTime(qint64 t);
|
||||||
|
|
||||||
class CodecStream
|
class CodecStream {
|
||||||
{
|
public:
|
||||||
public:
|
CodecStream()
|
||||||
CodecStream() :
|
: stream_(-1)
|
||||||
stream_(-1),
|
, block_(nullptr)
|
||||||
block_(nullptr)
|
{
|
||||||
{
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CodecStream(const QString& filename, int stream, Block *block) :
|
CodecStream(const QString &filename, int stream, Block *block)
|
||||||
filename_(filename),
|
: filename_(filename)
|
||||||
stream_(stream),
|
, stream_(stream)
|
||||||
block_(block)
|
, block_(block)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValid() const
|
bool IsValid() const
|
||||||
{
|
{
|
||||||
return !filename_.isEmpty() && stream_ >= 0;
|
return !filename_.isEmpty() && stream_ >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Exists() const
|
bool Exists() const
|
||||||
{
|
{
|
||||||
return QFileInfo::exists(filename_);
|
return QFileInfo::exists(filename_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
*this = CodecStream();
|
*this = CodecStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const CodecStream& rhs) const
|
bool operator==(const CodecStream &rhs) const
|
||||||
{
|
{
|
||||||
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
|
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& filename() const
|
const QString &filename() const
|
||||||
{
|
{
|
||||||
return filename_;
|
return filename_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stream() const
|
int stream() const
|
||||||
{
|
{
|
||||||
return stream_;
|
return stream_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Block *block() const
|
Block *block() const
|
||||||
{
|
{
|
||||||
return block_;
|
return block_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString filename_;
|
QString filename_;
|
||||||
|
|
||||||
int stream_;
|
int stream_;
|
||||||
|
|
||||||
Block *block_;
|
Block *block_;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
/**
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Open stream for decoding
|
* @brief Open stream for decoding
|
||||||
*
|
*
|
||||||
* This function is thread safe.
|
* This function is thread safe.
|
||||||
@@ -150,22 +154,21 @@ public:
|
|||||||
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
|
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
|
||||||
* be opened OR if already open and the stream is NOT the same.
|
* be opened OR if already open and the stream is NOT the same.
|
||||||
*/
|
*/
|
||||||
bool Open(const CodecStream& stream);
|
bool Open(const CodecStream &stream);
|
||||||
|
|
||||||
static const rational kAnyTimecode;
|
static const rational kAnyTimecode;
|
||||||
|
|
||||||
struct RetrieveVideoParams
|
struct RetrieveVideoParams {
|
||||||
{
|
Renderer *renderer = nullptr;
|
||||||
Renderer *renderer = nullptr;
|
rational time;
|
||||||
rational time;
|
int divider = 1;
|
||||||
int divider = 1;
|
PixelFormat maximum_format = PixelFormat::INVALID;
|
||||||
PixelFormat maximum_format = PixelFormat::INVALID;
|
CancelAtom *cancelled = nullptr;
|
||||||
CancelAtom *cancelled = nullptr;
|
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
|
||||||
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
|
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
|
||||||
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves a video frame from footage
|
* @brief Retrieves a video frame from footage
|
||||||
*
|
*
|
||||||
* This function will always return a valid frame unless a fatal error occurs (in such case,
|
* This function will always return a valid frame unless a fatal error occurs (in such case,
|
||||||
@@ -175,16 +178,16 @@ public:
|
|||||||
*
|
*
|
||||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||||
*/
|
*/
|
||||||
TexturePtr RetrieveVideo(const RetrieveVideoParams& p);
|
TexturePtr RetrieveVideo(const RetrieveVideoParams &p);
|
||||||
|
|
||||||
enum RetrieveAudioStatus {
|
enum RetrieveAudioStatus {
|
||||||
kInvalid = -1,
|
kInvalid = -1,
|
||||||
kOK,
|
kOK,
|
||||||
kWaitingForConform,
|
kWaitingForConform,
|
||||||
kUnknownError
|
kUnknownError
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve audio data from footage
|
* @brief Retrieve audio data from footage
|
||||||
*
|
*
|
||||||
* This function will always return a sample buffer unless a fatal error occurs (in such case,
|
* This function will always return a sample buffer unless a fatal error occurs (in such case,
|
||||||
@@ -192,14 +195,17 @@ public:
|
|||||||
*
|
*
|
||||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||||
*/
|
*/
|
||||||
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode);
|
RetrieveAudioStatus
|
||||||
|
RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
|
||||||
|
const AudioParams ¶ms, const QString &cache_path,
|
||||||
|
LoopMode loop_mode, RenderMode::Mode mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Determine the last time this decoder instance was used in any way
|
* @brief Determine the last time this decoder instance was used in any way
|
||||||
*/
|
*/
|
||||||
qint64 GetLastAccessedTime();
|
qint64 GetLastAccessedTime();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate a Footage object from a file
|
* @brief Generate a Footage object from a file
|
||||||
*
|
*
|
||||||
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
|
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
|
||||||
@@ -210,39 +216,43 @@ public:
|
|||||||
*
|
*
|
||||||
* This function is re-entrant.
|
* This function is re-entrant.
|
||||||
*/
|
*/
|
||||||
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const = 0;
|
virtual FootageDescription Probe(const QString &filename,
|
||||||
|
CancelAtom *cancelled) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes media/deallocates memory
|
* @brief Closes media/deallocates memory
|
||||||
*
|
*
|
||||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||||
*/
|
*/
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Conform audio stream
|
* @brief Conform audio stream
|
||||||
*/
|
*/
|
||||||
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams ¶ms, CancelAtom *cancelled = nullptr);
|
bool ConformAudio(const QVector<QString> &output_filenames,
|
||||||
|
const AudioParams ¶ms,
|
||||||
|
CancelAtom *cancelled = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a Decoder instance using a Decoder ID
|
* @brief Create a Decoder instance using a Decoder ID
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
* A Decoder instance or nullptr if a Decoder with this ID does not exist
|
* A Decoder instance or nullptr if a Decoder with this ID does not exist
|
||||||
*/
|
*/
|
||||||
static DecoderPtr CreateFromID(const QString& id);
|
static DecoderPtr CreateFromID(const QString &id);
|
||||||
|
|
||||||
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number);
|
static QString TransformImageSequenceFileName(const QString &filename,
|
||||||
|
const int64_t &number);
|
||||||
|
|
||||||
static int GetImageSequenceDigitCount(const QString& filename);
|
static int GetImageSequenceDigitCount(const QString &filename);
|
||||||
|
|
||||||
static int64_t GetImageSequenceIndex(const QString& filename);
|
static int64_t GetImageSequenceIndex(const QString &filename);
|
||||||
|
|
||||||
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
|
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Internal open function
|
* @brief Internal open function
|
||||||
*
|
*
|
||||||
* Sub-classes must override this function. Function will already be mutexed, so there is no need
|
* Sub-classes must override this function. Function will already be mutexed, so there is no need
|
||||||
@@ -254,62 +264,69 @@ protected:
|
|||||||
* return FALSE. If this function returns false, Decoder will call CloseInternal to clean any
|
* return FALSE. If this function returns false, Decoder will call CloseInternal to clean any
|
||||||
* memory allocated during OpenInternal.
|
* memory allocated during OpenInternal.
|
||||||
*/
|
*/
|
||||||
virtual bool OpenInternal() = 0;
|
virtual bool OpenInternal() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal close function
|
* @brief Internal close function
|
||||||
*
|
*
|
||||||
* Sub-classes must override this function. Function should be able to safely clear all allocated
|
* Sub-classes must override this function. Function should be able to safely clear all allocated
|
||||||
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
|
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
|
||||||
*/
|
*/
|
||||||
virtual void CloseInternal() = 0;
|
virtual void CloseInternal() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal frame retrieval function
|
* @brief Internal frame retrieval function
|
||||||
*
|
*
|
||||||
* Sub-classes must override this function IF they support video. Function is already mutexed
|
* Sub-classes must override this function IF they support video. Function is already mutexed
|
||||||
* so sub-classes don't need to worry about thread safety.
|
* so sub-classes don't need to worry about thread safety.
|
||||||
*/
|
*/
|
||||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p);
|
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p);
|
||||||
|
|
||||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, CancelAtom *cancelled);
|
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
|
||||||
|
const AudioParams ¶ms,
|
||||||
|
CancelAtom *cancelled);
|
||||||
|
|
||||||
void SignalProcessingProgress(int64_t ts, int64_t duration);
|
void SignalProcessingProgress(int64_t ts, int64_t duration);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return currently open stream
|
* @brief Return currently open stream
|
||||||
*
|
*
|
||||||
* This function is NOT thread safe and should therefore only be called by thread safe functions.
|
* This function is NOT thread safe and should therefore only be called by thread safe functions.
|
||||||
*/
|
*/
|
||||||
const CodecStream& stream() const
|
const CodecStream &stream() const
|
||||||
{
|
{
|
||||||
return stream_;
|
return stream_;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual rational GetAudioStartOffset() const { return 0; }
|
virtual rational GetAudioStartOffset() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
|
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
|
||||||
* available
|
* available
|
||||||
*/
|
*/
|
||||||
void IndexProgress(double);
|
void IndexProgress(double);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateLastAccessed();
|
void UpdateLastAccessed();
|
||||||
|
|
||||||
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams ¶ms);
|
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer,
|
||||||
|
const QVector<QString> &conform_filenames,
|
||||||
|
TimeRange range, LoopMode loop_mode,
|
||||||
|
const AudioParams ¶ms);
|
||||||
|
|
||||||
CodecStream stream_;
|
CodecStream stream_;
|
||||||
|
|
||||||
QMutex mutex_;
|
QMutex mutex_;
|
||||||
|
|
||||||
std::atomic_int64_t last_accessed_;
|
std::atomic_int64_t last_accessed_;
|
||||||
|
|
||||||
TexturePtr cached_texture_;
|
|
||||||
rational cached_time_;
|
|
||||||
int cached_divider_;
|
|
||||||
|
|
||||||
|
TexturePtr cached_texture_;
|
||||||
|
rational cached_time_;
|
||||||
|
int cached_divider_;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint qHash(Decoder::CodecStream stream, uint seed = 0);
|
uint qHash(Decoder::CodecStream stream, uint seed = 0);
|
||||||
|
|||||||
+415
-339
@@ -26,492 +26,568 @@
|
|||||||
#include "ffmpeg/ffmpegencoder.h"
|
#include "ffmpeg/ffmpegencoder.h"
|
||||||
#include "oiio/oiioencoder.h"
|
#include "oiio/oiioencoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
const QRegularExpression Encoder::kImageSequenceContainsDigits = QRegularExpression(QStringLiteral("\\[[#]+\\]"));
|
const QRegularExpression Encoder::kImageSequenceContainsDigits =
|
||||||
const QRegularExpression Encoder::kImageSequenceRemoveDigits = QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
|
QRegularExpression(QStringLiteral("\\[[#]+\\]"));
|
||||||
|
const QRegularExpression Encoder::kImageSequenceRemoveDigits =
|
||||||
|
QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
|
||||||
|
|
||||||
Encoder::Encoder(const EncodingParams ¶ms) :
|
Encoder::Encoder(const EncodingParams ¶ms)
|
||||||
params_(params)
|
: params_(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const EncodingParams &Encoder::params() const
|
const EncodingParams &Encoder::params() const
|
||||||
{
|
{
|
||||||
return params_;
|
return params_;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Encoder::GetFilenameForFrame(const rational &frame)
|
QString Encoder::GetFilenameForFrame(const rational &frame)
|
||||||
{
|
{
|
||||||
if (params().video_is_image_sequence()) {
|
if (params().video_is_image_sequence()) {
|
||||||
// Transform!
|
// Transform!
|
||||||
int64_t frame_index = Timecode::time_to_timestamp(frame, params().video_params().frame_rate_as_time_base());
|
int64_t frame_index = Timecode::time_to_timestamp(
|
||||||
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
|
frame, params().video_params().frame_rate_as_time_base());
|
||||||
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
|
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
|
||||||
|
QString frame_index_str =
|
||||||
|
QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
|
||||||
|
|
||||||
QString f = params_.filename();
|
QString f = params_.filename();
|
||||||
f.replace(kImageSequenceContainsDigits, frame_index_str);
|
f.replace(kImageSequenceContainsDigits, frame_index_str);
|
||||||
return f;
|
return f;
|
||||||
} else {
|
} else {
|
||||||
// Keep filename
|
// Keep filename
|
||||||
return params_.filename();
|
return params_.filename();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
|
int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
|
||||||
{
|
{
|
||||||
int start = filename.indexOf(kImageSequenceContainsDigits);
|
int start = filename.indexOf(kImageSequenceContainsDigits);
|
||||||
int digit_count = 0;
|
int digit_count = 0;
|
||||||
for (int i=start+1; i<filename.size(); i++) {
|
for (int i = start + 1; i < filename.size(); i++) {
|
||||||
if (filename.at(i) == '#') {
|
if (filename.at(i) == '#') {
|
||||||
digit_count++;
|
digit_count++;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return digit_count;
|
return digit_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Encoder::FilenameContainsDigitPlaceholder(const QString& filename)
|
bool Encoder::FilenameContainsDigitPlaceholder(const QString &filename)
|
||||||
{
|
{
|
||||||
return filename.contains(kImageSequenceContainsDigits);
|
return filename.contains(kImageSequenceContainsDigits);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
|
QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
|
||||||
{
|
{
|
||||||
return filename.remove(kImageSequenceRemoveDigits);
|
return filename.remove(kImageSequenceRemoveDigits);
|
||||||
}
|
}
|
||||||
|
|
||||||
EncodingParams::EncodingParams() :
|
EncodingParams::EncodingParams()
|
||||||
video_enabled_(false),
|
: video_enabled_(false)
|
||||||
video_bit_rate_(0),
|
, video_bit_rate_(0)
|
||||||
video_min_bit_rate_(0),
|
, video_min_bit_rate_(0)
|
||||||
video_max_bit_rate_(0),
|
, video_max_bit_rate_(0)
|
||||||
video_buffer_size_(0),
|
, video_buffer_size_(0)
|
||||||
video_threads_(0),
|
, video_threads_(0)
|
||||||
video_is_image_sequence_(false),
|
, video_is_image_sequence_(false)
|
||||||
audio_enabled_(false),
|
, audio_enabled_(false)
|
||||||
audio_bit_rate_(0),
|
, audio_bit_rate_(0)
|
||||||
subtitles_enabled_(false),
|
, subtitles_enabled_(false)
|
||||||
subtitles_are_sidecar_(false),
|
, subtitles_are_sidecar_(false)
|
||||||
video_scaling_method_(kStretch),
|
, video_scaling_method_(kStretch)
|
||||||
has_custom_range_(false)
|
, has_custom_range_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir EncodingParams::GetPresetPath()
|
QDir EncodingParams::GetPresetPath()
|
||||||
{
|
{
|
||||||
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets"));
|
return QDir(FileFunctions::GetConfigurationLocation())
|
||||||
|
.filePath(QStringLiteral("exportpresets"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList EncodingParams::GetListOfPresets()
|
QStringList EncodingParams::GetListOfPresets()
|
||||||
{
|
{
|
||||||
QDir d = EncodingParams::GetPresetPath();
|
QDir d = EncodingParams::GetPresetPath();
|
||||||
return d.entryList(QDir::Files);
|
return d.entryList(QDir::Files);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::EnableVideo(const VideoParams &video_params, const ExportCodec::Codec &vcodec)
|
void EncodingParams::EnableVideo(const VideoParams &video_params,
|
||||||
|
const ExportCodec::Codec &vcodec)
|
||||||
{
|
{
|
||||||
video_enabled_ = true;
|
video_enabled_ = true;
|
||||||
video_params_ = video_params;
|
video_params_ = video_params;
|
||||||
video_codec_ = vcodec;
|
video_codec_ = vcodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::EnableAudio(const AudioParams &audio_params, const ExportCodec::Codec &acodec)
|
void EncodingParams::EnableAudio(const AudioParams &audio_params,
|
||||||
|
const ExportCodec::Codec &acodec)
|
||||||
{
|
{
|
||||||
audio_enabled_ = true;
|
audio_enabled_ = true;
|
||||||
audio_params_ = audio_params;
|
audio_params_ = audio_params;
|
||||||
audio_codec_ = acodec;
|
audio_codec_ = acodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
|
void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
|
||||||
{
|
{
|
||||||
subtitles_enabled_ = true;
|
subtitles_enabled_ = true;
|
||||||
subtitles_codec_ = scodec;
|
subtitles_codec_ = scodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec)
|
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
|
||||||
|
const ExportCodec::Codec &scodec)
|
||||||
{
|
{
|
||||||
subtitles_enabled_ = true;
|
subtitles_enabled_ = true;
|
||||||
subtitles_are_sidecar_ = true;
|
subtitles_are_sidecar_ = true;
|
||||||
subtitle_sidecar_fmt_ = sfmt;
|
subtitle_sidecar_fmt_ = sfmt;
|
||||||
subtitles_codec_ = scodec;
|
subtitles_codec_ = scodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::DisableVideo()
|
void EncodingParams::DisableVideo()
|
||||||
{
|
{
|
||||||
video_enabled_ = false;
|
video_enabled_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::DisableAudio()
|
void EncodingParams::DisableAudio()
|
||||||
{
|
{
|
||||||
audio_enabled_ = false;
|
audio_enabled_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::DisableSubtitles()
|
void EncodingParams::DisableSubtitles()
|
||||||
{
|
{
|
||||||
subtitles_enabled_ = false;
|
subtitles_enabled_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncodingParams::Load(QXmlStreamReader *reader)
|
bool EncodingParams::Load(QXmlStreamReader *reader)
|
||||||
{
|
{
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("export")) {
|
if (reader->name() == QStringLiteral("export")) {
|
||||||
int version = 0;
|
int version = 0;
|
||||||
|
|
||||||
XMLAttributeLoop(reader, attr) {
|
XMLAttributeLoop(reader, attr)
|
||||||
if (attr.name() == QStringLiteral("version")) {
|
{
|
||||||
version = attr.value().toInt();
|
if (attr.name() == QStringLiteral("version")) {
|
||||||
}
|
version = attr.value().toInt();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 1:
|
case 1:
|
||||||
return LoadV1(reader);
|
return LoadV1(reader);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reader->skipCurrentElement();
|
reader->skipCurrentElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncodingParams::Load(QIODevice *device)
|
bool EncodingParams::Load(QIODevice *device)
|
||||||
{
|
{
|
||||||
QXmlStreamReader reader(device);
|
QXmlStreamReader reader(device);
|
||||||
return Load(&reader);
|
return Load(&reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::Save(QIODevice *device) const
|
void EncodingParams::Save(QIODevice *device) const
|
||||||
{
|
{
|
||||||
QXmlStreamWriter writer(device);
|
QXmlStreamWriter writer(device);
|
||||||
Save(&writer);
|
Save(&writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingParams::Save(QXmlStreamWriter *writer) const
|
void EncodingParams::Save(QXmlStreamWriter *writer) const
|
||||||
{
|
{
|
||||||
writer->writeStartDocument();
|
writer->writeStartDocument();
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("export"));
|
writer->writeStartElement(QStringLiteral("export"));
|
||||||
|
|
||||||
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion));
|
writer->writeAttribute(QStringLiteral("version"),
|
||||||
|
QString::number(kEncoderParamsVersion));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("filename"), filename_);
|
writer->writeTextElement(QStringLiteral("filename"), filename_);
|
||||||
writer->writeTextElement(QStringLiteral("format"), QString::number(format_));
|
writer->writeTextElement(QStringLiteral("format"),
|
||||||
|
QString::number(format_));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_));
|
writer->writeTextElement(QStringLiteral("range"),
|
||||||
writer->writeTextElement(QStringLiteral("customrangein"), QString::fromStdString(custom_range_.in().toString()));
|
QString::number(has_custom_range_));
|
||||||
writer->writeTextElement(QStringLiteral("customrangeout"), QString::fromStdString(custom_range_.out().toString()));
|
writer->writeTextElement(
|
||||||
|
QStringLiteral("customrangein"),
|
||||||
|
QString::fromStdString(custom_range_.in().toString()));
|
||||||
|
writer->writeTextElement(
|
||||||
|
QStringLiteral("customrangeout"),
|
||||||
|
QString::fromStdString(custom_range_.out().toString()));
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("video"));
|
writer->writeStartElement(QStringLiteral("video"));
|
||||||
|
|
||||||
writer->writeAttribute(QStringLiteral("enabled"), QString::number(video_enabled_));
|
writer->writeAttribute(QStringLiteral("enabled"),
|
||||||
|
QString::number(video_enabled_));
|
||||||
|
|
||||||
if (video_enabled_) {
|
if (video_enabled_) {
|
||||||
writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_));
|
writer->writeTextElement(QStringLiteral("codec"),
|
||||||
writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width()));
|
QString::number(video_codec_));
|
||||||
writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height()));
|
writer->writeTextElement(QStringLiteral("width"),
|
||||||
writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format()));
|
QString::number(video_params_.width()));
|
||||||
writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(video_params_.pixel_aspect_ratio().toString()));
|
writer->writeTextElement(QStringLiteral("height"),
|
||||||
writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(video_params_.time_base().toString()));
|
QString::number(video_params_.height()));
|
||||||
writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider()));
|
writer->writeTextElement(QStringLiteral("format"),
|
||||||
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_));
|
QString::number(video_params_.format()));
|
||||||
writer->writeTextElement(QStringLiteral("minbitrate"), QString::number(video_min_bit_rate_));
|
writer->writeTextElement(
|
||||||
writer->writeTextElement(QStringLiteral("maxbitrate"), QString::number(video_max_bit_rate_));
|
QStringLiteral("pixelaspect"),
|
||||||
writer->writeTextElement(QStringLiteral("bufsize"), QString::number(video_buffer_size_));
|
QString::fromStdString(
|
||||||
writer->writeTextElement(QStringLiteral("threads"), QString::number(video_threads_));
|
video_params_.pixel_aspect_ratio().toString()));
|
||||||
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
|
writer->writeTextElement(
|
||||||
writer->writeTextElement(QStringLiteral("imgseq"), QString::number(video_is_image_sequence_));
|
QStringLiteral("timebase"),
|
||||||
|
QString::fromStdString(video_params_.time_base().toString()));
|
||||||
|
writer->writeTextElement(QStringLiteral("divider"),
|
||||||
|
QString::number(video_params_.divider()));
|
||||||
|
writer->writeTextElement(QStringLiteral("bitrate"),
|
||||||
|
QString::number(video_bit_rate_));
|
||||||
|
writer->writeTextElement(QStringLiteral("minbitrate"),
|
||||||
|
QString::number(video_min_bit_rate_));
|
||||||
|
writer->writeTextElement(QStringLiteral("maxbitrate"),
|
||||||
|
QString::number(video_max_bit_rate_));
|
||||||
|
writer->writeTextElement(QStringLiteral("bufsize"),
|
||||||
|
QString::number(video_buffer_size_));
|
||||||
|
writer->writeTextElement(QStringLiteral("threads"),
|
||||||
|
QString::number(video_threads_));
|
||||||
|
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
|
||||||
|
writer->writeTextElement(QStringLiteral("imgseq"),
|
||||||
|
QString::number(video_is_image_sequence_));
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("color"));
|
writer->writeStartElement(QStringLiteral("color"));
|
||||||
writer->writeTextElement(QStringLiteral("output"), color_transform_.output());
|
writer->writeTextElement(QStringLiteral("output"),
|
||||||
writer->writeEndElement(); // colortransform
|
color_transform_.output());
|
||||||
|
writer->writeEndElement(); // colortransform
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
|
writer->writeTextElement(QStringLiteral("vscale"),
|
||||||
|
QString::number(video_scaling_method_));
|
||||||
|
|
||||||
if (!video_opts_.isEmpty()) {
|
if (!video_opts_.isEmpty()) {
|
||||||
writer->writeStartElement(QStringLiteral("opts"));
|
writer->writeStartElement(QStringLiteral("opts"));
|
||||||
|
|
||||||
QHash<QString, QString>::const_iterator i;
|
QHash<QString, QString>::const_iterator i;
|
||||||
for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) {
|
for (i = video_opts_.constBegin(); i != video_opts_.constEnd();
|
||||||
writer->writeStartElement(QStringLiteral("entry"));
|
i++) {
|
||||||
|
writer->writeStartElement(QStringLiteral("entry"));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("key"), i.key());
|
writer->writeTextElement(QStringLiteral("key"), i.key());
|
||||||
writer->writeTextElement(QStringLiteral("value"), i.value());
|
writer->writeTextElement(QStringLiteral("value"), i.value());
|
||||||
|
|
||||||
writer->writeEndElement(); // entry
|
writer->writeEndElement(); // entry
|
||||||
}
|
}
|
||||||
|
|
||||||
writer->writeEndElement(); // opts
|
writer->writeEndElement(); // opts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer->writeEndElement(); // video
|
writer->writeEndElement(); // video
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("audio"));
|
writer->writeStartElement(QStringLiteral("audio"));
|
||||||
|
|
||||||
writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_));
|
writer->writeAttribute(QStringLiteral("enabled"),
|
||||||
|
QString::number(audio_enabled_));
|
||||||
|
|
||||||
if (audio_enabled_) {
|
if (audio_enabled_) {
|
||||||
writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_));
|
writer->writeTextElement(QStringLiteral("codec"),
|
||||||
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate()));
|
QString::number(audio_codec_));
|
||||||
|
writer->writeTextElement(QStringLiteral("samplerate"),
|
||||||
|
QString::number(audio_params_.sample_rate()));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("channellayout"),
|
writer->writeTextElement(
|
||||||
QString::number(audio_params().channel_layout().u.mask));
|
QStringLiteral("channellayout"),
|
||||||
writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string()));
|
QString::number(audio_params().channel_layout().u.mask));
|
||||||
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_));
|
writer->writeTextElement(
|
||||||
}
|
QStringLiteral("format"),
|
||||||
|
QString::fromStdString(audio_params_.format().to_string()));
|
||||||
|
writer->writeTextElement(QStringLiteral("bitrate"),
|
||||||
|
QString::number(audio_bit_rate_));
|
||||||
|
}
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("subtitles"));
|
writer->writeStartElement(QStringLiteral("subtitles"));
|
||||||
|
|
||||||
writer->writeAttribute(QStringLiteral("enabled"), QString::number(subtitles_enabled_));
|
writer->writeAttribute(QStringLiteral("enabled"),
|
||||||
|
QString::number(subtitles_enabled_));
|
||||||
|
|
||||||
if (subtitles_enabled_) {
|
if (subtitles_enabled_) {
|
||||||
writer->writeTextElement(QStringLiteral("sidecar"), QString::number(subtitles_are_sidecar_));
|
writer->writeTextElement(QStringLiteral("sidecar"),
|
||||||
writer->writeTextElement(QStringLiteral("sidecarformat"), QString::number(subtitle_sidecar_fmt_));
|
QString::number(subtitles_are_sidecar_));
|
||||||
|
writer->writeTextElement(QStringLiteral("sidecarformat"),
|
||||||
|
QString::number(subtitle_sidecar_fmt_));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("codec"), QString::number(subtitles_codec_));
|
writer->writeTextElement(QStringLiteral("codec"),
|
||||||
}
|
QString::number(subtitles_codec_));
|
||||||
|
}
|
||||||
|
|
||||||
writer->writeEndElement(); // subtitles
|
writer->writeEndElement(); // subtitles
|
||||||
|
|
||||||
writer->writeEndElement(); // audio
|
writer->writeEndElement(); // audio
|
||||||
|
|
||||||
writer->writeEndElement(); // export
|
writer->writeEndElement(); // export
|
||||||
|
|
||||||
writer->writeEndDocument();
|
writer->writeEndDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params)
|
Encoder *Encoder::CreateFromID(Type id, const EncodingParams ¶ms)
|
||||||
{
|
{
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case kEncoderTypeNone:
|
case kEncoderTypeNone:
|
||||||
break;
|
break;
|
||||||
case kEncoderTypeFFmpeg:
|
case kEncoderTypeFFmpeg:
|
||||||
return new FFmpegEncoder(params);
|
return new FFmpegEncoder(params);
|
||||||
case kEncoderTypeOIIO:
|
case kEncoderTypeOIIO:
|
||||||
return new OIIOEncoder(params);
|
return new OIIOEncoder(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
|
Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
|
||||||
{
|
{
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case ExportFormat::kFormatDNxHD:
|
case ExportFormat::kFormatDNxHD:
|
||||||
case ExportFormat::kFormatMatroska:
|
case ExportFormat::kFormatMatroska:
|
||||||
case ExportFormat::kFormatQuickTime:
|
case ExportFormat::kFormatQuickTime:
|
||||||
case ExportFormat::kFormatMPEG4Video:
|
case ExportFormat::kFormatMPEG4Video:
|
||||||
case ExportFormat::kFormatMPEG4Audio:
|
case ExportFormat::kFormatMPEG4Audio:
|
||||||
case ExportFormat::kFormatWAV:
|
case ExportFormat::kFormatWAV:
|
||||||
case ExportFormat::kFormatAIFF:
|
case ExportFormat::kFormatAIFF:
|
||||||
case ExportFormat::kFormatMP3:
|
case ExportFormat::kFormatMP3:
|
||||||
case ExportFormat::kFormatFLAC:
|
case ExportFormat::kFormatFLAC:
|
||||||
case ExportFormat::kFormatOgg:
|
case ExportFormat::kFormatOgg:
|
||||||
case ExportFormat::kFormatWebM:
|
case ExportFormat::kFormatWebM:
|
||||||
case ExportFormat::kFormatSRT:
|
case ExportFormat::kFormatSRT:
|
||||||
return kEncoderTypeFFmpeg;
|
return kEncoderTypeFFmpeg;
|
||||||
case ExportFormat::kFormatOpenEXR:
|
case ExportFormat::kFormatOpenEXR:
|
||||||
case ExportFormat::kFormatPNG:
|
case ExportFormat::kFormatPNG:
|
||||||
case ExportFormat::kFormatTIFF:
|
case ExportFormat::kFormatTIFF:
|
||||||
return kEncoderTypeOIIO;
|
return kEncoderTypeOIIO;
|
||||||
case ExportFormat::kFormatCount:
|
case ExportFormat::kFormatCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return kEncoderTypeNone;
|
return kEncoderTypeNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms)
|
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f,
|
||||||
|
const EncodingParams ¶ms)
|
||||||
{
|
{
|
||||||
return CreateFromID(GetTypeFromFormat(f), params);
|
return CreateFromID(GetTypeFromFormat(f), params);
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder *Encoder::CreateFromParams(const EncodingParams ¶ms)
|
Encoder *Encoder::CreateFromParams(const EncodingParams ¶ms)
|
||||||
{
|
{
|
||||||
return CreateFromFormat(params.format(), params);
|
return CreateFromFormat(params.format(), params);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
||||||
{
|
{
|
||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SampleFormat> Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
|
std::vector<SampleFormat>
|
||||||
|
Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
|
||||||
{
|
{
|
||||||
return std::vector<SampleFormat>();
|
return std::vector<SampleFormat>();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
|
QMatrix4x4
|
||||||
int source_width, int source_height,
|
EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
|
||||||
int dest_width, int dest_height)
|
int source_width, int source_height,
|
||||||
|
int dest_width, int dest_height)
|
||||||
{
|
{
|
||||||
QMatrix4x4 preview_matrix;
|
QMatrix4x4 preview_matrix;
|
||||||
|
|
||||||
if (method == EncodingParams::kStretch) {
|
if (method == EncodingParams::kStretch) {
|
||||||
return preview_matrix;
|
return preview_matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height);
|
float export_ar =
|
||||||
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height);
|
static_cast<float>(dest_width) / static_cast<float>(dest_height);
|
||||||
|
float source_ar =
|
||||||
|
static_cast<float>(source_width) / static_cast<float>(source_height);
|
||||||
|
|
||||||
if (qFuzzyCompare(export_ar, source_ar)) {
|
if (qFuzzyCompare(export_ar, source_ar)) {
|
||||||
return preview_matrix;
|
return preview_matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
|
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
|
||||||
preview_matrix.scale(source_ar / export_ar, 1.0F);
|
preview_matrix.scale(source_ar / export_ar, 1.0F);
|
||||||
} else {
|
} else {
|
||||||
preview_matrix.scale(1.0F, export_ar / source_ar);
|
preview_matrix.scale(1.0F, export_ar / source_ar);
|
||||||
}
|
}
|
||||||
|
|
||||||
return preview_matrix;
|
return preview_matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
||||||
{
|
{
|
||||||
rational custom_range_in, custom_range_out;
|
rational custom_range_in, custom_range_out;
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("filename")) {
|
if (reader->name() == QStringLiteral("filename")) {
|
||||||
filename_ = reader->readElementText();
|
filename_ = reader->readElementText();
|
||||||
} else if (reader->name() == QStringLiteral("format")) {
|
} else if (reader->name() == QStringLiteral("format")) {
|
||||||
format_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
|
format_ = static_cast<ExportFormat::Format>(
|
||||||
} else if (reader->name() == QStringLiteral("range")) {
|
reader->readElementText().toInt());
|
||||||
has_custom_range_ = reader->readElementText().toInt();
|
} else if (reader->name() == QStringLiteral("range")) {
|
||||||
} else if (reader->name() == QStringLiteral("customrangein")) {
|
has_custom_range_ = reader->readElementText().toInt();
|
||||||
custom_range_in = rational::fromString(reader->readElementText().toStdString());
|
} else if (reader->name() == QStringLiteral("customrangein")) {
|
||||||
} else if (reader->name() == QStringLiteral("customrangeout")) {
|
custom_range_in =
|
||||||
custom_range_out = rational::fromString(reader->readElementText().toStdString());
|
rational::fromString(reader->readElementText().toStdString());
|
||||||
} else if (reader->name() == QStringLiteral("video")) {
|
} else if (reader->name() == QStringLiteral("customrangeout")) {
|
||||||
XMLAttributeLoop(reader, attr) {
|
custom_range_out =
|
||||||
if (attr.name() == QStringLiteral("enabled")) {
|
rational::fromString(reader->readElementText().toStdString());
|
||||||
video_enabled_ = attr.value().toInt();
|
} else if (reader->name() == QStringLiteral("video")) {
|
||||||
}
|
XMLAttributeLoop(reader, attr)
|
||||||
}
|
{
|
||||||
|
if (attr.name() == QStringLiteral("enabled")) {
|
||||||
|
video_enabled_ = attr.value().toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("codec")) {
|
if (reader->name() == QStringLiteral("codec")) {
|
||||||
video_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
|
video_codec_ = static_cast<ExportCodec::Codec>(
|
||||||
} else if (reader->name() == QStringLiteral("width")) {
|
reader->readElementText().toInt());
|
||||||
video_params_.set_width(reader->readElementText().toInt());
|
} else if (reader->name() == QStringLiteral("width")) {
|
||||||
} else if (reader->name() == QStringLiteral("height")) {
|
video_params_.set_width(reader->readElementText().toInt());
|
||||||
video_params_.set_height(reader->readElementText().toInt());
|
} else if (reader->name() == QStringLiteral("height")) {
|
||||||
} else if (reader->name() == QStringLiteral("format")) {
|
video_params_.set_height(reader->readElementText().toInt());
|
||||||
video_params_.set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt()));
|
} else if (reader->name() == QStringLiteral("format")) {
|
||||||
} else if (reader->name() == QStringLiteral("pixelaspect")) {
|
video_params_.set_format(static_cast<PixelFormat::Format>(
|
||||||
video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString()));
|
reader->readElementText().toInt()));
|
||||||
} else if (reader->name() == QStringLiteral("timebase")) {
|
} else if (reader->name() == QStringLiteral("pixelaspect")) {
|
||||||
video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString()));
|
video_params_.set_pixel_aspect_ratio(rational::fromString(
|
||||||
} else if (reader->name() == QStringLiteral("divider")) {
|
reader->readElementText().toStdString()));
|
||||||
video_params_.set_divider(reader->readElementText().toInt());
|
} else if (reader->name() == QStringLiteral("timebase")) {
|
||||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
video_params_.set_time_base(rational::fromString(
|
||||||
video_bit_rate_ = reader->readElementText().toLongLong();
|
reader->readElementText().toStdString()));
|
||||||
} else if (reader->name() == QStringLiteral("minbitrate")) {
|
} else if (reader->name() == QStringLiteral("divider")) {
|
||||||
video_min_bit_rate_ = reader->readElementText().toLongLong();
|
video_params_.set_divider(
|
||||||
} else if (reader->name() == QStringLiteral("maxbitrate")) {
|
reader->readElementText().toInt());
|
||||||
video_max_bit_rate_ = reader->readElementText().toLongLong();
|
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||||
} else if (reader->name() == QStringLiteral("bufsize")) {
|
video_bit_rate_ = reader->readElementText().toLongLong();
|
||||||
video_buffer_size_ = reader->readElementText().toLongLong();
|
} else if (reader->name() == QStringLiteral("minbitrate")) {
|
||||||
} else if (reader->name() == QStringLiteral("threads")) {
|
video_min_bit_rate_ =
|
||||||
video_threads_ = reader->readElementText().toInt();
|
reader->readElementText().toLongLong();
|
||||||
} else if (reader->name() == QStringLiteral("pixfmt")) {
|
} else if (reader->name() == QStringLiteral("maxbitrate")) {
|
||||||
video_pix_fmt_ = reader->readElementText();
|
video_max_bit_rate_ =
|
||||||
} else if (reader->name() == QStringLiteral("imgseq")) {
|
reader->readElementText().toLongLong();
|
||||||
video_is_image_sequence_ = reader->readElementText().toInt();
|
} else if (reader->name() == QStringLiteral("bufsize")) {
|
||||||
} else if (reader->name() == QStringLiteral("color")) {
|
video_buffer_size_ = reader->readElementText().toLongLong();
|
||||||
while (XMLReadNextStartElement(reader)) {
|
} else if (reader->name() == QStringLiteral("threads")) {
|
||||||
if (reader->name() == QStringLiteral("output")) {
|
video_threads_ = reader->readElementText().toInt();
|
||||||
color_transform_ = reader->readElementText();
|
} else if (reader->name() == QStringLiteral("pixfmt")) {
|
||||||
} else {
|
video_pix_fmt_ = reader->readElementText();
|
||||||
reader->skipCurrentElement();
|
} else if (reader->name() == QStringLiteral("imgseq")) {
|
||||||
}
|
video_is_image_sequence_ =
|
||||||
}
|
reader->readElementText().toInt();
|
||||||
} else if (reader->name() == QStringLiteral("vscale")) {
|
} else if (reader->name() == QStringLiteral("color")) {
|
||||||
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt());
|
while (XMLReadNextStartElement(reader)) {
|
||||||
} else if (reader->name() == QStringLiteral("opts")) {
|
if (reader->name() == QStringLiteral("output")) {
|
||||||
while (XMLReadNextStartElement(reader)) {
|
color_transform_ = reader->readElementText();
|
||||||
if (reader->name() == QStringLiteral("entry")) {
|
} else {
|
||||||
QString key, value;
|
reader->skipCurrentElement();
|
||||||
while (XMLReadNextStartElement(reader)) {
|
}
|
||||||
if (reader->name() == QStringLiteral("key")) {
|
}
|
||||||
key = reader->readElementText();
|
} else if (reader->name() == QStringLiteral("vscale")) {
|
||||||
} else if (reader->name() == QStringLiteral("value")) {
|
video_scaling_method_ = static_cast<VideoScalingMethod>(
|
||||||
value = reader->readElementText();
|
reader->readElementText().toInt());
|
||||||
} else {
|
} else if (reader->name() == QStringLiteral("opts")) {
|
||||||
reader->skipCurrentElement();
|
while (XMLReadNextStartElement(reader)) {
|
||||||
}
|
if (reader->name() == QStringLiteral("entry")) {
|
||||||
}
|
QString key, value;
|
||||||
set_video_option(key, value);
|
while (XMLReadNextStartElement(reader)) {
|
||||||
} else {
|
if (reader->name() == QStringLiteral("key")) {
|
||||||
reader->skipCurrentElement();
|
key = reader->readElementText();
|
||||||
}
|
} else if (reader->name() ==
|
||||||
}
|
QStringLiteral("value")) {
|
||||||
} else {
|
value = reader->readElementText();
|
||||||
reader->skipCurrentElement();
|
} else {
|
||||||
}
|
reader->skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
set_video_option(key, value);
|
||||||
|
} else {
|
||||||
|
reader->skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reader->skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
|
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
|
||||||
if (video_params_.pixel_aspect_ratio().isNull()) {
|
if (video_params_.pixel_aspect_ratio().isNull()) {
|
||||||
video_params_.set_pixel_aspect_ratio(1);
|
video_params_.set_pixel_aspect_ratio(1);
|
||||||
}
|
}
|
||||||
} else if (reader->name() == QStringLiteral("audio")) {
|
} else if (reader->name() == QStringLiteral("audio")) {
|
||||||
XMLAttributeLoop(reader, attr) {
|
XMLAttributeLoop(reader, attr)
|
||||||
if (attr.name() == QStringLiteral("enabled")) {
|
{
|
||||||
audio_enabled_ = attr.value().toInt();
|
if (attr.name() == QStringLiteral("enabled")) {
|
||||||
}
|
audio_enabled_ = attr.value().toInt();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("codec")) {
|
if (reader->name() == QStringLiteral("codec")) {
|
||||||
audio_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
|
audio_codec_ = static_cast<ExportCodec::Codec>(
|
||||||
} else if (reader->name() == QStringLiteral("samplerate")) {
|
reader->readElementText().toInt());
|
||||||
audio_params_.set_sample_rate(reader->readElementText().toInt());
|
} else if (reader->name() == QStringLiteral("samplerate")) {
|
||||||
} else if (reader->name() == QStringLiteral("channellayout")) {
|
audio_params_.set_sample_rate(
|
||||||
AVChannelLayout av_channel_layout;
|
reader->readElementText().toInt());
|
||||||
av_channel_layout_from_mask(&av_channel_layout, reader->readElementText().toLongLong());
|
} else if (reader->name() == QStringLiteral("channellayout")) {
|
||||||
audio_params_.set_channel_layout(av_channel_layout);
|
AVChannelLayout av_channel_layout;
|
||||||
av_channel_layout_uninit(&av_channel_layout);
|
av_channel_layout_from_mask(
|
||||||
} else if (reader->name() == QStringLiteral("format")) {
|
&av_channel_layout,
|
||||||
audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString()));
|
reader->readElementText().toLongLong());
|
||||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
audio_params_.set_channel_layout(av_channel_layout);
|
||||||
audio_bit_rate_ = reader->readElementText().toLongLong();
|
av_channel_layout_uninit(&av_channel_layout);
|
||||||
} else {
|
} else if (reader->name() == QStringLiteral("format")) {
|
||||||
reader->skipCurrentElement();
|
audio_params_.set_format(SampleFormat::from_string(
|
||||||
}
|
reader->readElementText().toStdString()));
|
||||||
}
|
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||||
|
audio_bit_rate_ = reader->readElementText().toLongLong();
|
||||||
|
} else {
|
||||||
|
reader->skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HACK: Resolve bug where I forgot to serialize the audio bit rate
|
// HACK: Resolve bug where I forgot to serialize the audio bit rate
|
||||||
if (!audio_bit_rate_) {
|
if (!audio_bit_rate_) {
|
||||||
audio_bit_rate_ = 320000;
|
audio_bit_rate_ = 320000;
|
||||||
}
|
}
|
||||||
} else if (reader->name() == QStringLiteral("subtitles")) {
|
} else if (reader->name() == QStringLiteral("subtitles")) {
|
||||||
XMLAttributeLoop(reader, attr) {
|
XMLAttributeLoop(reader, attr)
|
||||||
if (attr.name() == QStringLiteral("enabled")) {
|
{
|
||||||
subtitles_enabled_ = attr.value().toInt();
|
if (attr.name() == QStringLiteral("enabled")) {
|
||||||
}
|
subtitles_enabled_ = attr.value().toInt();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("sidecar")) {
|
if (reader->name() == QStringLiteral("sidecar")) {
|
||||||
subtitles_are_sidecar_ = reader->readElementText().toInt();
|
subtitles_are_sidecar_ = reader->readElementText().toInt();
|
||||||
} else if (reader->name() == QStringLiteral("sidecarformat")) {
|
} else if (reader->name() == QStringLiteral("sidecarformat")) {
|
||||||
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
|
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(
|
||||||
} else if (reader->name() == QStringLiteral("codec")) {
|
reader->readElementText().toInt());
|
||||||
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
|
} else if (reader->name() == QStringLiteral("codec")) {
|
||||||
} else {
|
subtitles_codec_ = static_cast<ExportCodec::Codec>(
|
||||||
reader->skipCurrentElement();
|
reader->readElementText().toInt());
|
||||||
}
|
} else {
|
||||||
}
|
reader->skipCurrentElement();
|
||||||
} else {
|
}
|
||||||
reader->skipCurrentElement();
|
}
|
||||||
}
|
} else {
|
||||||
}
|
reader->skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+270
-148
@@ -34,218 +34,340 @@
|
|||||||
#include "render/subtitleparams.h"
|
#include "render/subtitleparams.h"
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
//这个代码也许是导出编码视频用的?
|
//这个代码也许是导出编码视频用的?
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class Encoder;
|
class Encoder;
|
||||||
using EncoderPtr = std::shared_ptr<Encoder>;
|
using EncoderPtr = std::shared_ptr<Encoder>;
|
||||||
|
|
||||||
class EncodingParams
|
class EncodingParams {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
enum VideoScalingMethod {
|
enum VideoScalingMethod { kFit, kStretch, kCrop };
|
||||||
kFit,
|
|
||||||
kStretch,
|
|
||||||
kCrop
|
|
||||||
};
|
|
||||||
|
|
||||||
EncodingParams();
|
EncodingParams();
|
||||||
|
|
||||||
static QDir GetPresetPath();
|
static QDir GetPresetPath();
|
||||||
static QStringList GetListOfPresets();
|
static QStringList GetListOfPresets();
|
||||||
|
|
||||||
bool IsValid() const
|
bool IsValid() const
|
||||||
{
|
{
|
||||||
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
|
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFilename(const QString& filename) { filename_ = filename; }
|
void SetFilename(const QString &filename)
|
||||||
|
{
|
||||||
|
filename_ = filename;
|
||||||
|
}
|
||||||
|
|
||||||
void EnableVideo(const VideoParams& video_params, const ExportCodec::Codec& vcodec);
|
void EnableVideo(const VideoParams &video_params,
|
||||||
void EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec);
|
const ExportCodec::Codec &vcodec);
|
||||||
void EnableSubtitles(const ExportCodec::Codec &scodec);
|
void EnableAudio(const AudioParams &audio_params,
|
||||||
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec);
|
const ExportCodec::Codec &acodec);
|
||||||
|
void EnableSubtitles(const ExportCodec::Codec &scodec);
|
||||||
|
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
|
||||||
|
const ExportCodec::Codec &scodec);
|
||||||
|
|
||||||
void DisableVideo();
|
void DisableVideo();
|
||||||
void DisableAudio();
|
void DisableAudio();
|
||||||
void DisableSubtitles();
|
void DisableSubtitles();
|
||||||
|
|
||||||
const ExportFormat::Format &format() const { return format_; }
|
const ExportFormat::Format &format() const
|
||||||
void set_format(const ExportFormat::Format &format) { format_ = format; }
|
{
|
||||||
|
return format_;
|
||||||
|
}
|
||||||
|
void set_format(const ExportFormat::Format &format)
|
||||||
|
{
|
||||||
|
format_ = format;
|
||||||
|
}
|
||||||
|
|
||||||
void set_video_option(const QString& key, const QString& value) { video_opts_.insert(key, value); }
|
void set_video_option(const QString &key, const QString &value)
|
||||||
void set_video_bit_rate(const int64_t& rate) { video_bit_rate_ = rate; }
|
{
|
||||||
void set_video_min_bit_rate(const int64_t& rate) { video_min_bit_rate_ = rate; }
|
video_opts_.insert(key, value);
|
||||||
void set_video_max_bit_rate(const int64_t& rate) { video_max_bit_rate_ = rate; }
|
}
|
||||||
void set_video_buffer_size(const int64_t& sz) { video_buffer_size_ = sz; }
|
void set_video_bit_rate(const int64_t &rate)
|
||||||
void set_video_threads(const int& threads) { video_threads_ = threads; }
|
{
|
||||||
void set_video_pix_fmt(const QString& s) { video_pix_fmt_ = s; }
|
video_bit_rate_ = rate;
|
||||||
void set_video_is_image_sequence(bool s) { video_is_image_sequence_ = s; }
|
}
|
||||||
void set_color_transform(const ColorTransform& color_transform) { color_transform_ = color_transform; }
|
void set_video_min_bit_rate(const int64_t &rate)
|
||||||
|
{
|
||||||
|
video_min_bit_rate_ = rate;
|
||||||
|
}
|
||||||
|
void set_video_max_bit_rate(const int64_t &rate)
|
||||||
|
{
|
||||||
|
video_max_bit_rate_ = rate;
|
||||||
|
}
|
||||||
|
void set_video_buffer_size(const int64_t &sz)
|
||||||
|
{
|
||||||
|
video_buffer_size_ = sz;
|
||||||
|
}
|
||||||
|
void set_video_threads(const int &threads)
|
||||||
|
{
|
||||||
|
video_threads_ = threads;
|
||||||
|
}
|
||||||
|
void set_video_pix_fmt(const QString &s)
|
||||||
|
{
|
||||||
|
video_pix_fmt_ = s;
|
||||||
|
}
|
||||||
|
void set_video_is_image_sequence(bool s)
|
||||||
|
{
|
||||||
|
video_is_image_sequence_ = s;
|
||||||
|
}
|
||||||
|
void set_color_transform(const ColorTransform &color_transform)
|
||||||
|
{
|
||||||
|
color_transform_ = color_transform;
|
||||||
|
}
|
||||||
|
|
||||||
const QString& filename() const { return filename_; }
|
const QString &filename() const
|
||||||
|
{
|
||||||
|
return filename_;
|
||||||
|
}
|
||||||
|
|
||||||
bool video_enabled() const { return video_enabled_; }
|
bool video_enabled() const
|
||||||
const ExportCodec::Codec& video_codec() const { return video_codec_; }
|
{
|
||||||
const VideoParams& video_params() const { return video_params_; }
|
return video_enabled_;
|
||||||
const QHash<QString, QString>& video_opts() const { return video_opts_; }
|
}
|
||||||
QString video_option(const QString &key) const { return video_opts_.value(key); }
|
const ExportCodec::Codec &video_codec() const
|
||||||
bool has_video_opt(const QString &key) const { return video_opts_.contains(key); }
|
{
|
||||||
const int64_t& video_bit_rate() const { return video_bit_rate_; }
|
return video_codec_;
|
||||||
const int64_t& video_min_bit_rate() const { return video_min_bit_rate_; }
|
}
|
||||||
const int64_t& video_max_bit_rate() const { return video_max_bit_rate_; }
|
const VideoParams &video_params() const
|
||||||
const int64_t& video_buffer_size() const { return video_buffer_size_; }
|
{
|
||||||
const int& video_threads() const { return video_threads_; }
|
return video_params_;
|
||||||
const QString& video_pix_fmt() const { return video_pix_fmt_; }
|
}
|
||||||
bool video_is_image_sequence() const { return video_is_image_sequence_; }
|
const QHash<QString, QString> &video_opts() const
|
||||||
const ColorTransform& color_transform() const { return color_transform_; }
|
{
|
||||||
|
return video_opts_;
|
||||||
|
}
|
||||||
|
QString video_option(const QString &key) const
|
||||||
|
{
|
||||||
|
return video_opts_.value(key);
|
||||||
|
}
|
||||||
|
bool has_video_opt(const QString &key) const
|
||||||
|
{
|
||||||
|
return video_opts_.contains(key);
|
||||||
|
}
|
||||||
|
const int64_t &video_bit_rate() const
|
||||||
|
{
|
||||||
|
return video_bit_rate_;
|
||||||
|
}
|
||||||
|
const int64_t &video_min_bit_rate() const
|
||||||
|
{
|
||||||
|
return video_min_bit_rate_;
|
||||||
|
}
|
||||||
|
const int64_t &video_max_bit_rate() const
|
||||||
|
{
|
||||||
|
return video_max_bit_rate_;
|
||||||
|
}
|
||||||
|
const int64_t &video_buffer_size() const
|
||||||
|
{
|
||||||
|
return video_buffer_size_;
|
||||||
|
}
|
||||||
|
const int &video_threads() const
|
||||||
|
{
|
||||||
|
return video_threads_;
|
||||||
|
}
|
||||||
|
const QString &video_pix_fmt() const
|
||||||
|
{
|
||||||
|
return video_pix_fmt_;
|
||||||
|
}
|
||||||
|
bool video_is_image_sequence() const
|
||||||
|
{
|
||||||
|
return video_is_image_sequence_;
|
||||||
|
}
|
||||||
|
const ColorTransform &color_transform() const
|
||||||
|
{
|
||||||
|
return color_transform_;
|
||||||
|
}
|
||||||
|
|
||||||
bool audio_enabled() const { return audio_enabled_; }
|
bool audio_enabled() const
|
||||||
const ExportCodec::Codec &audio_codec() const { return audio_codec_; }
|
{
|
||||||
const AudioParams& audio_params() const { return audio_params_; }
|
return audio_enabled_;
|
||||||
const int64_t& audio_bit_rate() const { return audio_bit_rate_; }
|
}
|
||||||
|
const ExportCodec::Codec &audio_codec() const
|
||||||
|
{
|
||||||
|
return audio_codec_;
|
||||||
|
}
|
||||||
|
const AudioParams &audio_params() const
|
||||||
|
{
|
||||||
|
return audio_params_;
|
||||||
|
}
|
||||||
|
const int64_t &audio_bit_rate() const
|
||||||
|
{
|
||||||
|
return audio_bit_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
void set_audio_bit_rate(const int64_t& b) { audio_bit_rate_ = b; }
|
void set_audio_bit_rate(const int64_t &b)
|
||||||
|
{
|
||||||
|
audio_bit_rate_ = b;
|
||||||
|
}
|
||||||
|
|
||||||
bool subtitles_enabled() const { return subtitles_enabled_; }
|
bool subtitles_enabled() const
|
||||||
bool subtitles_are_sidecar() const { return subtitles_are_sidecar_; }
|
{
|
||||||
ExportFormat::Format subtitle_sidecar_fmt() const { return subtitle_sidecar_fmt_; }
|
return subtitles_enabled_;
|
||||||
ExportCodec::Codec subtitles_codec() const { return subtitles_codec_; }
|
}
|
||||||
|
bool subtitles_are_sidecar() const
|
||||||
|
{
|
||||||
|
return subtitles_are_sidecar_;
|
||||||
|
}
|
||||||
|
ExportFormat::Format subtitle_sidecar_fmt() const
|
||||||
|
{
|
||||||
|
return subtitle_sidecar_fmt_;
|
||||||
|
}
|
||||||
|
ExportCodec::Codec subtitles_codec() const
|
||||||
|
{
|
||||||
|
return subtitles_codec_;
|
||||||
|
}
|
||||||
|
|
||||||
const rational& GetExportLength() const { return export_length_; }
|
const rational &GetExportLength() const
|
||||||
void SetExportLength(const rational& export_length) { export_length_ = export_length; }
|
{
|
||||||
|
return export_length_;
|
||||||
|
}
|
||||||
|
void SetExportLength(const rational &export_length)
|
||||||
|
{
|
||||||
|
export_length_ = export_length;
|
||||||
|
}
|
||||||
|
|
||||||
bool Load(QIODevice *device);
|
bool Load(QIODevice *device);
|
||||||
bool Load(QXmlStreamReader *reader);
|
bool Load(QXmlStreamReader *reader);
|
||||||
|
|
||||||
void Save(QIODevice *device) const;
|
void Save(QIODevice *device) const;
|
||||||
void Save(QXmlStreamWriter* writer) const;
|
void Save(QXmlStreamWriter *writer) const;
|
||||||
|
|
||||||
bool has_custom_range() const { return has_custom_range_; }
|
bool has_custom_range() const
|
||||||
const TimeRange& custom_range() const { return custom_range_; }
|
{
|
||||||
void set_custom_range(const TimeRange& custom_range)
|
return has_custom_range_;
|
||||||
{
|
}
|
||||||
has_custom_range_ = true;
|
const TimeRange &custom_range() const
|
||||||
custom_range_ = custom_range;
|
{
|
||||||
}
|
return custom_range_;
|
||||||
|
}
|
||||||
|
void set_custom_range(const TimeRange &custom_range)
|
||||||
|
{
|
||||||
|
has_custom_range_ = true;
|
||||||
|
custom_range_ = custom_range;
|
||||||
|
}
|
||||||
|
|
||||||
const VideoScalingMethod& video_scaling_method() const { return video_scaling_method_; }
|
const VideoScalingMethod &video_scaling_method() const
|
||||||
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method) { video_scaling_method_ = video_scaling_method; }
|
{
|
||||||
|
return video_scaling_method_;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
set_video_scaling_method(const VideoScalingMethod &video_scaling_method)
|
||||||
|
{
|
||||||
|
video_scaling_method_ = video_scaling_method;
|
||||||
|
}
|
||||||
|
|
||||||
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
|
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
|
||||||
int source_width, int source_height,
|
int source_width, int source_height,
|
||||||
int dest_width, int dest_height);
|
int dest_width, int dest_height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kEncoderParamsVersion = 1;
|
static const int kEncoderParamsVersion = 1;
|
||||||
|
|
||||||
bool LoadV1(QXmlStreamReader *reader);
|
bool LoadV1(QXmlStreamReader *reader);
|
||||||
|
|
||||||
QString filename_;
|
QString filename_;
|
||||||
ExportFormat::Format format_;
|
ExportFormat::Format format_;
|
||||||
|
|
||||||
bool video_enabled_;
|
bool video_enabled_;
|
||||||
ExportCodec::Codec video_codec_;
|
ExportCodec::Codec video_codec_;
|
||||||
VideoParams video_params_;
|
VideoParams video_params_;
|
||||||
QHash<QString, QString> video_opts_;
|
QHash<QString, QString> video_opts_;
|
||||||
int64_t video_bit_rate_;
|
int64_t video_bit_rate_;
|
||||||
int64_t video_min_bit_rate_;
|
int64_t video_min_bit_rate_;
|
||||||
int64_t video_max_bit_rate_;
|
int64_t video_max_bit_rate_;
|
||||||
int64_t video_buffer_size_;
|
int64_t video_buffer_size_;
|
||||||
int video_threads_;
|
int video_threads_;
|
||||||
QString video_pix_fmt_;
|
QString video_pix_fmt_;
|
||||||
bool video_is_image_sequence_;
|
bool video_is_image_sequence_;
|
||||||
ColorTransform color_transform_;
|
ColorTransform color_transform_;
|
||||||
|
|
||||||
bool audio_enabled_;
|
bool audio_enabled_;
|
||||||
ExportCodec::Codec audio_codec_;
|
ExportCodec::Codec audio_codec_;
|
||||||
AudioParams audio_params_;
|
AudioParams audio_params_;
|
||||||
int64_t audio_bit_rate_;
|
int64_t audio_bit_rate_;
|
||||||
|
|
||||||
bool subtitles_enabled_;
|
bool subtitles_enabled_;
|
||||||
bool subtitles_are_sidecar_;
|
bool subtitles_are_sidecar_;
|
||||||
ExportFormat::Format subtitle_sidecar_fmt_;
|
ExportFormat::Format subtitle_sidecar_fmt_;
|
||||||
ExportCodec::Codec subtitles_codec_;
|
ExportCodec::Codec subtitles_codec_;
|
||||||
|
|
||||||
rational export_length_;
|
rational export_length_;
|
||||||
VideoScalingMethod video_scaling_method_;
|
VideoScalingMethod video_scaling_method_;
|
||||||
|
|
||||||
bool has_custom_range_;
|
|
||||||
TimeRange custom_range_;
|
|
||||||
|
|
||||||
|
bool has_custom_range_;
|
||||||
|
TimeRange custom_range_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Encoder : public QObject
|
class Encoder : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
Encoder(const EncodingParams& params);
|
Encoder(const EncodingParams ¶ms);
|
||||||
|
|
||||||
enum Type {
|
enum Type { kEncoderTypeNone = -1, kEncoderTypeFFmpeg, kEncoderTypeOIIO };
|
||||||
kEncoderTypeNone = -1,
|
|
||||||
kEncoderTypeFFmpeg,
|
|
||||||
kEncoderTypeOIIO
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a Encoder instance using a Encoder ID
|
* @brief Create a Encoder instance using a Encoder ID
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
* A Encoder instance or nullptr if a Decoder with this ID does not exist
|
* A Encoder instance or nullptr if a Decoder with this ID does not exist
|
||||||
*/
|
*/
|
||||||
static Encoder *CreateFromID(Type id, const EncodingParams ¶ms);
|
static Encoder *CreateFromID(Type id, const EncodingParams ¶ms);
|
||||||
|
|
||||||
static Type GetTypeFromFormat(ExportFormat::Format f);
|
static Type GetTypeFromFormat(ExportFormat::Format f);
|
||||||
|
|
||||||
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms);
|
static Encoder *CreateFromFormat(ExportFormat::Format f,
|
||||||
|
const EncodingParams ¶ms);
|
||||||
|
|
||||||
static Encoder *CreateFromParams(const EncodingParams ¶ms);
|
static Encoder *CreateFromParams(const EncodingParams ¶ms);
|
||||||
|
|
||||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
||||||
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const;
|
virtual std::vector<SampleFormat>
|
||||||
|
GetSampleFormatsForCodec(ExportCodec::Codec c) const;
|
||||||
|
|
||||||
const EncodingParams& params() const;
|
const EncodingParams ¶ms() const;
|
||||||
|
|
||||||
virtual PixelFormat GetDesiredPixelFormat() const
|
virtual PixelFormat GetDesiredPixelFormat() const
|
||||||
{
|
{
|
||||||
return PixelFormat::INVALID;
|
return PixelFormat::INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& GetError() const
|
const QString &GetError() const
|
||||||
{
|
{
|
||||||
return error_;
|
return error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GetFilenameForFrame(const rational& frame);
|
QString GetFilenameForFrame(const rational &frame);
|
||||||
|
|
||||||
static int GetImageSequencePlaceholderDigitCount(const QString& filename);
|
static int GetImageSequencePlaceholderDigitCount(const QString &filename);
|
||||||
|
|
||||||
static bool FilenameContainsDigitPlaceholder(const QString &filename);
|
static bool FilenameContainsDigitPlaceholder(const QString &filename);
|
||||||
static QString FilenameRemoveDigitPlaceholder(QString filename);
|
static QString FilenameRemoveDigitPlaceholder(QString filename);
|
||||||
|
|
||||||
static const QRegularExpression kImageSequenceContainsDigits;
|
static const QRegularExpression kImageSequenceContainsDigits;
|
||||||
static const QRegularExpression kImageSequenceRemoveDigits;
|
static const QRegularExpression kImageSequenceRemoveDigits;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual bool Open() = 0;
|
virtual bool Open() = 0;
|
||||||
|
|
||||||
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) = 0;
|
virtual bool WriteFrame(olive::FramePtr frame,
|
||||||
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
|
olive::core::rational time) = 0;
|
||||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
|
||||||
|
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
||||||
|
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetError(const QString& err)
|
void SetError(const QString &err)
|
||||||
{
|
{
|
||||||
error_ = err;
|
error_ = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EncodingParams params_;
|
EncodingParams params_;
|
||||||
|
|
||||||
QString error_;
|
|
||||||
|
|
||||||
|
QString error_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+96
-95
@@ -25,115 +25,116 @@ extern "C" {
|
|||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
QString ExportCodec::GetCodecName(ExportCodec::Codec c)
|
QString ExportCodec::GetCodecName(ExportCodec::Codec c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case kCodecDNxHD:
|
case kCodecDNxHD:
|
||||||
return tr("DNxHD");
|
return tr("DNxHD");
|
||||||
case kCodecH264:
|
case kCodecH264:
|
||||||
return tr("H.264");
|
return tr("H.264");
|
||||||
case kCodecH264rgb:
|
case kCodecH264rgb:
|
||||||
return tr("H.264 RGB");
|
return tr("H.264 RGB");
|
||||||
case kCodecH265:
|
case kCodecH265:
|
||||||
return tr("H.265");
|
return tr("H.265");
|
||||||
case kCodecOpenEXR:
|
case kCodecOpenEXR:
|
||||||
return tr("OpenEXR");
|
return tr("OpenEXR");
|
||||||
case kCodecPNG:
|
case kCodecPNG:
|
||||||
return tr("PNG");
|
return tr("PNG");
|
||||||
case kCodecProRes:
|
case kCodecProRes:
|
||||||
return tr("ProRes");
|
return tr("ProRes");
|
||||||
case kCodecCineform:
|
case kCodecCineform:
|
||||||
return tr("Cineform");
|
return tr("Cineform");
|
||||||
case kCodecTIFF:
|
case kCodecTIFF:
|
||||||
return tr("TIFF");
|
return tr("TIFF");
|
||||||
case kCodecMP2:
|
case kCodecMP2:
|
||||||
return tr("MP2");
|
return tr("MP2");
|
||||||
case kCodecMP3:
|
case kCodecMP3:
|
||||||
return tr("MP3");
|
return tr("MP3");
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
return tr("AAC");
|
return tr("AAC");
|
||||||
case kCodecPCM:
|
case kCodecPCM:
|
||||||
return tr("PCM (Uncompressed)");
|
return tr("PCM (Uncompressed)");
|
||||||
case kCodecFLAC:
|
case kCodecFLAC:
|
||||||
return tr("FLAC");
|
return tr("FLAC");
|
||||||
case kCodecOpus:
|
case kCodecOpus:
|
||||||
return tr("Opus");
|
return tr("Opus");
|
||||||
case kCodecVorbis:
|
case kCodecVorbis:
|
||||||
return tr("Vorbis");
|
return tr("Vorbis");
|
||||||
case kCodecVP9:
|
case kCodecVP9:
|
||||||
return tr("VP9");
|
return tr("VP9");
|
||||||
case kCodecAV1:
|
case kCodecAV1:
|
||||||
return tr("AV1");
|
return tr("AV1");
|
||||||
case kCodecSRT:
|
case kCodecSRT:
|
||||||
return tr("SubRip SRT");
|
return tr("SubRip SRT");
|
||||||
case kCodecCount:
|
case kCodecCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tr("Unknown");
|
return tr("Unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
|
bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case kCodecDNxHD:
|
case kCodecDNxHD:
|
||||||
case kCodecH264:
|
case kCodecH264:
|
||||||
case kCodecH264rgb:
|
case kCodecH264rgb:
|
||||||
case kCodecH265:
|
case kCodecH265:
|
||||||
case kCodecProRes:
|
case kCodecProRes:
|
||||||
case kCodecCineform:
|
case kCodecCineform:
|
||||||
case kCodecMP2:
|
case kCodecMP2:
|
||||||
case kCodecMP3:
|
case kCodecMP3:
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
case kCodecPCM:
|
case kCodecPCM:
|
||||||
case kCodecVorbis:
|
case kCodecVorbis:
|
||||||
case kCodecOpus:
|
case kCodecOpus:
|
||||||
case kCodecFLAC:
|
case kCodecFLAC:
|
||||||
case kCodecVP9:
|
case kCodecVP9:
|
||||||
case kCodecAV1:
|
case kCodecAV1:
|
||||||
case kCodecSRT:
|
case kCodecSRT:
|
||||||
return false;
|
return false;
|
||||||
case kCodecOpenEXR:
|
case kCodecOpenEXR:
|
||||||
case kCodecPNG:
|
case kCodecPNG:
|
||||||
case kCodecTIFF:
|
case kCodecTIFF:
|
||||||
return true;
|
return true;
|
||||||
case kCodecCount:
|
case kCodecCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportCodec::IsCodecLossless(Codec c)
|
bool ExportCodec::IsCodecLossless(Codec c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case kCodecPCM:
|
case kCodecPCM:
|
||||||
case kCodecFLAC:
|
case kCodecFLAC:
|
||||||
return true;
|
return true;
|
||||||
case kCodecDNxHD:
|
case kCodecDNxHD:
|
||||||
case kCodecH264:
|
case kCodecH264:
|
||||||
case kCodecH264rgb:
|
case kCodecH264rgb:
|
||||||
case kCodecH265:
|
case kCodecH265:
|
||||||
case kCodecProRes:
|
case kCodecProRes:
|
||||||
case kCodecCineform:
|
case kCodecCineform:
|
||||||
case kCodecMP2:
|
case kCodecMP2:
|
||||||
case kCodecMP3:
|
case kCodecMP3:
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
case kCodecVorbis:
|
case kCodecVorbis:
|
||||||
case kCodecOpus:
|
case kCodecOpus:
|
||||||
case kCodecVP9:
|
case kCodecVP9:
|
||||||
case kCodecAV1:
|
case kCodecAV1:
|
||||||
case kCodecSRT:
|
case kCodecSRT:
|
||||||
case kCodecOpenEXR:
|
case kCodecOpenEXR:
|
||||||
case kCodecPNG:
|
case kCodecPNG:
|
||||||
case kCodecTIFF:
|
case kCodecTIFF:
|
||||||
case kCodecCount:
|
case kCodecCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-31
@@ -27,43 +27,42 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "render/subtitleparams.h"
|
#include "render/subtitleparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ExportCodec : public QObject
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class ExportCodec : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
// Only append to this list (never insert) because indexes are used in serialized files
|
// Only append to this list (never insert) because indexes are used in serialized files
|
||||||
enum Codec {
|
enum Codec {
|
||||||
kCodecDNxHD,
|
kCodecDNxHD,
|
||||||
kCodecH264,
|
kCodecH264,
|
||||||
kCodecH264rgb,
|
kCodecH264rgb,
|
||||||
kCodecH265,
|
kCodecH265,
|
||||||
kCodecOpenEXR,
|
kCodecOpenEXR,
|
||||||
kCodecPNG,
|
kCodecPNG,
|
||||||
kCodecProRes,
|
kCodecProRes,
|
||||||
kCodecCineform,
|
kCodecCineform,
|
||||||
kCodecTIFF,
|
kCodecTIFF,
|
||||||
kCodecVP9,
|
kCodecVP9,
|
||||||
kCodecMP2,
|
kCodecMP2,
|
||||||
kCodecMP3,
|
kCodecMP3,
|
||||||
kCodecAAC,
|
kCodecAAC,
|
||||||
kCodecPCM,
|
kCodecPCM,
|
||||||
kCodecOpus,
|
kCodecOpus,
|
||||||
kCodecVorbis,
|
kCodecVorbis,
|
||||||
kCodecFLAC,
|
kCodecFLAC,
|
||||||
kCodecSRT,
|
kCodecSRT,
|
||||||
kCodecAV1,
|
kCodecAV1,
|
||||||
|
|
||||||
kCodecCount
|
kCodecCount
|
||||||
};
|
};
|
||||||
|
|
||||||
static QString GetCodecName(Codec c);
|
static QString GetCodecName(Codec c);
|
||||||
|
|
||||||
static bool IsCodecAStillImage(Codec c);
|
static bool IsCodecAStillImage(Codec c);
|
||||||
|
|
||||||
static bool IsCodecLossless(Codec c);
|
|
||||||
|
|
||||||
|
static bool IsCodecLossless(Codec c);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+184
-171
@@ -22,214 +22,227 @@
|
|||||||
|
|
||||||
#include "encoder.h"
|
#include "encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
||||||
{
|
{
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case kFormatDNxHD:
|
case kFormatDNxHD:
|
||||||
return tr("DNxHD");
|
return tr("DNxHD");
|
||||||
case kFormatMatroska:
|
case kFormatMatroska:
|
||||||
return tr("Matroska Video");
|
return tr("Matroska Video");
|
||||||
case kFormatMPEG4Video:
|
case kFormatMPEG4Video:
|
||||||
return tr("MPEG-4 Video");
|
return tr("MPEG-4 Video");
|
||||||
case kFormatMPEG4Audio:
|
case kFormatMPEG4Audio:
|
||||||
return tr("MPEG-4 Audio");
|
return tr("MPEG-4 Audio");
|
||||||
case kFormatOpenEXR:
|
case kFormatOpenEXR:
|
||||||
return tr("OpenEXR");
|
return tr("OpenEXR");
|
||||||
case kFormatPNG:
|
case kFormatPNG:
|
||||||
return tr("PNG");
|
return tr("PNG");
|
||||||
case kFormatTIFF:
|
case kFormatTIFF:
|
||||||
return tr("TIFF");
|
return tr("TIFF");
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return tr("QuickTime");
|
return tr("QuickTime");
|
||||||
case kFormatWAV:
|
case kFormatWAV:
|
||||||
return tr("Wave Audio");
|
return tr("Wave Audio");
|
||||||
case kFormatAIFF:
|
case kFormatAIFF:
|
||||||
return tr("AIFF");
|
return tr("AIFF");
|
||||||
case kFormatMP3:
|
case kFormatMP3:
|
||||||
return tr("MP3");
|
return tr("MP3");
|
||||||
case kFormatFLAC:
|
case kFormatFLAC:
|
||||||
return tr("FLAC");
|
return tr("FLAC");
|
||||||
case kFormatOgg:
|
case kFormatOgg:
|
||||||
return tr("Ogg");
|
return tr("Ogg");
|
||||||
case kFormatWebM:
|
case kFormatWebM:
|
||||||
return tr("WebM");
|
return tr("WebM");
|
||||||
case kFormatSRT:
|
case kFormatSRT:
|
||||||
return tr("SubRip SRT");
|
return tr("SubRip SRT");
|
||||||
|
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tr("Unknown");
|
return tr("Unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ExportFormat::GetExtension(ExportFormat::Format f)
|
QString ExportFormat::GetExtension(ExportFormat::Format f)
|
||||||
{
|
{
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case kFormatDNxHD:
|
case kFormatDNxHD:
|
||||||
return QStringLiteral("mxf");
|
return QStringLiteral("mxf");
|
||||||
case kFormatMatroska:
|
case kFormatMatroska:
|
||||||
return QStringLiteral("mkv");
|
return QStringLiteral("mkv");
|
||||||
case kFormatMPEG4Video:
|
case kFormatMPEG4Video:
|
||||||
return QStringLiteral("mp4");
|
return QStringLiteral("mp4");
|
||||||
case kFormatMPEG4Audio:
|
case kFormatMPEG4Audio:
|
||||||
return QStringLiteral("m4a");
|
return QStringLiteral("m4a");
|
||||||
case kFormatOpenEXR:
|
case kFormatOpenEXR:
|
||||||
return QStringLiteral("exr");
|
return QStringLiteral("exr");
|
||||||
case kFormatPNG:
|
case kFormatPNG:
|
||||||
return QStringLiteral("png");
|
return QStringLiteral("png");
|
||||||
case kFormatTIFF:
|
case kFormatTIFF:
|
||||||
return QStringLiteral("tiff");
|
return QStringLiteral("tiff");
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return QStringLiteral("mov");
|
return QStringLiteral("mov");
|
||||||
case kFormatWAV:
|
case kFormatWAV:
|
||||||
return QStringLiteral("wav");
|
return QStringLiteral("wav");
|
||||||
case kFormatAIFF:
|
case kFormatAIFF:
|
||||||
return QStringLiteral("aiff");
|
return QStringLiteral("aiff");
|
||||||
case kFormatMP3:
|
case kFormatMP3:
|
||||||
return QStringLiteral("mp3");
|
return QStringLiteral("mp3");
|
||||||
case kFormatFLAC:
|
case kFormatFLAC:
|
||||||
return QStringLiteral("flac");
|
return QStringLiteral("flac");
|
||||||
case kFormatOgg:
|
case kFormatOgg:
|
||||||
return QStringLiteral("ogg");
|
return QStringLiteral("ogg");
|
||||||
case kFormatWebM:
|
case kFormatWebM:
|
||||||
return QStringLiteral("webm");
|
return QStringLiteral("webm");
|
||||||
case kFormatSRT:
|
case kFormatSRT:
|
||||||
return QStringLiteral("srt");
|
return QStringLiteral("srt");
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
||||||
{
|
{
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case kFormatDNxHD:
|
case kFormatDNxHD:
|
||||||
return {ExportCodec::kCodecDNxHD};
|
return { ExportCodec::kCodecDNxHD };
|
||||||
case kFormatMatroska:
|
case kFormatMatroska:
|
||||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9};
|
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||||
case kFormatMPEG4Video:
|
ExportCodec::kCodecH265, ExportCodec::kCodecVP9 };
|
||||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265};
|
case kFormatMPEG4Video:
|
||||||
case kFormatOpenEXR:
|
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||||
return {ExportCodec::kCodecOpenEXR};
|
ExportCodec::kCodecH265 };
|
||||||
case kFormatPNG:
|
case kFormatOpenEXR:
|
||||||
return {ExportCodec::kCodecPNG};
|
return { ExportCodec::kCodecOpenEXR };
|
||||||
case kFormatTIFF:
|
case kFormatPNG:
|
||||||
return {ExportCodec::kCodecTIFF};
|
return { ExportCodec::kCodecPNG };
|
||||||
case kFormatQuickTime:
|
case kFormatTIFF:
|
||||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform};
|
return { ExportCodec::kCodecTIFF };
|
||||||
case kFormatWebM:
|
case kFormatQuickTime:
|
||||||
return {ExportCodec::kCodecAV1, ExportCodec::kCodecVP9};
|
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||||
case kFormatOgg:
|
ExportCodec::kCodecH265, ExportCodec::kCodecProRes,
|
||||||
case kFormatWAV:
|
ExportCodec::kCodecCineform };
|
||||||
case kFormatMPEG4Audio:
|
case kFormatWebM:
|
||||||
case kFormatAIFF:
|
return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
|
||||||
case kFormatMP3:
|
case kFormatOgg:
|
||||||
case kFormatFLAC:
|
case kFormatWAV:
|
||||||
case kFormatSRT:
|
case kFormatMPEG4Audio:
|
||||||
case kFormatCount:
|
case kFormatAIFF:
|
||||||
break;
|
case kFormatMP3:
|
||||||
}
|
case kFormatFLAC:
|
||||||
|
case kFormatSRT:
|
||||||
|
case kFormatCount:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
||||||
{
|
{
|
||||||
switch (f) {
|
switch (f) {
|
||||||
// Video/audio formats
|
// Video/audio formats
|
||||||
case kFormatDNxHD:
|
case kFormatDNxHD:
|
||||||
return {ExportCodec::kCodecPCM};
|
return { ExportCodec::kCodecPCM };
|
||||||
case kFormatMatroska:
|
case kFormatMatroska:
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC};
|
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||||
case kFormatMPEG4Video:
|
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM,
|
||||||
case kFormatMPEG4Audio:
|
ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus,
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3};
|
ExportCodec::kCodecFLAC };
|
||||||
case kFormatQuickTime:
|
case kFormatMPEG4Video:
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
case kFormatMPEG4Audio:
|
||||||
case kFormatWebM:
|
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||||
return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis};
|
ExportCodec::kCodecMP3 };
|
||||||
|
case kFormatQuickTime:
|
||||||
|
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||||
|
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM };
|
||||||
|
case kFormatWebM:
|
||||||
|
return { ExportCodec::kCodecOpus, ExportCodec::kCodecAAC,
|
||||||
|
ExportCodec::kCodecMP2, ExportCodec::kCodecMP3,
|
||||||
|
ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis };
|
||||||
|
|
||||||
// Audio only formats
|
// Audio only formats
|
||||||
case kFormatWAV:
|
case kFormatWAV:
|
||||||
return {ExportCodec::kCodecPCM};
|
return { ExportCodec::kCodecPCM };
|
||||||
case kFormatAIFF:
|
case kFormatAIFF:
|
||||||
return {ExportCodec::kCodecPCM};
|
return { ExportCodec::kCodecPCM };
|
||||||
case kFormatMP3:
|
case kFormatMP3:
|
||||||
return {ExportCodec::kCodecMP3};
|
return { ExportCodec::kCodecMP3 };
|
||||||
case kFormatFLAC:
|
case kFormatFLAC:
|
||||||
return {ExportCodec::kCodecFLAC};
|
return { ExportCodec::kCodecFLAC };
|
||||||
case kFormatOgg:
|
case kFormatOgg:
|
||||||
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM};
|
return { ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis,
|
||||||
|
ExportCodec::kCodecPCM };
|
||||||
|
|
||||||
// Video only formats
|
// Video only formats
|
||||||
case kFormatOpenEXR:
|
case kFormatOpenEXR:
|
||||||
case kFormatPNG:
|
case kFormatPNG:
|
||||||
case kFormatTIFF:
|
case kFormatTIFF:
|
||||||
case kFormatSRT:
|
case kFormatSRT:
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
return {};
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
|
QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
|
||||||
{
|
{
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case kFormatDNxHD:
|
case kFormatDNxHD:
|
||||||
case kFormatMPEG4Video:
|
case kFormatMPEG4Video:
|
||||||
case kFormatMPEG4Audio:
|
case kFormatMPEG4Audio:
|
||||||
case kFormatOpenEXR:
|
case kFormatOpenEXR:
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
case kFormatPNG:
|
case kFormatPNG:
|
||||||
case kFormatTIFF:
|
case kFormatTIFF:
|
||||||
case kFormatWAV:
|
case kFormatWAV:
|
||||||
case kFormatAIFF:
|
case kFormatAIFF:
|
||||||
case kFormatMP3:
|
case kFormatMP3:
|
||||||
case kFormatFLAC:
|
case kFormatFLAC:
|
||||||
case kFormatOgg:
|
case kFormatOgg:
|
||||||
case kFormatWebM:
|
case kFormatWebM:
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
case kFormatMatroska:
|
case kFormatMatroska:
|
||||||
case kFormatSRT:
|
case kFormatSRT:
|
||||||
return {ExportCodec::kCodecSRT};
|
return { ExportCodec::kCodecSRT };
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c)
|
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f,
|
||||||
|
ExportCodec::Codec c)
|
||||||
{
|
{
|
||||||
Encoder* e = Encoder::CreateFromFormat(f, EncodingParams());
|
Encoder *e = Encoder::CreateFromFormat(f, EncodingParams());
|
||||||
QStringList list;
|
QStringList list;
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
list = e->GetPixelFormatsForCodec(c);
|
list = e->GetPixelFormatsForCodec(c);
|
||||||
delete e;
|
delete e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
|
std::vector<SampleFormat>
|
||||||
|
ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
|
||||||
{
|
{
|
||||||
std::vector<SampleFormat> f;
|
std::vector<SampleFormat> f;
|
||||||
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
|
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
f = e->GetSampleFormatsForCodec(c);
|
f = e->GetSampleFormatsForCodec(c);
|
||||||
delete e;
|
delete e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return f;
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-31
@@ -27,42 +27,42 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "exportcodec.h"
|
#include "exportcodec.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ExportFormat : public QObject
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class ExportFormat : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
// Only append to this list (never insert) because indexes are used in serialized files
|
// Only append to this list (never insert) because indexes are used in serialized files
|
||||||
enum Format {
|
enum Format {
|
||||||
kFormatDNxHD,
|
kFormatDNxHD,
|
||||||
kFormatMatroska,
|
kFormatMatroska,
|
||||||
kFormatMPEG4Video,
|
kFormatMPEG4Video,
|
||||||
kFormatOpenEXR,
|
kFormatOpenEXR,
|
||||||
kFormatQuickTime,
|
kFormatQuickTime,
|
||||||
kFormatPNG,
|
kFormatPNG,
|
||||||
kFormatTIFF,
|
kFormatTIFF,
|
||||||
kFormatWAV,
|
kFormatWAV,
|
||||||
kFormatAIFF,
|
kFormatAIFF,
|
||||||
kFormatMP3,
|
kFormatMP3,
|
||||||
kFormatFLAC,
|
kFormatFLAC,
|
||||||
kFormatOgg,
|
kFormatOgg,
|
||||||
kFormatWebM,
|
kFormatWebM,
|
||||||
kFormatSRT,
|
kFormatSRT,
|
||||||
kFormatMPEG4Audio,
|
kFormatMPEG4Audio,
|
||||||
|
|
||||||
kFormatCount
|
kFormatCount
|
||||||
};
|
};
|
||||||
|
|
||||||
static QString GetName(Format f);
|
static QString GetName(Format f);
|
||||||
static QString GetExtension(Format f);
|
static QString GetExtension(Format f);
|
||||||
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
|
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
|
||||||
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
|
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
|
||||||
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
|
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
|
||||||
|
|
||||||
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
|
|
||||||
static std::vector<SampleFormat> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
|
|
||||||
|
|
||||||
|
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||||
|
static std::vector<SampleFormat>
|
||||||
|
GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+936
-860
File diff suppressed because it is too large
Load Diff
@@ -39,92 +39,100 @@ extern "C" {
|
|||||||
#include "codec/decoder.h"
|
#include "codec/decoder.h"
|
||||||
#include "common/ffmpegutils.h"
|
#include "common/ffmpegutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||||
*/
|
*/
|
||||||
class FFmpegDecoder : public Decoder
|
class FFmpegDecoder : public Decoder {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
FFmpegDecoder();
|
FFmpegDecoder();
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
|
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
|
||||||
|
|
||||||
virtual QString id() const override;
|
virtual QString id() const override;
|
||||||
|
|
||||||
virtual bool SupportsVideo() override{return true;}
|
virtual bool SupportsVideo() override
|
||||||
virtual bool SupportsAudio() override{return true;}
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool SupportsAudio() override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
virtual FootageDescription Probe(const QString &filename, CancelAtom *cancelled) const override;
|
virtual FootageDescription Probe(const QString &filename,
|
||||||
|
CancelAtom *cancelled) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool OpenInternal() override;
|
virtual bool OpenInternal() override;
|
||||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
|
virtual TexturePtr
|
||||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, CancelAtom *cancelled) override;
|
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
|
||||||
virtual void CloseInternal() override;
|
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
|
||||||
|
const AudioParams ¶ms,
|
||||||
|
CancelAtom *cancelled) override;
|
||||||
|
virtual void CloseInternal() override;
|
||||||
|
|
||||||
virtual rational GetAudioStartOffset() const override;
|
virtual rational GetAudioStartOffset() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Instance
|
class Instance {
|
||||||
{
|
public:
|
||||||
public:
|
Instance();
|
||||||
Instance();
|
|
||||||
|
|
||||||
~Instance()
|
~Instance()
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Open(const char* filename, int stream_index);
|
bool Open(const char *filename, int stream_index);
|
||||||
|
|
||||||
bool IsOpen() const
|
bool IsOpen() const
|
||||||
{
|
{
|
||||||
return fmt_ctx_;
|
return fmt_ctx_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
|
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
* An FFmpeg error code, or >= 0 on success
|
* An FFmpeg error code, or >= 0 on success
|
||||||
*/
|
*/
|
||||||
int GetFrame(AVPacket* pkt, AVFrame* frame);
|
int GetFrame(AVPacket *pkt, AVFrame *frame);
|
||||||
|
|
||||||
const char *GetSubtitleHeader() const;
|
const char *GetSubtitleHeader() const;
|
||||||
|
|
||||||
int GetSubtitle(AVPacket* pkt, AVSubtitle* sub);
|
int GetSubtitle(AVPacket *pkt, AVSubtitle *sub);
|
||||||
|
|
||||||
int GetPacket(AVPacket *pkt);
|
int GetPacket(AVPacket *pkt);
|
||||||
|
|
||||||
void Seek(int64_t timestamp);
|
void Seek(int64_t timestamp);
|
||||||
|
|
||||||
AVFormatContext* fmt_ctx() const
|
AVFormatContext *fmt_ctx() const
|
||||||
{
|
{
|
||||||
return fmt_ctx_;
|
return fmt_ctx_;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVStream* avstream() const
|
AVStream *avstream() const
|
||||||
{
|
{
|
||||||
return avstream_;
|
return avstream_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AVFormatContext* fmt_ctx_;
|
AVFormatContext *fmt_ctx_;
|
||||||
AVCodecContext* codec_ctx_;
|
AVCodecContext *codec_ctx_;
|
||||||
AVStream* avstream_;
|
AVStream *avstream_;
|
||||||
AVDictionary* opts_;
|
AVDictionary *opts_;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
/**
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handle an FFmpeg error code
|
* @brief Handle an FFmpeg error code
|
||||||
*
|
*
|
||||||
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
|
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
|
||||||
@@ -132,54 +140,56 @@ private:
|
|||||||
*
|
*
|
||||||
* @param error_code
|
* @param error_code
|
||||||
*/
|
*/
|
||||||
static QString FFmpegError(int error_code);
|
static QString FFmpegError(int error_code);
|
||||||
|
|
||||||
void FreeScaler();
|
void FreeScaler();
|
||||||
|
|
||||||
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
|
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
|
||||||
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
|
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
|
||||||
|
|
||||||
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
|
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
|
||||||
|
|
||||||
static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
|
static const char *
|
||||||
|
GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
|
||||||
|
|
||||||
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
|
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
|
||||||
|
|
||||||
AVFramePtr GetFrameFromCache(const int64_t &t) const;
|
AVFramePtr GetFrameFromCache(const int64_t &t) const;
|
||||||
|
|
||||||
void ClearFrameCache();
|
void ClearFrameCache();
|
||||||
|
|
||||||
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
|
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
|
||||||
|
|
||||||
TexturePtr ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original);
|
TexturePtr ProcessFrameIntoTexture(AVFramePtr f,
|
||||||
|
const RetrieveVideoParams &p,
|
||||||
|
const AVFramePtr original);
|
||||||
|
|
||||||
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
|
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
|
||||||
|
|
||||||
void RemoveFirstFrame();
|
void RemoveFirstFrame();
|
||||||
|
|
||||||
static int MaximumQueueSize();
|
static int MaximumQueueSize();
|
||||||
|
|
||||||
SwsContext *sws_ctx_;
|
SwsContext *sws_ctx_;
|
||||||
int sws_src_width_;
|
int sws_src_width_;
|
||||||
int sws_src_height_;
|
int sws_src_height_;
|
||||||
AVPixelFormat sws_src_format_;
|
AVPixelFormat sws_src_format_;
|
||||||
int sws_dst_width_;
|
int sws_dst_width_;
|
||||||
int sws_dst_height_;
|
int sws_dst_height_;
|
||||||
AVPixelFormat sws_dst_format_;
|
AVPixelFormat sws_dst_format_;
|
||||||
AVColorRange sws_colrange_;
|
AVColorRange sws_colrange_;
|
||||||
AVColorSpace sws_colspace_;
|
AVColorSpace sws_colspace_;
|
||||||
|
|
||||||
AVPacket *working_packet_;
|
AVPacket *working_packet_;
|
||||||
|
|
||||||
int64_t second_ts_;
|
int64_t second_ts_;
|
||||||
|
|
||||||
std::list<AVFramePtr> cached_frames_;
|
std::list<AVFramePtr> cached_frames_;
|
||||||
|
|
||||||
bool cache_at_zero_;
|
bool cache_at_zero_;
|
||||||
bool cache_at_eof_;
|
bool cache_at_eof_;
|
||||||
|
|
||||||
Instance instance_;
|
|
||||||
|
|
||||||
|
Instance instance_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+741
-661
File diff suppressed because it is too large
Load Diff
@@ -31,37 +31,41 @@ extern "C" {
|
|||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "codec/encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class FFmpegEncoder : public Encoder
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class FFmpegEncoder : public Encoder {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
FFmpegEncoder(const EncodingParams ¶ms);
|
FFmpegEncoder(const EncodingParams ¶ms);
|
||||||
|
|
||||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
|
virtual QStringList
|
||||||
|
GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
|
||||||
|
|
||||||
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
|
virtual std::vector<SampleFormat>
|
||||||
|
GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
|
||||||
|
|
||||||
virtual bool Open() override;
|
virtual bool Open() override;
|
||||||
|
|
||||||
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override;
|
virtual bool WriteFrame(olive::FramePtr frame,
|
||||||
|
olive::core::rational time) override;
|
||||||
|
|
||||||
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
|
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
|
||||||
|
|
||||||
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count);
|
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data,
|
||||||
|
int input_sample_count);
|
||||||
|
|
||||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||||
|
|
||||||
virtual void Close() override;
|
virtual void Close() override;
|
||||||
|
|
||||||
virtual PixelFormat GetDesiredPixelFormat() const override
|
virtual PixelFormat GetDesiredPixelFormat() const override
|
||||||
{
|
{
|
||||||
return video_conversion_fmt_;
|
return video_conversion_fmt_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Handle an FFmpeg error code
|
* @brief Handle an FFmpeg error code
|
||||||
*
|
*
|
||||||
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
|
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
|
||||||
@@ -69,43 +73,48 @@ private:
|
|||||||
*
|
*
|
||||||
* @param error_code
|
* @param error_code
|
||||||
*/
|
*/
|
||||||
void FFmpegError(const QString &context, int error_code);
|
void FFmpegError(const QString &context, int error_code);
|
||||||
|
|
||||||
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream);
|
bool WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
|
||||||
|
AVStream *stream);
|
||||||
|
|
||||||
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec);
|
bool InitializeStream(enum AVMediaType type, AVStream **stream,
|
||||||
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, const AVCodec* codec);
|
AVCodecContext **codec_ctx,
|
||||||
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, const AVCodec *codec);
|
const ExportCodec::Codec &codec);
|
||||||
|
bool InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx,
|
||||||
|
const AVCodec *codec);
|
||||||
|
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx,
|
||||||
|
const AVCodec *codec);
|
||||||
|
|
||||||
void FlushEncoders();
|
void FlushEncoders();
|
||||||
void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream);
|
void FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream);
|
||||||
|
|
||||||
bool InitializeResampleContext(const AudioParams &audio);
|
bool InitializeResampleContext(const AudioParams &audio);
|
||||||
|
|
||||||
static const AVCodec *GetEncoder(ExportCodec::Codec c, SampleFormat aformat);
|
static const AVCodec *GetEncoder(ExportCodec::Codec c,
|
||||||
|
SampleFormat aformat);
|
||||||
|
|
||||||
AVFormatContext* fmt_ctx_;
|
AVFormatContext *fmt_ctx_;
|
||||||
|
|
||||||
AVStream* video_stream_;
|
AVStream *video_stream_;
|
||||||
AVCodecContext* video_codec_ctx_;
|
AVCodecContext *video_codec_ctx_;
|
||||||
AVFilterGraph *video_scale_ctx_;
|
AVFilterGraph *video_scale_ctx_;
|
||||||
AVFilterContext *video_buffersrc_ctx_;
|
AVFilterContext *video_buffersrc_ctx_;
|
||||||
AVFilterContext *video_buffersink_ctx_;
|
AVFilterContext *video_buffersink_ctx_;
|
||||||
PixelFormat video_conversion_fmt_;
|
PixelFormat video_conversion_fmt_;
|
||||||
|
|
||||||
AVStream* audio_stream_;
|
AVStream *audio_stream_;
|
||||||
AVCodecContext* audio_codec_ctx_;
|
AVCodecContext *audio_codec_ctx_;
|
||||||
SwrContext* audio_resample_ctx_;
|
SwrContext *audio_resample_ctx_;
|
||||||
AVFrame* audio_frame_;
|
AVFrame *audio_frame_;
|
||||||
int audio_max_samples_;
|
int audio_max_samples_;
|
||||||
int audio_frame_offset_;
|
int audio_frame_offset_;
|
||||||
int audio_write_count_;
|
int audio_write_count_;
|
||||||
|
|
||||||
AVStream* subtitle_stream_;
|
AVStream *subtitle_stream_;
|
||||||
AVCodecContext* subtitle_codec_ctx_;
|
AVCodecContext *subtitle_codec_ctx_;
|
||||||
|
|
||||||
bool open_;
|
|
||||||
|
|
||||||
|
bool open_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-79
@@ -28,153 +28,161 @@
|
|||||||
#include "common/oiioutils.h"
|
#include "common/oiioutils.h"
|
||||||
#include "render/framemanager.h"
|
#include "render/framemanager.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
Frame::Frame() :
|
Frame::Frame()
|
||||||
data_(nullptr),
|
: data_(nullptr)
|
||||||
data_size_(0),
|
, data_size_(0)
|
||||||
timestamp_(0)
|
, timestamp_(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Frame::~Frame()
|
Frame::~Frame()
|
||||||
{
|
{
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
FramePtr Frame::Create()
|
FramePtr Frame::Create()
|
||||||
{
|
{
|
||||||
return std::make_shared<Frame>();
|
return std::make_shared<Frame>();
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoParams &Frame::video_params() const
|
const VideoParams &Frame::video_params() const
|
||||||
{
|
{
|
||||||
return params_;
|
return params_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frame::set_video_params(const VideoParams ¶ms)
|
void Frame::set_video_params(const VideoParams ¶ms)
|
||||||
{
|
{
|
||||||
params_ = params;
|
params_ = params;
|
||||||
|
|
||||||
linesize_ = generate_linesize_bytes(width(), params_.format(), params_.channel_count());
|
linesize_ = generate_linesize_bytes(width(), params_.format(),
|
||||||
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
|
params_.channel_count());
|
||||||
|
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
|
||||||
}
|
}
|
||||||
|
|
||||||
FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
|
FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
|
||||||
{
|
{
|
||||||
if (top->video_params() != bottom->video_params()) {
|
if (top->video_params() != bottom->video_params()) {
|
||||||
qCritical() << "Tried to interlace two frames that had incompatible parameters";
|
qCritical()
|
||||||
return nullptr;
|
<< "Tried to interlace two frames that had incompatible parameters";
|
||||||
}
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
FramePtr interlaced = Frame::Create();
|
FramePtr interlaced = Frame::Create();
|
||||||
interlaced->set_video_params(top->video_params());
|
interlaced->set_video_params(top->video_params());
|
||||||
interlaced->allocate();
|
interlaced->allocate();
|
||||||
|
|
||||||
int linesize = interlaced->linesize_bytes();
|
int linesize = interlaced->linesize_bytes();
|
||||||
|
|
||||||
for (int i=0; i<interlaced->height(); i++) {
|
for (int i = 0; i < interlaced->height(); i++) {
|
||||||
FramePtr which = (i%2 == 0) ? top : bottom;
|
FramePtr which = (i % 2 == 0) ? top : bottom;
|
||||||
|
|
||||||
memcpy(interlaced->data() + i*linesize,
|
memcpy(interlaced->data() + i * linesize,
|
||||||
which->const_data() + i*linesize,
|
which->const_data() + i * linesize, linesize);
|
||||||
linesize);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return interlaced;
|
return interlaced;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame::generate_linesize_bytes(int width, PixelFormat format, int channel_count)
|
int Frame::generate_linesize_bytes(int width, PixelFormat format,
|
||||||
|
int channel_count)
|
||||||
{
|
{
|
||||||
// Align to 32 bytes (not sure if this is necessary?)
|
// Align to 32 bytes (not sure if this is necessary?)
|
||||||
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31);
|
return VideoParams::GetBytesPerPixel(format, channel_count) *
|
||||||
|
((width + 31) & ~31);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color Frame::get_pixel(int x, int y) const
|
Color Frame::get_pixel(int x, int y) const
|
||||||
{
|
{
|
||||||
if (!contains_pixel(x, y)) {
|
if (!contains_pixel(x, y)) {
|
||||||
return Color();
|
return Color();
|
||||||
}
|
}
|
||||||
|
|
||||||
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
|
int byte_offset =
|
||||||
|
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
|
||||||
|
|
||||||
return Color(reinterpret_cast<const char*>(data_ + byte_offset), video_params().format(), video_params().channel_count());
|
return Color(reinterpret_cast<const char *>(data_ + byte_offset),
|
||||||
|
video_params().format(), video_params().channel_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Frame::contains_pixel(int x, int y) const
|
bool Frame::contains_pixel(int x, int y) const
|
||||||
{
|
{
|
||||||
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
|
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frame::set_pixel(int x, int y, const Color &c)
|
void Frame::set_pixel(int x, int y, const Color &c)
|
||||||
{
|
{
|
||||||
if (!contains_pixel(x, y)) {
|
if (!contains_pixel(x, y)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
|
int byte_offset =
|
||||||
|
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
|
||||||
|
|
||||||
c.toData(reinterpret_cast<char*>(data_ + byte_offset), video_params().format(), video_params().channel_count());
|
c.toData(reinterpret_cast<char *>(data_ + byte_offset),
|
||||||
|
video_params().format(), video_params().channel_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Frame::allocate()
|
bool Frame::allocate()
|
||||||
{
|
{
|
||||||
// Assume this frame is intended to be a video frame
|
// Assume this frame is intended to be a video frame
|
||||||
if (!params_.is_valid()) {
|
if (!params_.is_valid()) {
|
||||||
qWarning() << "Tried to allocate a frame with invalid parameters";
|
qWarning() << "Tried to allocate a frame with invalid parameters";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_allocated()) {
|
if (is_allocated()) {
|
||||||
// Already allocated
|
// Already allocated
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
data_size_ = linesize_ * height();
|
data_size_ = linesize_ * height();
|
||||||
data_ = FrameManager::Allocate(data_size_);
|
data_ = FrameManager::Allocate(data_size_);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frame::destroy()
|
void Frame::destroy()
|
||||||
{
|
{
|
||||||
if (is_allocated()) {
|
if (is_allocated()) {
|
||||||
FrameManager::Deallocate(data_size_, data_);
|
FrameManager::Deallocate(data_size_, data_);
|
||||||
|
|
||||||
data_size_ = 0;
|
data_size_ = 0;
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FramePtr Frame::convert(PixelFormat format) const
|
FramePtr Frame::convert(PixelFormat format) const
|
||||||
{
|
{
|
||||||
// Create new params with destination format
|
// Create new params with destination format
|
||||||
VideoParams params = params_;
|
VideoParams params = params_;
|
||||||
params.set_format(format);
|
params.set_format(format);
|
||||||
|
|
||||||
// Create new frame
|
// Create new frame
|
||||||
FramePtr converted = Frame::Create();
|
FramePtr converted = Frame::Create();
|
||||||
converted->set_video_params(params);
|
converted->set_video_params(params);
|
||||||
converted->set_timestamp(timestamp_);
|
converted->set_timestamp(timestamp_);
|
||||||
converted->allocate();
|
converted->allocate();
|
||||||
|
|
||||||
// Do the conversion through OIIO for convenience
|
// Do the conversion through OIIO for convenience
|
||||||
OIIO::ImageBuf src(OIIO::ImageSpec(width(), height(),
|
OIIO::ImageBuf src(
|
||||||
channel_count(),
|
OIIO::ImageSpec(width(), height(), channel_count(),
|
||||||
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
|
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
|
||||||
|
|
||||||
OIIOUtils::FrameToBuffer(this, &src);
|
OIIOUtils::FrameToBuffer(this, &src);
|
||||||
|
|
||||||
OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(),
|
OIIO::ImageBuf dst(OIIO::ImageSpec(
|
||||||
channel_count(),
|
converted->width(), converted->height(), channel_count(),
|
||||||
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
|
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
|
||||||
|
|
||||||
if (dst.copy_pixels(src)) {
|
if (dst.copy_pixels(src)) {
|
||||||
OIIOUtils::BufferToFrame(&dst, converted.get());
|
OIIOUtils::BufferToFrame(&dst, converted.get());
|
||||||
return converted;
|
return converted;
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-79
@@ -28,7 +28,8 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class Frame;
|
class Frame;
|
||||||
using FramePtr = std::shared_ptr<Frame>;
|
using FramePtr = std::shared_ptr<Frame>;
|
||||||
@@ -36,135 +37,134 @@ using FramePtr = std::shared_ptr<Frame>;
|
|||||||
/**
|
/**
|
||||||
* @brief Video frame data or audio sample data from a Decoder
|
* @brief Video frame data or audio sample data from a Decoder
|
||||||
*/
|
*/
|
||||||
class Frame
|
class Frame {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Frame();
|
Frame();
|
||||||
|
|
||||||
~Frame();
|
~Frame();
|
||||||
|
|
||||||
DISABLE_COPY_MOVE(Frame)
|
DISABLE_COPY_MOVE(Frame)
|
||||||
|
|
||||||
static FramePtr Create();
|
static FramePtr Create();
|
||||||
|
|
||||||
const VideoParams& video_params() const;
|
const VideoParams &video_params() const;
|
||||||
void set_video_params(const VideoParams& params);
|
void set_video_params(const VideoParams ¶ms);
|
||||||
|
|
||||||
static FramePtr Interlace(FramePtr top, FramePtr bottom);
|
static FramePtr Interlace(FramePtr top, FramePtr bottom);
|
||||||
|
|
||||||
static int generate_linesize_bytes(int width, PixelFormat format, int channel_count);
|
static int generate_linesize_bytes(int width, PixelFormat format,
|
||||||
|
int channel_count);
|
||||||
|
|
||||||
int linesize_pixels() const
|
int linesize_pixels() const
|
||||||
{
|
{
|
||||||
return linesize_pixels_;
|
return linesize_pixels_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int linesize_bytes() const
|
int linesize_bytes() const
|
||||||
{
|
{
|
||||||
return linesize_;
|
return linesize_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int width() const
|
int width() const
|
||||||
{
|
{
|
||||||
return params_.effective_width();
|
return params_.effective_width();
|
||||||
}
|
}
|
||||||
|
|
||||||
int height() const
|
int height() const
|
||||||
{
|
{
|
||||||
return params_.effective_height();
|
return params_.effective_height();
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelFormat format() const
|
PixelFormat format() const
|
||||||
{
|
{
|
||||||
return params_.format();
|
return params_.format();
|
||||||
}
|
}
|
||||||
|
|
||||||
int channel_count() const
|
int channel_count() const
|
||||||
{
|
{
|
||||||
return params_.channel_count();
|
return params_.channel_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
Color get_pixel(int x, int y) const;
|
Color get_pixel(int x, int y) const;
|
||||||
bool contains_pixel(int x, int y) const;
|
bool contains_pixel(int x, int y) const;
|
||||||
void set_pixel(int x, int y, const Color& c);
|
void set_pixel(int x, int y, const Color &c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get frame's timestamp.
|
* @brief Get frame's timestamp.
|
||||||
*
|
*
|
||||||
* This timestamp is always a rational that will equate to the time in seconds.
|
* This timestamp is always a rational that will equate to the time in seconds.
|
||||||
*/
|
*/
|
||||||
const rational& timestamp() const
|
const rational ×tamp() const
|
||||||
{
|
{
|
||||||
return timestamp_;
|
return timestamp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_timestamp(const rational& timestamp)
|
void set_timestamp(const rational ×tamp)
|
||||||
{
|
{
|
||||||
timestamp_ = timestamp;
|
timestamp_ = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the data buffer of this frame
|
* @brief Get the data buffer of this frame
|
||||||
*/
|
*/
|
||||||
char* data()
|
char *data()
|
||||||
{
|
{
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the const data buffer of this frame
|
* @brief Get the const data buffer of this frame
|
||||||
*/
|
*/
|
||||||
const char* const_data() const
|
const char *const_data() const
|
||||||
{
|
{
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocate memory buffer to store data based on parameters
|
* @brief Allocate memory buffer to store data based on parameters
|
||||||
*
|
*
|
||||||
* For video frames, the width(), height(), and format() must be set for this function to work.
|
* For video frames, the width(), height(), and format() must be set for this function to work.
|
||||||
*
|
*
|
||||||
* If a memory buffer has been previously allocated without destroying, this function will destroy it.
|
* If a memory buffer has been previously allocated without destroying, this function will destroy it.
|
||||||
*/
|
*/
|
||||||
bool allocate();
|
bool allocate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return whether the frame is allocated or not
|
* @brief Return whether the frame is allocated or not
|
||||||
*/
|
*/
|
||||||
bool is_allocated() const
|
bool is_allocated() const
|
||||||
{
|
{
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destroy a memory buffer allocated with allocate()
|
* @brief Destroy a memory buffer allocated with allocate()
|
||||||
*/
|
*/
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the size of the array returned in data() in bytes
|
* @brief Returns the size of the array returned in data() in bytes
|
||||||
*
|
*
|
||||||
* Returns 0 if nothing is allocated.
|
* Returns 0 if nothing is allocated.
|
||||||
*/
|
*/
|
||||||
int allocated_size() const
|
int allocated_size() const
|
||||||
{
|
{
|
||||||
return data_size_;
|
return data_size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
FramePtr convert(PixelFormat format) const;
|
FramePtr convert(PixelFormat format) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VideoParams params_;
|
VideoParams params_;
|
||||||
|
|
||||||
char* data_;
|
char *data_;
|
||||||
int data_size_;
|
int data_size_;
|
||||||
|
|
||||||
rational timestamp_;
|
rational timestamp_;
|
||||||
|
|
||||||
int linesize_;
|
int linesize_;
|
||||||
|
|
||||||
int linesize_pixels_;
|
|
||||||
|
|
||||||
|
int linesize_pixels_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+150
-133
@@ -31,217 +31,234 @@
|
|||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "render/renderer.h"
|
#include "render/renderer.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
QStringList OIIODecoder::supported_formats_;
|
QStringList OIIODecoder::supported_formats_;
|
||||||
|
|
||||||
OIIODecoder::OIIODecoder() :
|
OIIODecoder::OIIODecoder()
|
||||||
image_(nullptr)
|
: image_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OIIODecoder::id() const
|
QString OIIODecoder::id() const
|
||||||
{
|
{
|
||||||
return QStringLiteral("oiio");
|
return QStringLiteral("oiio");
|
||||||
}
|
}
|
||||||
|
|
||||||
FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cancelled) const
|
FootageDescription OIIODecoder::Probe(const QString &filename,
|
||||||
|
CancelAtom *cancelled) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(cancelled)
|
Q_UNUSED(cancelled)
|
||||||
|
|
||||||
FootageDescription desc(id());
|
FootageDescription desc(id());
|
||||||
|
|
||||||
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
|
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
|
||||||
// to open a file that it can't if it's given one
|
// to open a file that it can't if it's given one
|
||||||
if (!FileTypeIsSupported(filename)) {
|
if (!FileTypeIsSupported(filename)) {
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string std_filename = filename.toStdString();
|
std::string std_filename = filename.toStdString();
|
||||||
|
|
||||||
auto in = OIIO::ImageInput::open(std_filename);
|
auto in = OIIO::ImageInput::open(std_filename);
|
||||||
|
|
||||||
if (!in) {
|
if (!in) {
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
|
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
|
||||||
// it better
|
// it better
|
||||||
if (!strcmp(in->format_name(), "FFmpeg movie")) {
|
if (!strcmp(in->format_name(), "FFmpeg movie")) {
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool stream_enabled = true;
|
bool stream_enabled = true;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i=0; in->seek_subimage(i, 0); i++) {
|
for (i = 0; in->seek_subimage(i, 0); i++) {
|
||||||
OIIO::ImageSpec spec = in->spec();
|
OIIO::ImageSpec spec = in->spec();
|
||||||
|
|
||||||
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
|
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
|
||||||
|
|
||||||
video_params.set_stream_index(i);
|
video_params.set_stream_index(i);
|
||||||
|
|
||||||
if (i > 1) {
|
if (i > 1) {
|
||||||
// This is a multilayer image and this image might have an offset
|
// This is a multilayer image and this image might have an offset
|
||||||
OIIO::ImageSpec root_spec = in->spec(0);
|
OIIO::ImageSpec root_spec = in->spec(0);
|
||||||
|
|
||||||
float norm_x = spec.x + float(spec.width)*0.5f - float(root_spec.width)*0.5f;
|
float norm_x = spec.x + float(spec.width) * 0.5f -
|
||||||
float norm_y = spec.y + float(spec.height)*0.5f - float(root_spec.height)*0.5f;
|
float(root_spec.width) * 0.5f;
|
||||||
|
float norm_y = spec.y + float(spec.height) * 0.5f -
|
||||||
|
float(root_spec.height) * 0.5f;
|
||||||
|
|
||||||
video_params.set_x(norm_x);
|
video_params.set_x(norm_x);
|
||||||
video_params.set_y(norm_y);
|
video_params.set_y(norm_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, only enable the first subimage (presumably the combined image). Later we will
|
// By default, only enable the first subimage (presumably the combined image). Later we will
|
||||||
// ask the user if they want to enable the layers instead.
|
// ask the user if they want to enable the layers instead.
|
||||||
video_params.set_enabled(stream_enabled);
|
video_params.set_enabled(stream_enabled);
|
||||||
stream_enabled = false;
|
stream_enabled = false;
|
||||||
|
|
||||||
// OIIO automatically premultiplies alpha
|
// OIIO automatically premultiplies alpha
|
||||||
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
|
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
|
||||||
// likely reduces the fidelity?
|
// likely reduces the fidelity?
|
||||||
video_params.set_premultiplied_alpha(true);
|
video_params.set_premultiplied_alpha(true);
|
||||||
|
|
||||||
desc.AddVideoStream(video_params);
|
desc.AddVideoStream(video_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
desc.SetStreamCount(i);
|
desc.SetStreamCount(i);
|
||||||
|
|
||||||
// If we're here, we have a successful image open
|
// If we're here, we have a successful image open
|
||||||
in->close();
|
in->close();
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIODecoder::OpenInternal()
|
bool OIIODecoder::OpenInternal()
|
||||||
{
|
{
|
||||||
// If we can open the filename provided, assume everything is working
|
// If we can open the filename provided, assume everything is working
|
||||||
return OpenImageHandler(stream().filename(), stream().stream());
|
return OpenImageHandler(stream().filename(), stream().stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||||
{
|
{
|
||||||
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
|
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
|
||||||
vp.set_divider(p.divider);
|
vp.set_divider(p.divider);
|
||||||
|
|
||||||
if (!buffer_.is_allocated()
|
if (!buffer_.is_allocated() || last_params_.divider != p.divider) {
|
||||||
|| last_params_.divider != p.divider) {
|
last_params_ = p;
|
||||||
last_params_ = p;
|
|
||||||
|
|
||||||
buffer_.destroy();
|
buffer_.destroy();
|
||||||
buffer_.set_video_params(vp);
|
buffer_.set_video_params(vp);
|
||||||
buffer_.allocate();
|
buffer_.allocate();
|
||||||
|
|
||||||
if (p.divider == 1) {
|
if (p.divider == 1) {
|
||||||
// Just upload straight to the buffer
|
// Just upload straight to the buffer
|
||||||
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
|
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
|
||||||
} else {
|
} else {
|
||||||
OIIO::ImageBuf buf(image_->spec());
|
OIIO::ImageBuf buf(image_->spec());
|
||||||
image_->read_image(0,0,0,-1,image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride());
|
image_->read_image(0, 0, 0, -1, image_->spec().format,
|
||||||
|
buf.localpixels(), buf.pixel_stride(),
|
||||||
|
buf.scanline_stride(), buf.z_stride());
|
||||||
|
|
||||||
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
|
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
|
||||||
int px_sz = vp.GetBytesPerPixel();
|
int px_sz = vp.GetBytesPerPixel();
|
||||||
for (int dst_y=0; dst_y<buffer_.height(); dst_y++) {
|
for (int dst_y = 0; dst_y < buffer_.height(); dst_y++) {
|
||||||
int src_y = dst_y * buf.spec().height / buffer_.height();
|
int src_y = dst_y * buf.spec().height / buffer_.height();
|
||||||
|
|
||||||
for (int dst_x=0; dst_x<buffer_.width(); dst_x++) {
|
for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) {
|
||||||
int src_x = dst_x * buf.spec().width / buffer_.width();
|
int src_x = dst_x * buf.spec().width / buffer_.width();
|
||||||
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x,
|
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y +
|
||||||
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x,
|
px_sz * dst_x,
|
||||||
px_sz);
|
static_cast<uint8_t *>(buf.localpixels()) +
|
||||||
}
|
buf.scanline_stride() * src_y + px_sz * src_x,
|
||||||
}
|
px_sz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
|
return p.renderer->CreateTexture(vp, buffer_.data(),
|
||||||
|
buffer_.linesize_pixels());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OIIODecoder::CloseInternal()
|
void OIIODecoder::CloseInternal()
|
||||||
{
|
{
|
||||||
CloseImageHandle();
|
CloseImageHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
|
bool OIIODecoder::FileTypeIsSupported(const QString &fn)
|
||||||
{
|
{
|
||||||
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
|
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
|
||||||
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
|
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
|
||||||
// "extension_list" attribute and match it with the extension of the file.
|
// "extension_list" attribute and match it with the extension of the file.
|
||||||
|
|
||||||
// Check if we've created the supported formats list, create it if not
|
// Check if we've created the supported formats list, create it if not
|
||||||
if (supported_formats_.isEmpty()) {
|
if (supported_formats_.isEmpty()) {
|
||||||
QStringList extension_list = QString::fromStdString(OIIO::get_string_attribute("extension_list")).split(';');
|
QStringList extension_list =
|
||||||
|
QString::fromStdString(OIIO::get_string_attribute("extension_list"))
|
||||||
|
.split(';');
|
||||||
|
|
||||||
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
|
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
|
||||||
foreach (const QString& ext, extension_list) {
|
foreach (const QString &ext, extension_list) {
|
||||||
QStringList format_and_ext = ext.split(':');
|
QStringList format_and_ext = ext.split(':');
|
||||||
|
|
||||||
supported_formats_.append(format_and_ext.at(1).split(','));
|
supported_formats_.append(format_and_ext.at(1).split(','));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) {
|
if (!supported_formats_.contains(QFileInfo(fn).suffix(),
|
||||||
return false;
|
Qt::CaseInsensitive)) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
|
bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
|
||||||
{
|
{
|
||||||
image_ = OIIO::ImageInput::open(fn.toStdString());
|
image_ = OIIO::ImageInput::open(fn.toStdString());
|
||||||
|
|
||||||
if (!image_) {
|
if (!image_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!image_->seek_subimage(subimage, 0)) {
|
if (!image_->seek_subimage(subimage, 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we can work with this pixel format
|
// Check if we can work with this pixel format
|
||||||
const OIIO::ImageSpec& spec = image_->spec();
|
const OIIO::ImageSpec &spec = image_->spec();
|
||||||
|
|
||||||
// We use RGBA frames because that tends to be the native format of GPUs
|
// We use RGBA frames because that tends to be the native format of GPUs
|
||||||
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
|
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(
|
||||||
|
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
|
||||||
|
|
||||||
if (pix_fmt_ == PixelFormat::INVALID) {
|
if (pix_fmt_ == PixelFormat::INVALID) {
|
||||||
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
|
qWarning()
|
||||||
return false;
|
<< "Failed to convert OIIO::ImageDesc to native pixel format";
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
|
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
|
||||||
|
|
||||||
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
|
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
|
||||||
qCritical() << "Failed to determine appropriate OIIO basetype from native format";
|
qCritical()
|
||||||
return false;
|
<< "Failed to determine appropriate OIIO basetype from native format";
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OIIODecoder::CloseImageHandle()
|
void OIIODecoder::CloseImageHandle()
|
||||||
{
|
{
|
||||||
if (image_) {
|
if (image_) {
|
||||||
image_->close();
|
image_->close();
|
||||||
image_ = nullptr;
|
image_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_.destroy();
|
buffer_.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoParams OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
|
VideoParams
|
||||||
|
OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
|
||||||
{
|
{
|
||||||
VideoParams video_params;
|
VideoParams video_params;
|
||||||
|
|
||||||
video_params.set_width(spec.width);
|
video_params.set_width(spec.width);
|
||||||
video_params.set_height(spec.height);
|
video_params.set_height(spec.height);
|
||||||
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
|
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(
|
||||||
video_params.set_channel_count(spec.nchannels);
|
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
|
||||||
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
|
video_params.set_channel_count(spec.nchannels);
|
||||||
video_params.set_video_type(VideoParams::kVideoTypeStill);
|
video_params.set_pixel_aspect_ratio(
|
||||||
|
OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
|
||||||
|
video_params.set_video_type(VideoParams::kVideoTypeStill);
|
||||||
|
|
||||||
return video_params;
|
return video_params;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,46 +26,50 @@
|
|||||||
|
|
||||||
#include "codec/decoder.h"
|
#include "codec/decoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class OIIODecoder : public Decoder
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class OIIODecoder : public Decoder {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
OIIODecoder();
|
OIIODecoder();
|
||||||
|
|
||||||
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
|
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
|
||||||
|
|
||||||
virtual QString id() const override;
|
virtual QString id() const override;
|
||||||
|
|
||||||
virtual bool SupportsVideo() override{return true;}
|
virtual bool SupportsVideo() override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const override;
|
virtual FootageDescription Probe(const QString &filename,
|
||||||
|
CancelAtom *cancelled) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool OpenInternal() override;
|
virtual bool OpenInternal() override;
|
||||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
|
virtual TexturePtr
|
||||||
virtual void CloseInternal() override;
|
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
|
||||||
|
virtual void CloseInternal() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<OIIO::ImageInput> image_;
|
std::unique_ptr<OIIO::ImageInput> image_;
|
||||||
|
|
||||||
static bool FileTypeIsSupported(const QString& fn);
|
static bool FileTypeIsSupported(const QString &fn);
|
||||||
|
|
||||||
bool OpenImageHandler(const QString& fn, int subimage);
|
bool OpenImageHandler(const QString &fn, int subimage);
|
||||||
|
|
||||||
void CloseImageHandle();
|
void CloseImageHandle();
|
||||||
|
|
||||||
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
|
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
|
||||||
|
|
||||||
PixelFormat pix_fmt_;
|
PixelFormat pix_fmt_;
|
||||||
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
|
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
|
||||||
|
|
||||||
Frame buffer_;
|
Frame buffer_;
|
||||||
RetrieveVideoParams last_params_;
|
RetrieveVideoParams last_params_;
|
||||||
|
|
||||||
static QStringList supported_formats_;
|
|
||||||
|
|
||||||
|
static QStringList supported_formats_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,60 +22,62 @@
|
|||||||
|
|
||||||
#include "common/oiioutils.h"
|
#include "common/oiioutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
OIIOEncoder::OIIOEncoder(const EncodingParams ¶ms) :
|
|
||||||
Encoder(params)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
OIIOEncoder::OIIOEncoder(const EncodingParams ¶ms)
|
||||||
|
: Encoder(params)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIOEncoder::Open()
|
bool OIIOEncoder::Open()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
|
bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
|
||||||
{
|
{
|
||||||
std::string filename = GetFilenameForFrame(time).toStdString();
|
std::string filename = GetFilenameForFrame(time).toStdString();
|
||||||
|
|
||||||
auto output = OIIO::ImageOutput::create(filename);
|
auto output = OIIO::ImageOutput::create(filename);
|
||||||
if (!output) {
|
if (!output) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
|
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
|
||||||
OIIO::ImageSpec spec(frame->width(), frame->height(), frame->channel_count(), type);
|
OIIO::ImageSpec spec(frame->width(), frame->height(),
|
||||||
|
frame->channel_count(), type);
|
||||||
|
|
||||||
if (!output->open(filename, spec)) {
|
if (!output->open(filename, spec)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!output->write_image(type, frame->data(), OIIO::AutoStride, frame->linesize_bytes())) {
|
if (!output->write_image(type, frame->data(), OIIO::AutoStride,
|
||||||
return false;
|
frame->linesize_bytes())) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!output->close()) {
|
if (!output->close()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
|
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
|
||||||
{
|
{
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OIIOEncoder::Close()
|
void OIIOEncoder::Close()
|
||||||
{
|
{
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,23 +23,23 @@
|
|||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "codec/encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class OIIOEncoder : public Encoder
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class OIIOEncoder : public Encoder {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
OIIOEncoder(const EncodingParams ¶ms);
|
OIIOEncoder(const EncodingParams ¶ms);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual bool Open() override;
|
virtual bool Open() override;
|
||||||
|
|
||||||
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override;
|
virtual bool WriteFrame(olive::FramePtr frame,
|
||||||
virtual bool WriteAudio(const SampleBuffer &audio) override;
|
olive::core::rational time) override;
|
||||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
virtual bool WriteAudio(const SampleBuffer &audio) override;
|
||||||
|
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||||
virtual void Close() override;
|
|
||||||
|
|
||||||
|
virtual void Close() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,100 +20,103 @@
|
|||||||
|
|
||||||
#include "planarfiledevice.h"
|
#include "planarfiledevice.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
PlanarFileDevice::PlanarFileDevice(QObject *parent) :
|
|
||||||
QObject(parent)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
PlanarFileDevice::PlanarFileDevice(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PlanarFileDevice::~PlanarFileDevice()
|
PlanarFileDevice::~PlanarFileDevice()
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMode mode)
|
bool PlanarFileDevice::open(const QVector<QString> &filenames,
|
||||||
|
QIODevice::OpenMode mode)
|
||||||
{
|
{
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
// Already open
|
// Already open
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
files_.resize(filenames.size());
|
files_.resize(filenames.size());
|
||||||
files_.fill(nullptr);
|
files_.fill(nullptr);
|
||||||
|
|
||||||
for (int i=0; i<files_.size(); i++) {
|
for (int i = 0; i < files_.size(); i++) {
|
||||||
files_[i] = new QFile(filenames.at(i));
|
files_[i] = new QFile(filenames.at(i));
|
||||||
if (!files_[i]->open(mode)) {
|
if (!files_[i]->open(mode)) {
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offset)
|
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel,
|
||||||
|
qint64 offset)
|
||||||
{
|
{
|
||||||
qint64 ret = -1;
|
qint64 ret = -1;
|
||||||
|
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
for (int i=0; i<files_.size(); i++) {
|
for (int i = 0; i < files_.size(); i++) {
|
||||||
// Kind of clunky but should be largely fine
|
// Kind of clunky but should be largely fine
|
||||||
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
|
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel, qint64 offset)
|
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel,
|
||||||
|
qint64 offset)
|
||||||
{
|
{
|
||||||
qint64 ret = -1;
|
qint64 ret = -1;
|
||||||
|
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
for (int i=0; i<files_.size(); i++) {
|
for (int i = 0; i < files_.size(); i++) {
|
||||||
// Kind of clunky but should be largely fine
|
// Kind of clunky but should be largely fine
|
||||||
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
|
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 PlanarFileDevice::size() const
|
qint64 PlanarFileDevice::size() const
|
||||||
{
|
{
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
return files_.first()->size();
|
return files_.first()->size();
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlanarFileDevice::seek(qint64 pos)
|
bool PlanarFileDevice::seek(qint64 pos)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
for (int i=0; i<files_.size(); i++) {
|
for (int i = 0; i < files_.size(); i++) {
|
||||||
ret = files_[i]->seek(pos) & ret;
|
ret = files_[i]->seek(pos) & ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlanarFileDevice::close()
|
void PlanarFileDevice::close()
|
||||||
{
|
{
|
||||||
for (int i=0; i<files_.size(); i++) {
|
for (int i = 0; i < files_.size(); i++) {
|
||||||
QFile *f = files_.at(i);
|
QFile *f = files_.at(i);
|
||||||
if (f) {
|
if (f) {
|
||||||
if (f->isOpen()) {
|
if (f->isOpen()) {
|
||||||
f->close();
|
f->close();
|
||||||
}
|
}
|
||||||
delete f;
|
delete f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files_.clear();
|
files_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,38 +25,38 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
class PlanarFileDevice : public QObject
|
class PlanarFileDevice : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
PlanarFileDevice(QObject *parent = nullptr);
|
PlanarFileDevice(QObject *parent = nullptr);
|
||||||
|
|
||||||
virtual ~PlanarFileDevice() override;
|
virtual ~PlanarFileDevice() override;
|
||||||
|
|
||||||
bool isOpen() const
|
bool isOpen() const
|
||||||
{
|
{
|
||||||
return !files_.isEmpty();
|
return !files_.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
|
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
|
||||||
|
|
||||||
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
|
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
|
||||||
|
|
||||||
qint64 write(const char **data, qint64 bytes_per_channel, qint64 offset = 0);
|
qint64 write(const char **data, qint64 bytes_per_channel,
|
||||||
|
qint64 offset = 0);
|
||||||
|
|
||||||
qint64 size() const;
|
qint64 size() const;
|
||||||
|
|
||||||
bool seek(qint64 pos);
|
bool seek(qint64 pos);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<QFile*> files_;
|
QVector<QFile *> files_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,12 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class AutoScroll {
|
class AutoScroll {
|
||||||
public:
|
public:
|
||||||
enum Method {
|
enum Method { kNone, kPage, kSmooth };
|
||||||
kNone,
|
|
||||||
kPage,
|
|
||||||
kSmooth
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,36 +24,38 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "render/cancelatom.h"
|
#include "render/cancelatom.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class CancelableObject {
|
class CancelableObject {
|
||||||
public:
|
public:
|
||||||
CancelableObject()
|
CancelableObject()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cancel()
|
void Cancel()
|
||||||
{
|
{
|
||||||
cancel_.Cancel();
|
cancel_.Cancel();
|
||||||
CancelEvent();
|
CancelEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
CancelAtom *GetCancelAtom()
|
CancelAtom *GetCancelAtom()
|
||||||
{
|
{
|
||||||
return &cancel_;
|
return &cancel_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsCancelled()
|
bool IsCancelled()
|
||||||
{
|
{
|
||||||
return cancel_.IsCancelled();
|
return cancel_.IsCancelled();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void CancelEvent(){}
|
virtual void CancelEvent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CancelAtom cancel_;
|
CancelAtom cancel_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,146 +25,154 @@
|
|||||||
|
|
||||||
CommandLineParser::~CommandLineParser()
|
CommandLineParser::~CommandLineParser()
|
||||||
{
|
{
|
||||||
foreach (const KnownOption& o, options_) {
|
foreach (const KnownOption &o, options_) {
|
||||||
delete o.option;
|
delete o.option;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const KnownPositionalArgument& a, positional_args_) {
|
foreach (const KnownPositionalArgument &a, positional_args_) {
|
||||||
delete a.option;
|
delete a.option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CommandLineParser::Option *CommandLineParser::AddOption(const QStringList &strings, const QString &description, bool takes_arg, const QString &arg_placeholder, bool hidden)
|
const CommandLineParser::Option *
|
||||||
|
CommandLineParser::AddOption(const QStringList &strings,
|
||||||
|
const QString &description, bool takes_arg,
|
||||||
|
const QString &arg_placeholder, bool hidden)
|
||||||
{
|
{
|
||||||
Option* o = new Option();
|
Option *o = new Option();
|
||||||
|
|
||||||
options_.append({strings, description, o, takes_arg, arg_placeholder, hidden});
|
options_.append(
|
||||||
|
{ strings, description, o, takes_arg, arg_placeholder, hidden });
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArgument(const QString &name, const QString &description, bool required)
|
const CommandLineParser::PositionalArgument *
|
||||||
|
CommandLineParser::AddPositionalArgument(const QString &name,
|
||||||
|
const QString &description,
|
||||||
|
bool required)
|
||||||
{
|
{
|
||||||
PositionalArgument* a = new PositionalArgument();
|
PositionalArgument *a = new PositionalArgument();
|
||||||
|
|
||||||
positional_args_.append({name, description, a, required});
|
positional_args_.append({ name, description, a, required });
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandLineParser::Process(const QVector<QString> &argv)
|
void CommandLineParser::Process(const QVector<QString> &argv)
|
||||||
{
|
{
|
||||||
int positional_index = 0;
|
int positional_index = 0;
|
||||||
|
|
||||||
for (int i=1; i<argv.size(); i++) {
|
for (int i = 1; i < argv.size(); i++) {
|
||||||
if (argv[i][0] == '-') {
|
if (argv[i][0] == '-') {
|
||||||
// Must be an option
|
// Must be an option
|
||||||
|
|
||||||
// Skip past first dashes
|
// Skip past first dashes
|
||||||
QString arg_basename = argv[i].mid(1);
|
QString arg_basename = argv[i].mid(1);
|
||||||
|
|
||||||
bool matched_known = false;
|
bool matched_known = false;
|
||||||
|
|
||||||
for (int j=0; j<options_.size(); j++) {
|
for (int j = 0; j < options_.size(); j++) {
|
||||||
KnownOption& o = options_[j];
|
KnownOption &o = options_[j];
|
||||||
|
|
||||||
foreach (const QString& s, o.args) {
|
foreach (const QString &s, o.args) {
|
||||||
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
|
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
|
||||||
// Flag discovered!
|
// Flag discovered!
|
||||||
o.option->Set();
|
o.option->Set();
|
||||||
|
|
||||||
if (o.takes_arg && i+1 < argv.size()) {
|
if (o.takes_arg && i + 1 < argv.size()) {
|
||||||
o.option->SetSetting(argv[i+1]);
|
o.option->SetSetting(argv[i + 1]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
matched_known = true;
|
matched_known = true;
|
||||||
goto found_flag;
|
goto found_flag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
found_flag:
|
found_flag:
|
||||||
if (!matched_known) {
|
if (!matched_known) {
|
||||||
qWarning() << "Unknown parameter:" << argv[i];
|
qWarning() << "Unknown parameter:" << argv[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Must be a positional flag
|
// Must be a positional flag
|
||||||
if (positional_index < positional_args_.size()) {
|
if (positional_index < positional_args_.size()) {
|
||||||
positional_args_[positional_index].option->SetSetting(argv[i]);
|
positional_args_[positional_index].option->SetSetting(argv[i]);
|
||||||
positional_index++;
|
positional_index++;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Unknown parameter:" << argv[i];
|
qWarning() << "Unknown parameter:" << argv[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandLineParser::PrintHelp(const char* filename)
|
void CommandLineParser::PrintHelp(const char *filename)
|
||||||
{
|
{
|
||||||
printf("%s %s\n",
|
printf("%s %s\n", QCoreApplication::applicationName().toUtf8().constData(),
|
||||||
QCoreApplication::applicationName().toUtf8().constData(),
|
QCoreApplication::applicationVersion().toUtf8().constData());
|
||||||
QCoreApplication::applicationVersion().toUtf8().constData());
|
|
||||||
|
|
||||||
printf("Copyright (C) 2018-2022 Olive Team\n");
|
printf("Copyright (C) 2018-2022 Olive Team\n");
|
||||||
|
|
||||||
QString positional_args;
|
QString positional_args;
|
||||||
for (int i=0; i<positional_args_.size(); i++) {
|
for (int i = 0; i < positional_args_.size(); i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
positional_args.append(' ');
|
positional_args.append(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
positional_args.append('[');
|
positional_args.append('[');
|
||||||
positional_args.append(positional_args_.at(i).name);
|
positional_args.append(positional_args_.at(i).name);
|
||||||
positional_args.append(']');
|
positional_args.append(']');
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* basename;
|
const char *basename;
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
basename = strrchr(filename, '\\');
|
basename = strrchr(filename, '\\');
|
||||||
if (!basename) {
|
if (!basename) {
|
||||||
basename = strrchr(filename, '/');
|
basename = strrchr(filename, '/');
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
basename = strrchr(filename, '/');
|
basename = strrchr(filename, '/');
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (basename) {
|
if (basename) {
|
||||||
// Slash found, increment pointer to avoid showing the slash itself
|
// Slash found, increment pointer to avoid showing the slash itself
|
||||||
basename++;
|
basename++;
|
||||||
} else {
|
} else {
|
||||||
// If no slashes are found, assume string is already a basename
|
// If no slashes are found, assume string is already a basename
|
||||||
basename = filename;
|
basename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Usage: %s [options] %s\n\n", basename, positional_args.toUtf8().constData());
|
printf("Usage: %s [options] %s\n\n", basename,
|
||||||
foreach (const KnownOption& o, options_) {
|
positional_args.toUtf8().constData());
|
||||||
if (o.hidden) {
|
foreach (const KnownOption &o, options_) {
|
||||||
continue;
|
if (o.hidden) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QString all_args;
|
QString all_args;
|
||||||
|
|
||||||
for (int i=0; i<o.args.size(); i++) {
|
for (int i = 0; i < o.args.size(); i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
all_args.append(QStringLiteral(", "));
|
all_args.append(QStringLiteral(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& this_arg = o.args.at(i);
|
const QString &this_arg = o.args.at(i);
|
||||||
|
|
||||||
all_args.append('-');
|
all_args.append('-');
|
||||||
all_args.append(this_arg);
|
all_args.append(this_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o.arg_placeholder.isEmpty()) {
|
if (o.arg_placeholder.isEmpty()) {
|
||||||
printf(" %s\n", all_args.toUtf8().constData());
|
printf(" %s\n", all_args.toUtf8().constData());
|
||||||
} else {
|
} else {
|
||||||
printf(" %s <%s>\n", all_args.toUtf8().constData(), o.arg_placeholder.toUtf8().constData());
|
printf(" %s <%s>\n", all_args.toUtf8().constData(),
|
||||||
}
|
o.arg_placeholder.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
printf(" %s\n\n", o.description.toUtf8().constData());
|
printf(" %s\n\n", o.description.toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,86 +36,86 @@
|
|||||||
* the user as a command line argument. Therefore we needed a custom implementation that could
|
* the user as a command line argument. Therefore we needed a custom implementation that could
|
||||||
* parse arguments without the need for a Q(Core)Application to be present already.
|
* parse arguments without the need for a Q(Core)Application to be present already.
|
||||||
*/
|
*/
|
||||||
class CommandLineParser
|
class CommandLineParser {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
~CommandLineParser();
|
~CommandLineParser();
|
||||||
|
|
||||||
DISABLE_COPY_MOVE(CommandLineParser)
|
DISABLE_COPY_MOVE(CommandLineParser)
|
||||||
|
|
||||||
class PositionalArgument
|
class PositionalArgument {
|
||||||
{
|
public:
|
||||||
public:
|
PositionalArgument() = default;
|
||||||
PositionalArgument() = default;
|
|
||||||
|
|
||||||
const QString& GetSetting() const
|
const QString &GetSetting() const
|
||||||
{
|
{
|
||||||
return setting_;
|
return setting_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSetting(const QString& s)
|
void SetSetting(const QString &s)
|
||||||
{
|
{
|
||||||
setting_ = s;
|
setting_ = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString setting_;
|
QString setting_;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
class Option : public PositionalArgument {
|
||||||
|
public:
|
||||||
|
Option()
|
||||||
|
{
|
||||||
|
is_set_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
class Option : public PositionalArgument {
|
bool IsSet() const
|
||||||
public:
|
{
|
||||||
Option()
|
return is_set_;
|
||||||
{
|
}
|
||||||
is_set_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSet() const
|
void Set()
|
||||||
{
|
{
|
||||||
return is_set_;
|
is_set_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Set()
|
private:
|
||||||
{
|
bool is_set_;
|
||||||
is_set_ = true;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
CommandLineParser() = default;
|
||||||
bool is_set_;
|
|
||||||
|
|
||||||
};
|
const Option *AddOption(const QStringList &strings,
|
||||||
|
const QString &description, bool takes_arg = false,
|
||||||
|
const QString &arg_placeholder = QString(),
|
||||||
|
bool hidden = false);
|
||||||
|
|
||||||
CommandLineParser() = default;
|
const PositionalArgument *AddPositionalArgument(const QString &name,
|
||||||
|
const QString &description,
|
||||||
|
bool required = false);
|
||||||
|
|
||||||
const Option* AddOption(const QStringList& strings, const QString& description, bool takes_arg = false, const QString& arg_placeholder = QString(), bool hidden = false);
|
void Process(const QVector<QString> &argv);
|
||||||
|
|
||||||
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false);
|
void PrintHelp(const char *filename);
|
||||||
|
|
||||||
void Process(const QVector<QString> &argv);
|
|
||||||
|
|
||||||
void PrintHelp(const char* filename);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct KnownOption {
|
struct KnownOption {
|
||||||
QStringList args;
|
QStringList args;
|
||||||
QString description;
|
QString description;
|
||||||
Option* option;
|
Option *option;
|
||||||
bool takes_arg;
|
bool takes_arg;
|
||||||
QString arg_placeholder;
|
QString arg_placeholder;
|
||||||
bool hidden;
|
bool hidden;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KnownPositionalArgument {
|
struct KnownPositionalArgument {
|
||||||
QString name;
|
QString name;
|
||||||
QString description;
|
QString description;
|
||||||
PositionalArgument* option;
|
PositionalArgument *option;
|
||||||
bool required;
|
bool required;
|
||||||
};
|
};
|
||||||
|
|
||||||
QVector<KnownOption> options_;
|
QVector<KnownOption> options_;
|
||||||
|
|
||||||
QVector<KnownPositionalArgument> positional_args_;
|
|
||||||
|
|
||||||
|
QVector<KnownPositionalArgument> positional_args_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COMMANDLINEPARSER_H
|
#endif // COMMANDLINEPARSER_H
|
||||||
|
|||||||
@@ -40,52 +40,63 @@ crashpad::CrashpadClient *client;
|
|||||||
|
|
||||||
bool InitializeCrashpad()
|
bool InitializeCrashpad()
|
||||||
{
|
{
|
||||||
QString report_path = QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("reports"));
|
QString report_path = QDir(olive::FileFunctions::GetTempFilePath())
|
||||||
|
.filePath(QStringLiteral("reports"));
|
||||||
|
|
||||||
QString handler_fn = olive::FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("crashpad_handler"));
|
QString handler_fn =
|
||||||
|
olive::FileFunctions::GetFormattedExecutableForPlatform(
|
||||||
|
QStringLiteral("crashpad_handler"));
|
||||||
|
|
||||||
// Generate absolute path
|
// Generate absolute path
|
||||||
QString handler_abs_path = QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
|
QString handler_abs_path =
|
||||||
|
QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
|
||||||
|
|
||||||
bool status = false;
|
bool status = false;
|
||||||
|
|
||||||
if (QFileInfo::exists(handler_abs_path)) {
|
if (QFileInfo::exists(handler_abs_path)) {
|
||||||
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
|
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
|
||||||
|
|
||||||
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
|
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
|
||||||
|
|
||||||
base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics"))));
|
base::FilePath metrics_dir(
|
||||||
|
QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath())
|
||||||
|
.filePath(QStringLiteral("metrics"))));
|
||||||
|
|
||||||
// Metadata that will be posted to the server with the crash report map
|
// Metadata that will be posted to the server with the crash report map
|
||||||
std::map<std::string, std::string> annotations;
|
std::map<std::string, std::string> annotations;
|
||||||
|
|
||||||
// Disable crashpad rate limiting so that all crashes have dmp files
|
// Disable crashpad rate limiting so that all crashes have dmp files
|
||||||
std::vector<std::string> arguments;
|
std::vector<std::string> arguments;
|
||||||
arguments.push_back("--no-rate-limit");
|
arguments.push_back("--no-rate-limit");
|
||||||
arguments.push_back("--no-upload-gzip");
|
arguments.push_back("--no-upload-gzip");
|
||||||
|
|
||||||
// Initialize Crashpad database
|
// Initialize Crashpad database
|
||||||
std::unique_ptr<crashpad::CrashReportDatabase> database = crashpad::CrashReportDatabase::Initialize(reports_dir);
|
std::unique_ptr<crashpad::CrashReportDatabase> database =
|
||||||
if (database == NULL) return false;
|
crashpad::CrashReportDatabase::Initialize(reports_dir);
|
||||||
|
if (database == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Disable automated crash uploads
|
// Disable automated crash uploads
|
||||||
crashpad::Settings *settings = database->GetSettings();
|
crashpad::Settings *settings = database->GetSettings();
|
||||||
if (settings == NULL) return false;
|
if (settings == NULL)
|
||||||
settings->SetUploadsEnabled(false);
|
return false;
|
||||||
|
settings->SetUploadsEnabled(false);
|
||||||
|
|
||||||
// Start crash handler
|
// Start crash handler
|
||||||
client = new crashpad::CrashpadClient();
|
client = new crashpad::CrashpadClient();
|
||||||
status = client->StartHandler(handler, reports_dir, metrics_dir,
|
status = client->StartHandler(
|
||||||
"https://olivevideoeditor.org/crashpad/report.php",
|
handler, reports_dir, metrics_dir,
|
||||||
annotations, arguments, true, true);
|
"https://olivevideoeditor.org/crashpad/report.php", annotations,
|
||||||
}
|
arguments, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
// Override Crashpad exception filter with our own
|
// Override Crashpad exception filter with our own
|
||||||
if (!status) {
|
if (!status) {
|
||||||
qWarning() << "Failed to start Crashpad, automatic crash reporting will be disabled";
|
qWarning()
|
||||||
}
|
<< "Failed to start Crashpad, automatic crash reporting will be disabled";
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_CRASHPAD
|
#endif // USE_CRASHPAD
|
||||||
|
|||||||
@@ -35,6 +35,6 @@
|
|||||||
// arrays encoded in UTF-16.
|
// arrays encoded in UTF-16.
|
||||||
#define QSTRING_TO_BASE_STRING(x) x.toStdWString()
|
#define QSTRING_TO_BASE_STRING(x) x.toStdWString()
|
||||||
#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x)
|
#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x)
|
||||||
#endif // BUILDFLAG(IS_WIN)
|
#endif // BUILDFLAG(IS_WIN)
|
||||||
|
|
||||||
#endif // CRASHPADUTILS_H
|
#endif // CRASHPADUTILS_H
|
||||||
|
|||||||
+29
-27
@@ -20,38 +20,40 @@
|
|||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
|
||||||
{
|
{
|
||||||
QByteArray localMsg = msg.toLocal8Bit();
|
|
||||||
|
|
||||||
const char* msg_type = "UNKNOWN";
|
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
switch (type) {
|
const QString &msg)
|
||||||
case QtDebugMsg:
|
{
|
||||||
msg_type = "DEBUG";
|
QByteArray localMsg = msg.toLocal8Bit();
|
||||||
break;
|
|
||||||
case QtInfoMsg:
|
|
||||||
msg_type = "INFO";
|
|
||||||
break;
|
|
||||||
case QtWarningMsg:
|
|
||||||
msg_type = "WARNING";
|
|
||||||
break;
|
|
||||||
case QtCriticalMsg:
|
|
||||||
msg_type = "ERROR";
|
|
||||||
break;
|
|
||||||
case QtFatalMsg:
|
|
||||||
msg_type = "FATAL";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
|
const char *msg_type = "UNKNOWN";
|
||||||
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
|
switch (type) {
|
||||||
|
case QtDebugMsg:
|
||||||
|
msg_type = "DEBUG";
|
||||||
|
break;
|
||||||
|
case QtInfoMsg:
|
||||||
|
msg_type = "INFO";
|
||||||
|
break;
|
||||||
|
case QtWarningMsg:
|
||||||
|
msg_type = "WARNING";
|
||||||
|
break;
|
||||||
|
case QtCriticalMsg:
|
||||||
|
msg_type = "ERROR";
|
||||||
|
break;
|
||||||
|
case QtFatalMsg:
|
||||||
|
msg_type = "FATAL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
|
||||||
|
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
|
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
|
||||||
// is flushed
|
// is flushed
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -25,9 +25,11 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
|
const QString &msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
-55
@@ -26,77 +26,76 @@
|
|||||||
|
|
||||||
//#define ALLOW_RETURNING_INFINITY
|
//#define ALLOW_RETURNING_INFINITY
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class Decibel
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Decibel {
|
||||||
public:
|
public:
|
||||||
// In basically all circumstances, this should calculate to 0.0 linear
|
// In basically all circumstances, this should calculate to 0.0 linear
|
||||||
static constexpr double MINIMUM = -200.0;
|
static constexpr double MINIMUM = -200.0;
|
||||||
|
|
||||||
static double fromLinear(double linear)
|
static double fromLinear(double linear)
|
||||||
{
|
{
|
||||||
double v = double(20.0) * std::log10(linear);
|
double v = double(20.0) * std::log10(linear);
|
||||||
#ifndef ALLOW_RETURNING_INFINITY
|
#ifndef ALLOW_RETURNING_INFINITY
|
||||||
if (std::isinf(v)) {
|
if (std::isinf(v)) {
|
||||||
return MINIMUM;
|
return MINIMUM;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double toLinear(double decibel)
|
static double toLinear(double decibel)
|
||||||
{
|
{
|
||||||
double to_linear = std::pow(double(10.0), decibel / double(20.0));
|
double to_linear = std::pow(double(10.0), decibel / double(20.0));
|
||||||
|
|
||||||
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
|
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
|
||||||
if (to_linear < 0.000001) {
|
if (to_linear < 0.000001) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return to_linear;
|
return to_linear;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static double fromLogarithmic(double logarithmic)
|
static double fromLogarithmic(double logarithmic)
|
||||||
{
|
{
|
||||||
if (logarithmic < 0.001)
|
if (logarithmic < 0.001)
|
||||||
#ifdef ALLOW_RETURNING_INFINITY
|
#ifdef ALLOW_RETURNING_INFINITY
|
||||||
return std::numeric_limits<double>::infinity();
|
return std::numeric_limits<double>::infinity();
|
||||||
#else
|
#else
|
||||||
return MINIMUM;
|
return MINIMUM;
|
||||||
#endif
|
#endif
|
||||||
else if (logarithmic > 0.99)
|
else if (logarithmic > 0.99)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
|
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
|
||||||
}
|
}
|
||||||
|
|
||||||
static double toLogarithmic(double decibel)
|
static double toLogarithmic(double decibel)
|
||||||
{
|
{
|
||||||
if (qFuzzyIsNull(decibel)) {
|
if (qFuzzyIsNull(decibel)) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
|
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static double LinearToLogarithmic(double linear)
|
static double LinearToLogarithmic(double linear)
|
||||||
{
|
{
|
||||||
return 1 - std::exp(-linear * LOG100);
|
return 1 - std::exp(-linear * LOG100);
|
||||||
}
|
}
|
||||||
|
|
||||||
static double LogarithmicToLinear(double logarithmic)
|
static double LogarithmicToLinear(double logarithmic)
|
||||||
{
|
{
|
||||||
if (logarithmic > 0.99) {
|
if (logarithmic > 0.99) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return -std::log(1 - logarithmic) / LOG100;
|
return -std::log(1 - logarithmic) / LOG100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr double LOG100 = 4.60517018599;
|
static constexpr double LOG100 = 4.60517018599;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-12
@@ -21,7 +21,8 @@
|
|||||||
#ifndef OLIVECOMMONDEFINE_H
|
#ifndef OLIVECOMMONDEFINE_H
|
||||||
#define OLIVECOMMONDEFINE_H
|
#define OLIVECOMMONDEFINE_H
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
/// The minimum size an icon in ProjectExplorer can be
|
/// The minimum size an icon in ProjectExplorer can be
|
||||||
const int kProjectIconSizeMinimum = 16;
|
const int kProjectIconSizeMinimum = 16;
|
||||||
@@ -39,25 +40,28 @@ const int kBytesInGigabyte = 1073741824;
|
|||||||
#define MACRO_NAME_AS_STR(s) #s
|
#define MACRO_NAME_AS_STR(s) #s
|
||||||
#define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s)
|
#define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s)
|
||||||
|
|
||||||
#define OLIVE_NS_CONST_ARG(x, y) QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
|
#define OLIVE_NS_CONST_ARG(x, y) \
|
||||||
#define OLIVE_NS_ARG(x, y) QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||||
#define OLIVE_NS_RETURN_ARG(x, y) QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
#define OLIVE_NS_ARG(x, y) \
|
||||||
|
QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||||
|
#define OLIVE_NS_RETURN_ARG(x, y) \
|
||||||
|
QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
|
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
|
||||||
* use our own functions for portability.
|
* use our own functions for portability.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DISABLE_COPY(Class) \
|
#define DISABLE_COPY(Class) \
|
||||||
Class(const Class &) = delete;\
|
Class(const Class &) = delete; \
|
||||||
Class &operator=(const Class &) = delete;
|
Class &operator=(const Class &) = delete;
|
||||||
|
|
||||||
#define DISABLE_MOVE(Class) \
|
#define DISABLE_MOVE(Class) \
|
||||||
Class(Class &&) = delete; \
|
Class(Class &&) = delete; \
|
||||||
Class &operator=(Class &&) = delete;
|
Class &operator=(Class &&) = delete;
|
||||||
|
|
||||||
#define DISABLE_COPY_MOVE(Class) \
|
#define DISABLE_COPY_MOVE(Class) \
|
||||||
DISABLE_COPY(Class) \
|
DISABLE_COPY(Class) \
|
||||||
DISABLE_MOVE(Class)
|
DISABLE_MOVE(Class)
|
||||||
|
|
||||||
#endif // OLIVECOMMONDEFINE_H
|
#endif // OLIVECOMMONDEFINE_H
|
||||||
|
|||||||
+10
-9
@@ -23,21 +23,22 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
inline int64_t GetDigitCount(int64_t input)
|
inline int64_t GetDigitCount(int64_t input)
|
||||||
{
|
{
|
||||||
input = std::abs(input);
|
input = std::abs(input);
|
||||||
|
|
||||||
int64_t lim = 10;
|
int64_t lim = 10;
|
||||||
int64_t digit = 1;
|
int64_t digit = 1;
|
||||||
|
|
||||||
while (input >= lim) {
|
while (input >= lim) {
|
||||||
lim *= 10;
|
lim *= 10;
|
||||||
digit++;
|
digit++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return digit;
|
return digit;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+148
-141
@@ -20,180 +20,187 @@
|
|||||||
|
|
||||||
#include "common/ffmpegutils.h"
|
#include "common/ffmpegutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum)
|
|
||||||
{
|
{
|
||||||
AVPixelFormat possible_pix_fmts[3];
|
|
||||||
|
|
||||||
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
|
AVPixelFormat
|
||||||
|
FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
|
||||||
|
PixelFormat maximum)
|
||||||
|
{
|
||||||
|
AVPixelFormat possible_pix_fmts[3];
|
||||||
|
|
||||||
if (maximum == PixelFormat::U8) {
|
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
|
||||||
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
|
|
||||||
} else {
|
|
||||||
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
|
|
||||||
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
|
if (maximum == PixelFormat::U8) {
|
||||||
pix_fmt,
|
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
|
||||||
1,
|
} else {
|
||||||
nullptr);
|
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
|
||||||
|
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt, 1,
|
||||||
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
|
SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
|
||||||
{
|
{
|
||||||
switch (smp_fmt) {
|
switch (smp_fmt) {
|
||||||
case AV_SAMPLE_FMT_U8:
|
case AV_SAMPLE_FMT_U8:
|
||||||
return SampleFormat::U8;
|
return SampleFormat::U8;
|
||||||
case AV_SAMPLE_FMT_S16:
|
case AV_SAMPLE_FMT_S16:
|
||||||
return SampleFormat::S16;
|
return SampleFormat::S16;
|
||||||
case AV_SAMPLE_FMT_S32:
|
case AV_SAMPLE_FMT_S32:
|
||||||
return SampleFormat::S32;
|
return SampleFormat::S32;
|
||||||
case AV_SAMPLE_FMT_S64:
|
case AV_SAMPLE_FMT_S64:
|
||||||
return SampleFormat::S64;
|
return SampleFormat::S64;
|
||||||
case AV_SAMPLE_FMT_FLT:
|
case AV_SAMPLE_FMT_FLT:
|
||||||
return SampleFormat::F32;
|
return SampleFormat::F32;
|
||||||
case AV_SAMPLE_FMT_DBL:
|
case AV_SAMPLE_FMT_DBL:
|
||||||
return SampleFormat::F64;
|
return SampleFormat::F64;
|
||||||
case AV_SAMPLE_FMT_U8P :
|
case AV_SAMPLE_FMT_U8P:
|
||||||
return SampleFormat::U8P;
|
return SampleFormat::U8P;
|
||||||
case AV_SAMPLE_FMT_S16P:
|
case AV_SAMPLE_FMT_S16P:
|
||||||
return SampleFormat::S16P;
|
return SampleFormat::S16P;
|
||||||
case AV_SAMPLE_FMT_S32P:
|
case AV_SAMPLE_FMT_S32P:
|
||||||
return SampleFormat::S32P;
|
return SampleFormat::S32P;
|
||||||
case AV_SAMPLE_FMT_S64P:
|
case AV_SAMPLE_FMT_S64P:
|
||||||
return SampleFormat::S64P;
|
return SampleFormat::S64P;
|
||||||
case AV_SAMPLE_FMT_FLTP:
|
case AV_SAMPLE_FMT_FLTP:
|
||||||
return SampleFormat::F32P;
|
return SampleFormat::F32P;
|
||||||
case AV_SAMPLE_FMT_DBLP:
|
case AV_SAMPLE_FMT_DBLP:
|
||||||
return SampleFormat::F64P;
|
return SampleFormat::F64P;
|
||||||
case AV_SAMPLE_FMT_NONE:
|
case AV_SAMPLE_FMT_NONE:
|
||||||
case AV_SAMPLE_FMT_NB:
|
case AV_SAMPLE_FMT_NB:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SampleFormat::INVALID;
|
return SampleFormat::INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const SampleFormat &smp_fmt)
|
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const SampleFormat &smp_fmt)
|
||||||
{
|
{
|
||||||
switch (smp_fmt) {
|
switch (smp_fmt) {
|
||||||
case SampleFormat::U8:
|
case SampleFormat::U8:
|
||||||
return AV_SAMPLE_FMT_U8;
|
return AV_SAMPLE_FMT_U8;
|
||||||
case SampleFormat::S16:
|
case SampleFormat::S16:
|
||||||
return AV_SAMPLE_FMT_S16;
|
return AV_SAMPLE_FMT_S16;
|
||||||
case SampleFormat::S32:
|
case SampleFormat::S32:
|
||||||
return AV_SAMPLE_FMT_S32;
|
return AV_SAMPLE_FMT_S32;
|
||||||
case SampleFormat::S64:
|
case SampleFormat::S64:
|
||||||
return AV_SAMPLE_FMT_S64;
|
return AV_SAMPLE_FMT_S64;
|
||||||
case SampleFormat::F32:
|
case SampleFormat::F32:
|
||||||
return AV_SAMPLE_FMT_FLT;
|
return AV_SAMPLE_FMT_FLT;
|
||||||
case SampleFormat::F64:
|
case SampleFormat::F64:
|
||||||
return AV_SAMPLE_FMT_DBL;
|
return AV_SAMPLE_FMT_DBL;
|
||||||
case SampleFormat::U8P:
|
case SampleFormat::U8P:
|
||||||
return AV_SAMPLE_FMT_U8P;
|
return AV_SAMPLE_FMT_U8P;
|
||||||
case SampleFormat::S16P:
|
case SampleFormat::S16P:
|
||||||
return AV_SAMPLE_FMT_S16P;
|
return AV_SAMPLE_FMT_S16P;
|
||||||
case SampleFormat::S32P:
|
case SampleFormat::S32P:
|
||||||
return AV_SAMPLE_FMT_S32P;
|
return AV_SAMPLE_FMT_S32P;
|
||||||
case SampleFormat::S64P:
|
case SampleFormat::S64P:
|
||||||
return AV_SAMPLE_FMT_S64P;
|
return AV_SAMPLE_FMT_S64P;
|
||||||
case SampleFormat::F32P:
|
case SampleFormat::F32P:
|
||||||
return AV_SAMPLE_FMT_FLTP;
|
return AV_SAMPLE_FMT_FLTP;
|
||||||
case SampleFormat::F64P:
|
case SampleFormat::F64P:
|
||||||
return AV_SAMPLE_FMT_DBLP;
|
return AV_SAMPLE_FMT_DBLP;
|
||||||
case SampleFormat::INVALID:
|
case SampleFormat::INVALID:
|
||||||
case SampleFormat::COUNT:
|
case SampleFormat::COUNT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AV_SAMPLE_FMT_NONE;
|
return AV_SAMPLE_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
|
int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
|
||||||
{
|
{
|
||||||
switch (cs) {
|
switch (cs) {
|
||||||
case AVCOL_SPC_BT709:
|
case AVCOL_SPC_BT709:
|
||||||
return SWS_CS_ITU709;
|
return SWS_CS_ITU709;
|
||||||
case AVCOL_SPC_FCC:
|
case AVCOL_SPC_FCC:
|
||||||
return SWS_CS_FCC;
|
return SWS_CS_FCC;
|
||||||
case AVCOL_SPC_BT470BG:
|
case AVCOL_SPC_BT470BG:
|
||||||
return SWS_CS_ITU624;
|
return SWS_CS_ITU624;
|
||||||
case AVCOL_SPC_SMPTE170M:
|
case AVCOL_SPC_SMPTE170M:
|
||||||
return SWS_CS_SMPTE170M;
|
return SWS_CS_SMPTE170M;
|
||||||
case AVCOL_SPC_SMPTE240M:
|
case AVCOL_SPC_SMPTE240M:
|
||||||
return SWS_CS_SMPTE240M;
|
return SWS_CS_SMPTE240M;
|
||||||
case AVCOL_SPC_BT2020_NCL:
|
case AVCOL_SPC_BT2020_NCL:
|
||||||
return SWS_CS_BT2020;
|
return SWS_CS_BT2020;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SWS_CS_DEFAULT;
|
return SWS_CS_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
|
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
|
||||||
{
|
{
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P;
|
case AV_PIX_FMT_YUVJ420P:
|
||||||
case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P;
|
return AV_PIX_FMT_YUV420P;
|
||||||
case AV_PIX_FMT_YUVJ444P: return AV_PIX_FMT_YUV444P;
|
case AV_PIX_FMT_YUVJ422P:
|
||||||
case AV_PIX_FMT_YUVJ440P: return AV_PIX_FMT_YUV440P;
|
return AV_PIX_FMT_YUV422P;
|
||||||
case AV_PIX_FMT_YUVJ411P: return AV_PIX_FMT_YUV411P;
|
case AV_PIX_FMT_YUVJ444P:
|
||||||
default:
|
return AV_PIX_FMT_YUV444P;
|
||||||
break;
|
case AV_PIX_FMT_YUVJ440P:
|
||||||
}
|
return AV_PIX_FMT_YUV440P;
|
||||||
|
case AV_PIX_FMT_YUVJ411P:
|
||||||
|
return AV_PIX_FMT_YUV411P;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt, int channel_layout)
|
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
|
||||||
|
int channel_layout)
|
||||||
{
|
{
|
||||||
if (channel_layout == VideoParams::kRGBChannelCount) {
|
if (channel_layout == VideoParams::kRGBChannelCount) {
|
||||||
switch (pix_fmt) {
|
switch (pix_fmt) {
|
||||||
case PixelFormat::U8:
|
case PixelFormat::U8:
|
||||||
return AV_PIX_FMT_RGB24;
|
return AV_PIX_FMT_RGB24;
|
||||||
case PixelFormat::U16:
|
case PixelFormat::U16:
|
||||||
return AV_PIX_FMT_RGB48;
|
return AV_PIX_FMT_RGB48;
|
||||||
case PixelFormat::F16:
|
case PixelFormat::F16:
|
||||||
case PixelFormat::F32:
|
case PixelFormat::F32:
|
||||||
case PixelFormat::INVALID:
|
case PixelFormat::INVALID:
|
||||||
case PixelFormat::COUNT:
|
case PixelFormat::COUNT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
|
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
|
||||||
switch (pix_fmt) {
|
switch (pix_fmt) {
|
||||||
case PixelFormat::U8:
|
case PixelFormat::U8:
|
||||||
return AV_PIX_FMT_RGBA;
|
return AV_PIX_FMT_RGBA;
|
||||||
case PixelFormat::U16:
|
case PixelFormat::U16:
|
||||||
return AV_PIX_FMT_RGBA64;
|
return AV_PIX_FMT_RGBA64;
|
||||||
case PixelFormat::F16:
|
case PixelFormat::F16:
|
||||||
case PixelFormat::F32:
|
case PixelFormat::F32:
|
||||||
case PixelFormat::INVALID:
|
case PixelFormat::INVALID:
|
||||||
case PixelFormat::COUNT:
|
case PixelFormat::COUNT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AV_PIX_FMT_NONE;
|
return AV_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelFormat FFmpegUtils::GetCompatiblePixelFormat(const PixelFormat &pix_fmt)
|
PixelFormat FFmpegUtils::GetCompatiblePixelFormat(const PixelFormat &pix_fmt)
|
||||||
{
|
{
|
||||||
switch (pix_fmt) {
|
switch (pix_fmt) {
|
||||||
case PixelFormat::U8:
|
case PixelFormat::U8:
|
||||||
return PixelFormat::U8;
|
return PixelFormat::U8;
|
||||||
case PixelFormat::U16:
|
case PixelFormat::U16:
|
||||||
case PixelFormat::F16:
|
case PixelFormat::F16:
|
||||||
case PixelFormat::F32:
|
case PixelFormat::F32:
|
||||||
return PixelFormat::U16;
|
return PixelFormat::U16;
|
||||||
case PixelFormat::INVALID:
|
case PixelFormat::INVALID:
|
||||||
case PixelFormat::COUNT:
|
case PixelFormat::COUNT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PixelFormat::INVALID;
|
return PixelFormat::INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-17
@@ -31,63 +31,67 @@ extern "C" {
|
|||||||
|
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
class FFmpegUtils {
|
class FFmpegUtils {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
|
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
|
||||||
*/
|
*/
|
||||||
static AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt, PixelFormat maximum = PixelFormat::INVALID);
|
static AVPixelFormat
|
||||||
|
GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
|
||||||
|
PixelFormat maximum = PixelFormat::INVALID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
|
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
|
||||||
*/
|
*/
|
||||||
static PixelFormat GetCompatiblePixelFormat(const PixelFormat& pix_fmt);
|
static PixelFormat GetCompatiblePixelFormat(const PixelFormat &pix_fmt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns an FFmpeg pixel format for a given native pixel format
|
* @brief Returns an FFmpeg pixel format for a given native pixel format
|
||||||
*/
|
*/
|
||||||
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat& pix_fmt, int channel_layout);
|
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
|
||||||
|
int channel_layout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a native sample format type for a given AVSampleFormat
|
* @brief Returns a native sample format type for a given AVSampleFormat
|
||||||
*/
|
*/
|
||||||
static SampleFormat GetNativeSampleFormat(const AVSampleFormat& smp_fmt);
|
static SampleFormat GetNativeSampleFormat(const AVSampleFormat &smp_fmt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns an FFmpeg sample format type for a given native type
|
* @brief Returns an FFmpeg sample format type for a given native type
|
||||||
*/
|
*/
|
||||||
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
|
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns an SWS_CS_* macro from an AVColorSpace enum member
|
* @brief Returns an SWS_CS_* macro from an AVColorSpace enum member
|
||||||
*
|
*
|
||||||
* Why aren't these the same thing anyway? And for that matter, why doesn't FFmpeg provide a
|
* Why aren't these the same thing anyway? And for that matter, why doesn't FFmpeg provide a
|
||||||
* convenience function to do this conversion for us? Who knows, but here we are.
|
* convenience function to do this conversion for us? Who knows, but here we are.
|
||||||
*/
|
*/
|
||||||
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart
|
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart
|
||||||
*
|
*
|
||||||
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
|
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
|
||||||
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
|
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
|
||||||
* aware), we use this function.
|
* aware), we use this function.
|
||||||
*/
|
*/
|
||||||
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
|
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
|
||||||
};
|
};
|
||||||
|
|
||||||
using AVFramePtr = std::shared_ptr<AVFrame>;
|
using AVFramePtr = std::shared_ptr<AVFrame>;
|
||||||
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
|
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
|
||||||
{
|
{
|
||||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); });
|
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
|
||||||
}
|
}
|
||||||
inline AVFramePtr CreateAVFramePtr()
|
inline AVFramePtr CreateAVFramePtr()
|
||||||
{
|
{
|
||||||
return CreateAVFramePtr(av_frame_alloc());
|
return CreateAVFramePtr(av_frame_alloc());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+142
-125
@@ -29,206 +29,223 @@
|
|||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
|
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
|
||||||
{
|
{
|
||||||
QFileInfo info(filename);
|
QFileInfo info(filename);
|
||||||
|
|
||||||
if (!info.exists()) {
|
if (!info.exists()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||||
|
|
||||||
hash.addData(info.absoluteFilePath().toUtf8());
|
hash.addData(info.absoluteFilePath().toUtf8());
|
||||||
|
|
||||||
hash.addData(QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
|
hash.addData(
|
||||||
|
QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
|
||||||
|
|
||||||
QByteArray result = hash.result();
|
QByteArray result = hash.result();
|
||||||
|
|
||||||
return QString(result.toHex());
|
return QString(result.toHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::GetConfigurationLocation()
|
QString FileFunctions::GetConfigurationLocation()
|
||||||
{
|
{
|
||||||
if (IsPortable()) {
|
if (IsPortable()) {
|
||||||
return GetApplicationPath();
|
return GetApplicationPath();
|
||||||
} else {
|
} else {
|
||||||
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
QString s =
|
||||||
QDir(s).mkpath(".");
|
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
return s;
|
QDir(s).mkpath(".");
|
||||||
}
|
return s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileFunctions::IsPortable()
|
bool FileFunctions::IsPortable()
|
||||||
{
|
{
|
||||||
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
|
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::GetApplicationPath()
|
QString FileFunctions::GetApplicationPath()
|
||||||
{
|
{
|
||||||
return QCoreApplication::applicationDirPath();
|
return QCoreApplication::applicationDirPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::GetTempFilePath()
|
QString FileFunctions::GetTempFilePath()
|
||||||
{
|
{
|
||||||
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
QString temp_path =
|
||||||
.filePath(QCoreApplication::organizationName()))
|
QDir(
|
||||||
.filePath(QCoreApplication::applicationName());
|
QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
||||||
|
.filePath(QCoreApplication::organizationName()))
|
||||||
|
.filePath(QCoreApplication::applicationName());
|
||||||
|
|
||||||
// Ensure it exists
|
// Ensure it exists
|
||||||
QDir(temp_path).mkpath(".");
|
QDir(temp_path).mkpath(".");
|
||||||
|
|
||||||
return temp_path;
|
return temp_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest)
|
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString &source,
|
||||||
|
const QString &dest)
|
||||||
{
|
{
|
||||||
QFileInfoList info_list = QDir(source).entryInfoList();
|
QFileInfoList info_list = QDir(source).entryInfoList();
|
||||||
|
|
||||||
foreach (const QFileInfo& info, info_list) {
|
foreach (const QFileInfo &info, info_list) {
|
||||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||||
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
|
if (info.fileName() == QStringLiteral(".") ||
|
||||||
continue;
|
info.fileName() == QStringLiteral("..")) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QString dest_equivalent = QDir(dest).filePath(info.fileName());
|
QString dest_equivalent = QDir(dest).filePath(info.fileName());
|
||||||
|
|
||||||
if (info.isDir()) {
|
if (info.isDir()) {
|
||||||
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(), dest_equivalent)) {
|
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(),
|
||||||
return false;
|
dest_equivalent)) {
|
||||||
}
|
return false;
|
||||||
} else if (QFileInfo::exists(dest_equivalent)) {
|
}
|
||||||
return false;
|
} else if (QFileInfo::exists(dest_equivalent)) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bool overwrite)
|
void FileFunctions::CopyDirectory(const QString &source, const QString &dest,
|
||||||
|
bool overwrite)
|
||||||
{
|
{
|
||||||
QDir d(source);
|
QDir d(source);
|
||||||
|
|
||||||
if (!d.exists()) {
|
if (!d.exists()) {
|
||||||
qCritical() << "Failed to copy directory, source" << source << "didn't exist";
|
qCritical()
|
||||||
return;
|
<< "Failed to copy directory, source" << source << "didn't exist";
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QDir dest_dir(dest);
|
QDir dest_dir(dest);
|
||||||
|
|
||||||
if (!dest_dir.mkpath(QStringLiteral("."))) {
|
if (!dest_dir.mkpath(QStringLiteral("."))) {
|
||||||
qCritical() << "Failed to create destination directory" << dest;
|
qCritical() << "Failed to create destination directory" << dest;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFileInfoList l = d.entryInfoList();
|
QFileInfoList l = d.entryInfoList();
|
||||||
|
|
||||||
foreach (const QFileInfo& info, l) {
|
foreach (const QFileInfo &info, l) {
|
||||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||||
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
|
if (info.fileName() == QStringLiteral(".") ||
|
||||||
continue;
|
info.fileName() == QStringLiteral("..")) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
QString dest_file_path = dest_dir.filePath(info.fileName());
|
QString dest_file_path = dest_dir.filePath(info.fileName());
|
||||||
|
|
||||||
if (info.isDir()) {
|
if (info.isDir()) {
|
||||||
// Copy dir
|
// Copy dir
|
||||||
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
|
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
|
||||||
} else {
|
} else {
|
||||||
// Copy file
|
// Copy file
|
||||||
if (overwrite && QFile::exists(dest_file_path)) {
|
if (overwrite && QFile::exists(dest_file_path)) {
|
||||||
QFile file(dest_file_path);
|
QFile file(dest_file_path);
|
||||||
file.setPermissions(file.permissions() | QFileDevice::WriteOwner | QFileDevice::WriteUser |
|
file.setPermissions(
|
||||||
QFileDevice::WriteGroup | QFileDevice::WriteOther);
|
file.permissions() | QFileDevice::WriteOwner |
|
||||||
file.remove();
|
QFileDevice::WriteUser | QFileDevice::WriteGroup |
|
||||||
}
|
QFileDevice::WriteOther);
|
||||||
|
file.remove();
|
||||||
|
}
|
||||||
|
|
||||||
QFile::copy(info.absoluteFilePath(), dest_file_path);
|
QFile::copy(info.absoluteFilePath(), dest_file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileFunctions::DirectoryIsValid(const QDir &d, bool try_to_create_if_not_exists)
|
bool FileFunctions::DirectoryIsValid(const QDir &d,
|
||||||
|
bool try_to_create_if_not_exists)
|
||||||
{
|
{
|
||||||
// Return whether the directory exists, or whether it could be created if it doesn't
|
// Return whether the directory exists, or whether it could be created if it doesn't
|
||||||
return d.exists() || d.mkpath(QStringLiteral("."));
|
return d.exists() || d.mkpath(QStringLiteral("."));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension)
|
QString FileFunctions::EnsureFilenameExtension(QString fn,
|
||||||
|
const QString &extension)
|
||||||
{
|
{
|
||||||
// No-op if either input is empty
|
// No-op if either input is empty
|
||||||
if (!fn.isEmpty() && !extension.isEmpty()) {
|
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||||
QString extension_with_dot;
|
QString extension_with_dot;
|
||||||
|
|
||||||
extension_with_dot.append('.');
|
extension_with_dot.append('.');
|
||||||
extension_with_dot.append(extension);
|
extension_with_dot.append(extension);
|
||||||
|
|
||||||
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||||
fn.append(extension_with_dot);
|
fn.append(extension_with_dot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::ReadFileAsString(const QString &filename)
|
QString FileFunctions::ReadFileAsString(const QString &filename)
|
||||||
{
|
{
|
||||||
QFile f(filename);
|
QFile f(filename);
|
||||||
QString file_data;
|
QString file_data;
|
||||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
QTextStream text_stream(&f);
|
QTextStream text_stream(&f);
|
||||||
file_data = text_stream.readAll();
|
file_data = text_stream.readAll();
|
||||||
f.close();
|
f.close();
|
||||||
}
|
}
|
||||||
return file_data;
|
return file_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
|
QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
|
||||||
{
|
{
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
QFileInfo original_info(original);
|
QFileInfo original_info(original);
|
||||||
QString basename = original_info.baseName();
|
QString basename = original_info.baseName();
|
||||||
QString complete_suffix = original_info.completeSuffix();
|
QString complete_suffix = original_info.completeSuffix();
|
||||||
|
|
||||||
// If we have a complete suffix, make sure there's a period in it
|
// If we have a complete suffix, make sure there's a period in it
|
||||||
if (!complete_suffix.isEmpty()) {
|
if (!complete_suffix.isEmpty()) {
|
||||||
complete_suffix.prepend('.');
|
complete_suffix.prepend('.');
|
||||||
}
|
}
|
||||||
|
|
||||||
QString temp_abs_path;
|
QString temp_abs_path;
|
||||||
do {
|
do {
|
||||||
temp_abs_path = original_info.dir().filePath(
|
temp_abs_path = original_info.dir().filePath(
|
||||||
QStringLiteral("%1.tmp%2%3").arg(basename,
|
QStringLiteral("%1.tmp%2%3")
|
||||||
QString::number(counter),
|
.arg(basename, QString::number(counter), complete_suffix));
|
||||||
complete_suffix));
|
counter++;
|
||||||
counter++;
|
} while (QFileInfo::exists(temp_abs_path));
|
||||||
} while (QFileInfo::exists(temp_abs_path));
|
|
||||||
|
|
||||||
return temp_abs_path;
|
return temp_abs_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString &to)
|
bool FileFunctions::RenameFileAllowOverwrite(const QString &from,
|
||||||
|
const QString &to)
|
||||||
{
|
{
|
||||||
if (QFileInfo::exists(to) && !QFile::remove(to)) {
|
if (QFileInfo::exists(to) && !QFile::remove(to)) {
|
||||||
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
|
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// By this point, we can assume `to` either never existed or has now been deleted
|
// By this point, we can assume `to` either never existed or has now been deleted
|
||||||
if (!QFile::rename(from, to)) {
|
if (!QFile::rename(from, to)) {
|
||||||
qCritical() << "Failed to rename file" << from << "to" << to;
|
qCritical() << "Failed to rename file" << from << "to" << to;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::GetAutoRecoveryRoot()
|
QString FileFunctions::GetAutoRecoveryRoot()
|
||||||
{
|
{
|
||||||
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery"));
|
return QDir(QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::AppLocalDataLocation))
|
||||||
|
.filePath(QStringLiteral("autorecovery"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-26
@@ -26,36 +26,40 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A collection of static file and directory functions
|
* @brief A collection of static file and directory functions
|
||||||
*/
|
*/
|
||||||
class FileFunctions {
|
class FileFunctions {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Returns true if the application is running in portable mode
|
* @brief Returns true if the application is running in portable mode
|
||||||
*
|
*
|
||||||
* In portable mode, any persistent configuration files should be made in a path relative to the application rather
|
* In portable mode, any persistent configuration files should be made in a path relative to the application rather
|
||||||
* than in the user's home folder.
|
* than in the user's home folder.
|
||||||
*/
|
*/
|
||||||
static bool IsPortable();
|
static bool IsPortable();
|
||||||
|
|
||||||
static QString GetUniqueFileIdentifier(const QString& filename);
|
static QString GetUniqueFileIdentifier(const QString &filename);
|
||||||
|
|
||||||
static QString GetConfigurationLocation();
|
static QString GetConfigurationLocation();
|
||||||
|
|
||||||
static QString GetApplicationPath();
|
static QString GetApplicationPath();
|
||||||
|
|
||||||
static QString GetTempFilePath();
|
static QString GetTempFilePath();
|
||||||
|
|
||||||
static bool CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest);
|
static bool CanCopyDirectoryWithoutOverwriting(const QString &source,
|
||||||
|
const QString &dest);
|
||||||
|
|
||||||
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false);
|
static void CopyDirectory(const QString &source, const QString &dest,
|
||||||
|
bool overwrite = false);
|
||||||
|
|
||||||
static bool DirectoryIsValid(const QDir& dir, bool try_to_create_if_not_exists = true);
|
static bool DirectoryIsValid(const QDir &dir,
|
||||||
|
bool try_to_create_if_not_exists = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Ensures a given filename has a certain extension
|
* @brief Ensures a given filename has a certain extension
|
||||||
*
|
*
|
||||||
* Checks if the filename has the extension provided and appends it if not. The extension is
|
* Checks if the filename has the extension provided and appends it if not. The extension is
|
||||||
@@ -64,11 +68,12 @@ public:
|
|||||||
|
|
||||||
* @return The filename provided either untouched or with the extension appended to it.
|
* @return The filename provided either untouched or with the extension appended to it.
|
||||||
*/
|
*/
|
||||||
static QString EnsureFilenameExtension(QString fn, const QString& extension);
|
static QString EnsureFilenameExtension(QString fn,
|
||||||
|
const QString &extension);
|
||||||
|
|
||||||
static QString ReadFileAsString(const QString& filename);
|
static QString ReadFileAsString(const QString &filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a temporary filename that can be used while writing rather than the original
|
* @brief Returns a temporary filename that can be used while writing rather than the original
|
||||||
*
|
*
|
||||||
* If overwriting a file, it's safest to write to a new file first and then only replace it at
|
* If overwriting a file, it's safest to write to a new file first and then only replace it at
|
||||||
@@ -78,28 +83,26 @@ public:
|
|||||||
* This function returns a slight variant of the filename provided that's guaranteed to not exist
|
* This function returns a slight variant of the filename provided that's guaranteed to not exist
|
||||||
* and therefore won't overwrite anything important.
|
* and therefore won't overwrite anything important.
|
||||||
*/
|
*/
|
||||||
static QString GetSafeTemporaryFilename(const QString& original);
|
static QString GetSafeTemporaryFilename(const QString &original);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
|
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
|
||||||
*/
|
*/
|
||||||
static bool RenameFileAllowOverwrite(const QString& from, const QString& to);
|
static bool RenameFileAllowOverwrite(const QString &from,
|
||||||
|
const QString &to);
|
||||||
|
|
||||||
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
|
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
unformatted.append(QStringLiteral(".exe"));
|
unformatted.append(QStringLiteral(".exe"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return unformatted;
|
return unformatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString GetAutoRecoveryRoot();
|
|
||||||
|
|
||||||
|
static QString GetAutoRecoveryRoot();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FILEFUNCTIONS_H
|
#endif // FILEFUNCTIONS_H
|
||||||
|
|||||||
+349
-323
@@ -5,446 +5,472 @@
|
|||||||
|
|
||||||
#include "xmlutils.h"
|
#include "xmlutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
const QVector<QString> Html::kBlockTags = {
|
const QVector<QString> Html::kBlockTags = { QStringLiteral("p"),
|
||||||
QStringLiteral("p"),
|
QStringLiteral("div") };
|
||||||
QStringLiteral("div")
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool StrEquals(const QStringView &a, const QStringView &b)
|
inline bool StrEquals(const QStringView &a, const QStringView &b)
|
||||||
{
|
{
|
||||||
return !a.compare(b, Qt::CaseInsensitive);
|
return !a.compare(b, Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Html::DocToHtml(const QTextDocument *doc)
|
QString Html::DocToHtml(const QTextDocument *doc)
|
||||||
{
|
{
|
||||||
QString html;
|
QString html;
|
||||||
QXmlStreamWriter writer(&html);
|
QXmlStreamWriter writer(&html);
|
||||||
|
|
||||||
//writer.setAutoFormatting(true);
|
//writer.setAutoFormatting(true);
|
||||||
|
|
||||||
for (auto it=doc->begin(); it!=doc->end(); it=it.next()) {
|
for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
|
||||||
WriteBlock(&writer, it);
|
WriteBlock(&writer, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HtmlNode {
|
struct HtmlNode {
|
||||||
QString tag;
|
QString tag;
|
||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
};
|
};
|
||||||
|
|
||||||
QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
|
QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
|
||||||
{
|
{
|
||||||
QTextCharFormat f;
|
QTextCharFormat f;
|
||||||
|
|
||||||
for (int i=0; i<stack.size(); i++) {
|
for (int i = 0; i < stack.size(); i++) {
|
||||||
f.merge(stack.at(i).format);
|
f.merge(stack.at(i).format);
|
||||||
}
|
}
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
||||||
{
|
{
|
||||||
// Empty doc
|
// Empty doc
|
||||||
doc->clear();
|
doc->clear();
|
||||||
bool inside_block = true;
|
bool inside_block = true;
|
||||||
|
|
||||||
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||||
QTextCursor c(doc);
|
QTextCursor c(doc);
|
||||||
|
|
||||||
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||||
QXmlStreamReader reader(wrapped);
|
QXmlStreamReader reader(wrapped);
|
||||||
|
|
||||||
QVector<HtmlNode> fmt_stack;
|
QVector<HtmlNode> fmt_stack;
|
||||||
|
|
||||||
QTextCharFormat default_fmt;
|
QTextCharFormat default_fmt;
|
||||||
default_fmt.setFontWeight(QFont::Normal);
|
default_fmt.setFontWeight(QFont::Normal);
|
||||||
fmt_stack.append({QStringLiteral("html"), default_fmt});
|
fmt_stack.append({ QStringLiteral("html"), default_fmt });
|
||||||
|
|
||||||
QTextCharFormat current_fmt;
|
QTextCharFormat current_fmt;
|
||||||
|
|
||||||
while (!reader.atEnd()) {
|
while (!reader.atEnd()) {
|
||||||
reader.readNext();
|
reader.readNext();
|
||||||
|
|
||||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
|
QString tag = reader.name().toString().toLower();
|
||||||
|
|
||||||
QString tag = reader.name().toString().toLower();
|
fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) });
|
||||||
|
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||||
|
|
||||||
fmt_stack.append({tag, ReadCharFormat(reader.attributes())});
|
if (kBlockTags.contains(tag)) {
|
||||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
QTextBlockFormat block_fmt =
|
||||||
|
ReadBlockFormat(reader.attributes());
|
||||||
|
if (inside_block) {
|
||||||
|
c.setBlockFormat(block_fmt);
|
||||||
|
c.setBlockCharFormat(current_fmt);
|
||||||
|
} else {
|
||||||
|
c.insertBlock(block_fmt, current_fmt);
|
||||||
|
inside_block = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (kBlockTags.contains(tag)) {
|
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||||
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes());
|
QString characters = reader.text().toString();
|
||||||
if (inside_block) {
|
c.insertText(characters, current_fmt);
|
||||||
c.setBlockFormat(block_fmt);
|
|
||||||
c.setBlockCharFormat(current_fmt);
|
|
||||||
} else {
|
|
||||||
c.insertBlock(block_fmt, current_fmt);
|
|
||||||
inside_block = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||||
|
QString tag = reader.name().toString().toLower();
|
||||||
|
|
||||||
QString characters = reader.text().toString();
|
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
|
||||||
c.insertText(characters, current_fmt);
|
if (fmt_stack.at(i).tag == tag) {
|
||||||
|
fmt_stack.removeAt(i);
|
||||||
|
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||||
|
|
||||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
if (kBlockTags.contains(tag)) {
|
||||||
|
inside_block = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString tag = reader.name().toString().toLower();
|
if (reader.error()) {
|
||||||
|
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||||
for (int i=fmt_stack.size()-1; i>=0; i--) {
|
}
|
||||||
if (fmt_stack.at(i).tag == tag) {
|
|
||||||
fmt_stack.removeAt(i);
|
|
||||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
|
||||||
|
|
||||||
if (kBlockTags.contains(tag)) {
|
|
||||||
inside_block = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader.error()) {
|
|
||||||
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
||||||
{
|
{
|
||||||
writer->writeStartElement(QStringLiteral("p"));
|
writer->writeStartElement(QStringLiteral("p"));
|
||||||
|
|
||||||
const QTextBlockFormat &fmt = block.blockFormat();
|
const QTextBlockFormat &fmt = block.blockFormat();
|
||||||
|
|
||||||
// Write block alignment
|
// Write block alignment
|
||||||
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||||
if (fmt.alignment() & Qt::AlignRight) {
|
if (fmt.alignment() & Qt::AlignRight) {
|
||||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("right"));
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
QStringLiteral("right"));
|
||||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center"));
|
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify"));
|
QStringLiteral("center"));
|
||||||
}
|
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||||
}
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
|
QStringLiteral("justify"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RTL support
|
// RTL support
|
||||||
if (block.textDirection() == Qt::RightToLeft) {
|
if (block.textDirection() == Qt::RightToLeft) {
|
||||||
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write CSS attributes
|
// Write CSS attributes
|
||||||
QString style;
|
QString style;
|
||||||
|
|
||||||
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||||
WriteCSSProperty(&style, QStringLiteral("line-height"), QStringLiteral("%1%").arg(fmt.lineHeight()));
|
WriteCSSProperty(&style, QStringLiteral("line-height"),
|
||||||
}
|
QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
WriteCharFormat(&style, block.charFormat());
|
WriteCharFormat(&style, block.charFormat());
|
||||||
|
|
||||||
if (!style.isEmpty()) {
|
if (!style.isEmpty()) {
|
||||||
writer->writeAttribute(QStringLiteral("style"), style);
|
writer->writeAttribute(QStringLiteral("style"), style);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = block.begin();
|
auto it = block.begin();
|
||||||
|
|
||||||
if (it != block.end()) {
|
if (it != block.end()) {
|
||||||
for (; it!=block.end(); it++) {
|
for (; it != block.end(); it++) {
|
||||||
WriteFragment(writer, it.fragment());
|
WriteFragment(writer, it.fragment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer->writeEndElement(); // p
|
writer->writeEndElement(); // p
|
||||||
}
|
}
|
||||||
|
|
||||||
void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment)
|
void Html::WriteFragment(QXmlStreamWriter *writer,
|
||||||
|
const QTextFragment &fragment)
|
||||||
{
|
{
|
||||||
const QTextCharFormat &fmt = fragment.charFormat();
|
const QTextCharFormat &fmt = fragment.charFormat();
|
||||||
|
|
||||||
writer->writeStartElement(QStringLiteral("span"));
|
writer->writeStartElement(QStringLiteral("span"));
|
||||||
|
|
||||||
// Write CSS attributes
|
// Write CSS attributes
|
||||||
QString style;
|
QString style;
|
||||||
|
|
||||||
WriteCharFormat(&style, fmt);
|
WriteCharFormat(&style, fmt);
|
||||||
|
|
||||||
if (!style.isEmpty()) {
|
if (!style.isEmpty()) {
|
||||||
writer->writeAttribute(QStringLiteral("style"), style);
|
writer->writeAttribute(QStringLiteral("style"), style);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||||
bool first_line = true;
|
bool first_line = true;
|
||||||
foreach (const QString &l, lines) {
|
foreach (const QString &l, lines) {
|
||||||
if (first_line) {
|
if (first_line) {
|
||||||
first_line = false;
|
first_line = false;
|
||||||
} else {
|
} else {
|
||||||
writer->writeEmptyElement(QStringLiteral("br"));
|
writer->writeEmptyElement(QStringLiteral("br"));
|
||||||
}
|
}
|
||||||
writer->writeCharacters(l);
|
writer->writeCharacters(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer->writeEndElement(); // span
|
writer->writeEndElement(); // span
|
||||||
}
|
}
|
||||||
|
|
||||||
void Html::WriteCSSProperty(QString *style, const QString &key, const QStringList &values)
|
void Html::WriteCSSProperty(QString *style, const QString &key,
|
||||||
|
const QStringList &values)
|
||||||
{
|
{
|
||||||
QString value;
|
QString value;
|
||||||
foreach (QString v, values) {
|
foreach (QString v, values) {
|
||||||
if (v.contains(' ')) {
|
if (v.contains(' ')) {
|
||||||
v = QStringLiteral("'%1'").arg(v);
|
v = QStringLiteral("'%1'").arg(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendStringAutoSpace(&value, v);
|
AppendStringAutoSpace(&value, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
|
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
|
void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
|
||||||
{
|
{
|
||||||
QStringList families = fmt.fontFamilies().toStringList();
|
QStringList families = fmt.fontFamilies().toStringList();
|
||||||
if (!families.isEmpty()) {
|
if (!families.isEmpty()) {
|
||||||
WriteCSSProperty(style, QStringLiteral("font-family"), families.first());
|
WriteCSSProperty(style, QStringLiteral("font-family"),
|
||||||
}
|
families.first());
|
||||||
|
}
|
||||||
|
|
||||||
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||||
WriteCSSProperty(style, QStringLiteral("font-size"), QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
WriteCSSProperty(
|
||||||
}
|
style, QStringLiteral("font-size"),
|
||||||
|
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||||
|
}
|
||||||
|
|
||||||
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||||
WriteCSSProperty(style, QStringLiteral("font-weight"), QString::number(fmt.fontWeight() * 8));
|
WriteCSSProperty(style, QStringLiteral("font-weight"),
|
||||||
}
|
QString::number(fmt.fontWeight() * 8));
|
||||||
|
}
|
||||||
|
|
||||||
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||||
WriteCSSProperty(style, QStringLiteral("font-style"), fmt.fontItalic() ? QStringLiteral("italic") : QStringLiteral("normal"));
|
WriteCSSProperty(style, QStringLiteral("font-style"),
|
||||||
}
|
fmt.fontItalic() ? QStringLiteral("italic") :
|
||||||
|
QStringLiteral("normal"));
|
||||||
|
}
|
||||||
|
|
||||||
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||||
WriteCSSProperty(style, QStringLiteral("-ove-font-style"), fmt.fontStyleName().toString());
|
WriteCSSProperty(style, QStringLiteral("-ove-font-style"),
|
||||||
}
|
fmt.fontStyleName().toString());
|
||||||
|
}
|
||||||
|
|
||||||
QStringList deco;
|
QStringList deco;
|
||||||
|
|
||||||
if (fmt.fontUnderline()) {
|
if (fmt.fontUnderline()) {
|
||||||
deco.append(QStringLiteral("underline"));
|
deco.append(QStringLiteral("underline"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fmt.fontStrikeOut()) {
|
if (fmt.fontStrikeOut()) {
|
||||||
deco.append(QStringLiteral("line-through"));
|
deco.append(QStringLiteral("line-through"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fmt.fontOverline()) {
|
if (fmt.fontOverline()) {
|
||||||
deco.append(QStringLiteral("overline"));
|
deco.append(QStringLiteral("overline"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!deco.isEmpty()) {
|
if (!deco.isEmpty()) {
|
||||||
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
|
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fmt.foreground().style() != Qt::NoBrush) {
|
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||||
const QColor &color = fmt.foreground().color();
|
const QColor &color = fmt.foreground().color();
|
||||||
QString cs;
|
QString cs;
|
||||||
|
|
||||||
if (color.alpha() == 255) {
|
if (color.alpha() == 255) {
|
||||||
cs = color.name();
|
cs = color.name();
|
||||||
} else if (color.alpha()) {
|
} else if (color.alpha()) {
|
||||||
cs = QStringLiteral("rgba(%1, %2, %3, %4)").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()), QString::number(color.alphaF()));
|
cs = QStringLiteral("rgba(%1, %2, %3, %4)")
|
||||||
}
|
.arg(QString::number(color.red()),
|
||||||
|
QString::number(color.green()),
|
||||||
|
QString::number(color.blue()),
|
||||||
|
QString::number(color.alphaF()));
|
||||||
|
}
|
||||||
|
|
||||||
WriteCSSProperty(style, QStringLiteral("color"), cs);
|
WriteCSSProperty(style, QStringLiteral("color"), cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||||
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||||
WriteCSSProperty(style, QStringLiteral("font-variant"), QStringLiteral("small-caps"));
|
WriteCSSProperty(style, QStringLiteral("font-variant"),
|
||||||
// TODO: Add others
|
QStringLiteral("small-caps"));
|
||||||
}
|
// TODO: Add others
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fmt.fontLetterSpacing() != 0.0) {
|
if (fmt.fontLetterSpacing() != 0.0) {
|
||||||
WriteCSSProperty(style, QStringLiteral("letter-spacing"), QStringLiteral("%1%").arg(QString::number(fmt.fontLetterSpacing())));
|
WriteCSSProperty(style, QStringLiteral("letter-spacing"),
|
||||||
}
|
QStringLiteral("%1%").arg(
|
||||||
|
QString::number(fmt.fontLetterSpacing())));
|
||||||
|
}
|
||||||
|
|
||||||
if (fmt.fontStretch() != 0) {
|
if (fmt.fontStretch() != 0) {
|
||||||
WriteCSSProperty(style, QStringLiteral("font-stretch"), QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
WriteCSSProperty(
|
||||||
}
|
style, QStringLiteral("font-stretch"),
|
||||||
|
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
|
QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
|
||||||
{
|
{
|
||||||
QTextCharFormat fmt;
|
QTextCharFormat fmt;
|
||||||
|
|
||||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||||
if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||||
auto css = GetCSSFromStyle(attr.value().toString());
|
auto css = GetCSSFromStyle(attr.value().toString());
|
||||||
|
|
||||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
for (auto it = css.begin(); it != css.end(); it++) {
|
||||||
const QString &first_val = it.value().first();
|
const QString &first_val = it.value().first();
|
||||||
|
|
||||||
if (it.key() == QStringLiteral("font-family")) {
|
if (it.key() == QStringLiteral("font-family")) {
|
||||||
fmt.setFontFamilies({first_val});
|
fmt.setFontFamilies({ first_val });
|
||||||
} else if (it.key() == QStringLiteral("font-size")) {
|
} else if (it.key() == QStringLiteral("font-size")) {
|
||||||
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) {
|
if (first_val.endsWith(QStringLiteral("pt"),
|
||||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
Qt::CaseInsensitive)) {
|
||||||
}
|
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
}
|
||||||
fmt.setFontWeight(first_val.toInt()/8);
|
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||||
} else if (it.key() == QStringLiteral("font-style")) {
|
fmt.setFontWeight(first_val.toInt() / 8);
|
||||||
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic")));
|
} else if (it.key() == QStringLiteral("font-style")) {
|
||||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
fmt.setFontItalic(
|
||||||
foreach (const QString &v, it.value()) {
|
StrEquals(first_val, QStringLiteral("italic")));
|
||||||
if (StrEquals(v, QStringLiteral("underline"))) {
|
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||||
fmt.setFontUnderline(true);
|
foreach (const QString &v, it.value()) {
|
||||||
} else if (StrEquals(v, QStringLiteral("line-through"))) {
|
if (StrEquals(v, QStringLiteral("underline"))) {
|
||||||
fmt.setFontStrikeOut(true);
|
fmt.setFontUnderline(true);
|
||||||
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
} else if (StrEquals(v,
|
||||||
fmt.setFontOverline(true);
|
QStringLiteral("line-through"))) {
|
||||||
}
|
fmt.setFontStrikeOut(true);
|
||||||
}
|
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
||||||
} else if (it.key() == QStringLiteral("color")) {
|
fmt.setFontOverline(true);
|
||||||
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) {
|
}
|
||||||
QString vals_only = first_val;
|
}
|
||||||
vals_only.remove(QStringLiteral("rgba"));
|
} else if (it.key() == QStringLiteral("color")) {
|
||||||
vals_only.remove(QStringLiteral("("));
|
if (first_val.startsWith(QStringLiteral("rgba"),
|
||||||
vals_only.remove(QStringLiteral(")"));
|
Qt::CaseInsensitive)) {
|
||||||
QStringList rgba = vals_only.split(',');
|
QString vals_only = first_val;
|
||||||
if (rgba.size() == 4) {
|
vals_only.remove(QStringLiteral("rgba"));
|
||||||
QColor c;
|
vals_only.remove(QStringLiteral("("));
|
||||||
c.setRedF(rgba.at(0).toDouble());
|
vals_only.remove(QStringLiteral(")"));
|
||||||
c.setGreenF(rgba.at(1).toDouble());
|
QStringList rgba = vals_only.split(',');
|
||||||
c.setBlueF(rgba.at(2).toDouble());
|
if (rgba.size() == 4) {
|
||||||
c.setAlphaF(rgba.at(3).toDouble());
|
QColor c;
|
||||||
fmt.setForeground(c);
|
c.setRedF(rgba.at(0).toDouble());
|
||||||
}
|
c.setGreenF(rgba.at(1).toDouble());
|
||||||
} else {
|
c.setBlueF(rgba.at(2).toDouble());
|
||||||
fmt.setForeground(QColor(first_val));
|
c.setAlphaF(rgba.at(3).toDouble());
|
||||||
}
|
fmt.setForeground(c);
|
||||||
} else if (it.key() == QStringLiteral("font-variant")) {
|
}
|
||||||
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
|
} else {
|
||||||
fmt.setFontCapitalization(QFont::SmallCaps);
|
fmt.setForeground(QColor(first_val));
|
||||||
}
|
}
|
||||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||||
if (first_val.contains(QChar('%'))) {
|
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
|
||||||
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble());
|
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||||
}
|
}
|
||||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||||
if (first_val.contains(QChar('%'))) {
|
if (first_val.contains(QChar('%'))) {
|
||||||
fmt.setFontStretch(first_val.chopped(1).toInt());
|
fmt.setFontLetterSpacing(
|
||||||
}
|
first_val.chopped(1).toDouble());
|
||||||
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
}
|
||||||
fmt.setFontStyleName(first_val);
|
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||||
}
|
if (first_val.contains(QChar('%'))) {
|
||||||
}
|
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||||
}
|
}
|
||||||
}
|
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||||
return fmt;
|
fmt.setFontStyleName(first_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
|
QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
|
||||||
{
|
{
|
||||||
QTextBlockFormat block_fmt;
|
QTextBlockFormat block_fmt;
|
||||||
|
|
||||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||||
if (StrEquals(attr.name(), QStringLiteral("align"))) {
|
if (StrEquals(attr.name(), QStringLiteral("align"))) {
|
||||||
if (StrEquals(attr.value(), QStringLiteral("right"))) {
|
if (StrEquals(attr.value(), QStringLiteral("right"))) {
|
||||||
block_fmt.setAlignment(Qt::AlignRight);
|
block_fmt.setAlignment(Qt::AlignRight);
|
||||||
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
|
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
|
||||||
block_fmt.setAlignment(Qt::AlignHCenter);
|
block_fmt.setAlignment(Qt::AlignHCenter);
|
||||||
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
|
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
|
||||||
block_fmt.setAlignment(Qt::AlignJustify);
|
block_fmt.setAlignment(Qt::AlignJustify);
|
||||||
}
|
}
|
||||||
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
|
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
|
||||||
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
|
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
|
||||||
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||||
}
|
}
|
||||||
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||||
auto css = GetCSSFromStyle(attr.value().toString());
|
auto css = GetCSSFromStyle(attr.value().toString());
|
||||||
|
|
||||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
for (auto it = css.begin(); it != css.end(); it++) {
|
||||||
if (it.key() == QStringLiteral("line-height")) {
|
if (it.key() == QStringLiteral("line-height")) {
|
||||||
const QString &first_val = it.value().constFirst();
|
const QString &first_val = it.value().constFirst();
|
||||||
if (first_val.contains(QChar('%'))) {
|
if (first_val.contains(QChar('%'))) {
|
||||||
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight);
|
block_fmt.setLineHeight(
|
||||||
}
|
first_val.chopped(1).toDouble(),
|
||||||
}
|
QTextBlockFormat::ProportionalHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return block_fmt;
|
return block_fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Html::AppendStringAutoSpace(QString *s, const QString &append)
|
void Html::AppendStringAutoSpace(QString *s, const QString &append)
|
||||||
{
|
{
|
||||||
if (!s->isEmpty()) {
|
if (!s->isEmpty()) {
|
||||||
s->append(QChar(' '));
|
s->append(QChar(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
s->append(append);
|
s->append(append);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
|
QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
|
||||||
{
|
{
|
||||||
QMap<QString, QStringList> map;
|
QMap<QString, QStringList> map;
|
||||||
|
|
||||||
QStringList list = s.split(QChar(';'));
|
QStringList list = s.split(QChar(';'));
|
||||||
|
|
||||||
foreach (const QString &a, list) {
|
foreach (const QString &a, list) {
|
||||||
QStringList kv = a.split(QChar(':'));
|
QStringList kv = a.split(QChar(':'));
|
||||||
|
|
||||||
if (kv.size() != 2) {
|
if (kv.size() != 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||||
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||||
// match. Also commas should be filtered out.
|
// match. Also commas should be filtered out.
|
||||||
QStringList values;
|
QStringList values;
|
||||||
const QString &val = kv.at(1);
|
const QString &val = kv.at(1);
|
||||||
QChar in_quote(0);
|
QChar in_quote(0);
|
||||||
QString current_str;
|
QString current_str;
|
||||||
for (int i=0; i<val.size(); i++) {
|
for (int i = 0; i < val.size(); i++) {
|
||||||
const QChar ¤t_char = val.at(i);
|
const QChar ¤t_char = val.at(i);
|
||||||
|
|
||||||
if (!in_quote.isNull()) {
|
if (!in_quote.isNull()) {
|
||||||
// If inside quotes and character isn't quote, indiscriminately append char
|
// If inside quotes and character isn't quote, indiscriminately append char
|
||||||
if (current_char == in_quote) {
|
if (current_char == in_quote) {
|
||||||
in_quote = QChar(0);
|
in_quote = QChar(0);
|
||||||
} else {
|
} else {
|
||||||
current_str.append(current_char);
|
current_str.append(current_char);
|
||||||
}
|
}
|
||||||
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||||
// Dump current
|
// Dump current
|
||||||
if (!current_str.isEmpty()) {
|
if (!current_str.isEmpty()) {
|
||||||
values.append(current_str);
|
values.append(current_str);
|
||||||
current_str.clear();
|
current_str.clear();
|
||||||
}
|
}
|
||||||
} else if (in_quote.isNull() && (current_char == QChar('\'') || current_char == QChar('"'))) {
|
} else if (in_quote.isNull() && (current_char == QChar('\'') ||
|
||||||
in_quote = current_char;
|
current_char == QChar('"'))) {
|
||||||
} else {
|
in_quote = current_char;
|
||||||
current_str.append(current_char);
|
} else {
|
||||||
}
|
current_str.append(current_char);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!current_str.isEmpty()) {
|
if (!current_str.isEmpty()) {
|
||||||
values.append(current_str);
|
values.append(current_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||||
if (values.isEmpty()) {
|
if (values.isEmpty()) {
|
||||||
values.append(QString());
|
values.append(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
map[kv.at(0).trimmed().toLower()] = values;
|
map[kv.at(0).trimmed().toLower()] = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-17
@@ -6,7 +6,8 @@
|
|||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Functions for converting HTML to QTextDocument and vice versa
|
* @brief Functions for converting HTML to QTextDocument and vice versa
|
||||||
@@ -26,33 +27,37 @@ namespace olive {
|
|||||||
*/
|
*/
|
||||||
class Html {
|
class Html {
|
||||||
public:
|
public:
|
||||||
static QString DocToHtml(const QTextDocument *doc);
|
static QString DocToHtml(const QTextDocument *doc);
|
||||||
|
|
||||||
static void HtmlToDoc(QTextDocument *doc, const QString &html);
|
static void HtmlToDoc(QTextDocument *doc, const QString &html);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
|
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
|
||||||
|
|
||||||
static void WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment);
|
static void WriteFragment(QXmlStreamWriter *writer,
|
||||||
|
const QTextFragment &fragment);
|
||||||
|
|
||||||
static void WriteCSSProperty(QString *style, const QString &key, const QStringList &value);
|
static void WriteCSSProperty(QString *style, const QString &key,
|
||||||
static void WriteCSSProperty(QString *style, const QString &key, const QString &value)
|
const QStringList &value);
|
||||||
{
|
static void WriteCSSProperty(QString *style, const QString &key,
|
||||||
WriteCSSProperty(style, key, QStringList({value}));
|
const QString &value)
|
||||||
}
|
{
|
||||||
|
WriteCSSProperty(style, key, QStringList({ value }));
|
||||||
|
}
|
||||||
|
|
||||||
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
|
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
|
||||||
|
|
||||||
static QTextCharFormat ReadCharFormat(const QXmlStreamAttributes &attributes);
|
static QTextCharFormat
|
||||||
|
ReadCharFormat(const QXmlStreamAttributes &attributes);
|
||||||
|
|
||||||
static QTextBlockFormat ReadBlockFormat(const QXmlStreamAttributes &attributes);
|
static QTextBlockFormat
|
||||||
|
ReadBlockFormat(const QXmlStreamAttributes &attributes);
|
||||||
|
|
||||||
static void AppendStringAutoSpace(QString *s, const QString &append);
|
static void AppendStringAutoSpace(QString *s, const QString &append);
|
||||||
|
|
||||||
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
|
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
|
||||||
|
|
||||||
static const QVector<QString> kBlockTags;
|
|
||||||
|
|
||||||
|
static const QVector<QString> kBlockTags;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,29 +2,30 @@
|
|||||||
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
uint64_t job_time_index = 0;
|
uint64_t job_time_index = 0;
|
||||||
QMutex job_time_mutex;
|
QMutex job_time_mutex;
|
||||||
|
|
||||||
JobTime::JobTime()
|
JobTime::JobTime()
|
||||||
{
|
{
|
||||||
Acquire();
|
Acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JobTime::Acquire()
|
void JobTime::Acquire()
|
||||||
{
|
{
|
||||||
job_time_mutex.lock();
|
job_time_mutex.lock();
|
||||||
|
|
||||||
value_ = job_time_index;
|
value_ = job_time_index;
|
||||||
job_time_index++;
|
job_time_index++;
|
||||||
|
|
||||||
job_time_mutex.unlock();
|
job_time_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const olive::JobTime& r)
|
QDebug operator<<(QDebug debug, const olive::JobTime &r)
|
||||||
{
|
{
|
||||||
return debug.space() << r.value();
|
return debug.space() << r.value();
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-36
@@ -4,58 +4,57 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class JobTime
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class JobTime {
|
||||||
public:
|
public:
|
||||||
JobTime();
|
JobTime();
|
||||||
|
|
||||||
void Acquire();
|
void Acquire();
|
||||||
|
|
||||||
uint64_t value() const
|
uint64_t value() const
|
||||||
{
|
{
|
||||||
return value_;
|
return value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const JobTime &rhs) const
|
bool operator==(const JobTime &rhs) const
|
||||||
{
|
{
|
||||||
return value_ == rhs.value_;
|
return value_ == rhs.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const JobTime &rhs) const
|
bool operator!=(const JobTime &rhs) const
|
||||||
{
|
{
|
||||||
return value_ != rhs.value_;
|
return value_ != rhs.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const JobTime &rhs) const
|
bool operator<(const JobTime &rhs) const
|
||||||
{
|
{
|
||||||
return value_ < rhs.value_;
|
return value_ < rhs.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator>(const JobTime &rhs) const
|
bool operator>(const JobTime &rhs) const
|
||||||
{
|
{
|
||||||
return value_ > rhs.value_;
|
return value_ > rhs.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator<=(const JobTime &rhs) const
|
bool operator<=(const JobTime &rhs) const
|
||||||
{
|
{
|
||||||
return value_ <= rhs.value_;
|
return value_ <= rhs.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator>=(const JobTime &rhs) const
|
bool operator>=(const JobTime &rhs) const
|
||||||
{
|
{
|
||||||
return value_ >= rhs.value_;
|
return value_ >= rhs.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t value_;
|
uint64_t value_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const olive::JobTime& r);
|
QDebug operator<<(QDebug debug, const olive::JobTime &r);
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(olive::JobTime)
|
Q_DECLARE_METATYPE(olive::JobTime)
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -21,20 +21,21 @@
|
|||||||
#ifndef LERP_H
|
#ifndef LERP_H
|
||||||
#define LERP_H
|
#define LERP_H
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
/**
|
/**
|
||||||
* @brief Linearly interpolate a value between a and b using t
|
* @brief Linearly interpolate a value between a and b using t
|
||||||
*
|
*
|
||||||
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
|
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
|
||||||
* in between a and b at that point linearly.
|
* in between a and b at that point linearly.
|
||||||
*/
|
*/
|
||||||
T lerp(T a, T b, double t) {
|
T lerp(T a, T b, double t)
|
||||||
return (a * (1.0 - t)) + (b * t);
|
{
|
||||||
|
return (a * (1.0 - t)) + (b * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> T lerp(T a, T b, float t)
|
||||||
T lerp(T a, T b, float t) {
|
{
|
||||||
return (a * (1.0f - t)) + (b * t);
|
return (a * (1.0f - t)) + (b * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LERP_H
|
#endif // LERP_H
|
||||||
|
|||||||
+249
-245
@@ -31,7 +31,8 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief MemoryPool base class
|
* @brief MemoryPool base class
|
||||||
@@ -46,386 +47,389 @@ namespace olive {
|
|||||||
* `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
|
* `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
|
||||||
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
|
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
|
||||||
*/
|
*/
|
||||||
class MemoryPool : public QObject
|
class MemoryPool : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructor
|
* @brief Constructor
|
||||||
* @param element_count
|
* @param element_count
|
||||||
*
|
*
|
||||||
* Number of elements per arena
|
* Number of elements per arena
|
||||||
*/
|
*/
|
||||||
MemoryPool(int element_count)
|
MemoryPool(int element_count)
|
||||||
{
|
{
|
||||||
element_count_ = element_count;
|
element_count_ = element_count;
|
||||||
|
|
||||||
clear_timer_ = new QTimer();
|
clear_timer_ = new QTimer();
|
||||||
clear_timer_->setInterval(kMaxEmptyArenaLife);
|
clear_timer_->setInterval(kMaxEmptyArenaLife);
|
||||||
clear_timer_->moveToThread(qApp->thread());
|
clear_timer_->moveToThread(qApp->thread());
|
||||||
connect(clear_timer_, &QTimer::timeout, this, &MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
|
connect(clear_timer_, &QTimer::timeout, this,
|
||||||
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
&MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
|
||||||
}
|
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destructor
|
* @brief Destructor
|
||||||
*
|
*
|
||||||
* Deletes all arenas.
|
* Deletes all arenas.
|
||||||
*/
|
*/
|
||||||
virtual ~MemoryPool()
|
virtual ~MemoryPool()
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
clear_timer_->deleteLater();
|
clear_timer_->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
DISABLE_COPY_MOVE(MemoryPool)
|
DISABLE_COPY_MOVE(MemoryPool)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clears all arenas, freeing all of their memory
|
* @brief Clears all arenas, freeing all of their memory
|
||||||
*
|
*
|
||||||
* Note that this function is not safe, any elements that are still out there will be invalid
|
* Note that this function is not safe, any elements that are still out there will be invalid
|
||||||
* and accessing them will cause a crash. You'll need to make sure all elements are already
|
* and accessing them will cause a crash. You'll need to make sure all elements are already
|
||||||
* relinquished before then.
|
* relinquished before then.
|
||||||
*/
|
*/
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
qDeleteAll(arenas_);
|
qDeleteAll(arenas_);
|
||||||
arenas_.clear();
|
arenas_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns whether any arenas are successfully allocated
|
* @brief Returns whether any arenas are successfully allocated
|
||||||
*/
|
*/
|
||||||
inline bool IsAllocated() const
|
inline bool IsAllocated() const
|
||||||
{
|
{
|
||||||
return !arenas_.empty();
|
return !arenas_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns current number of allocated arenas
|
* @brief Returns current number of allocated arenas
|
||||||
*/
|
*/
|
||||||
inline int GetArenaCount() const
|
inline int GetArenaCount() const
|
||||||
{
|
{
|
||||||
return arenas_.size();
|
return arenas_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Arena;
|
class Arena;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A handle for a chunk of memory in an arena
|
* @brief A handle for a chunk of memory in an arena
|
||||||
*
|
*
|
||||||
* Calling Get() on the pool or arena will return a shared pointer to an element which will contain a pointer to
|
* Calling Get() on the pool or arena will return a shared pointer to an element which will contain a pointer to
|
||||||
* the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the memory
|
* the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the memory
|
||||||
* is released back into the pool so it can be used by another class.
|
* is released back into the pool so it can be used by another class.
|
||||||
*/
|
*/
|
||||||
class Element {
|
class Element {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Element Constructor
|
* @brief Element Constructor
|
||||||
*
|
*
|
||||||
* There is no need to use this outside of the memory pool's internal functions.
|
* There is no need to use this outside of the memory pool's internal functions.
|
||||||
*/
|
*/
|
||||||
Element(Arena* parent, uint8_t* data)
|
Element(Arena *parent, uint8_t *data)
|
||||||
{
|
{
|
||||||
parent_ = parent;
|
parent_ = parent;
|
||||||
data_ = data;
|
data_ = data;
|
||||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Element Destructor
|
* @brief Element Destructor
|
||||||
*
|
*
|
||||||
* Automatically releases this element's memory back to the arena it was retrieved from.
|
* Automatically releases this element's memory back to the arena it was retrieved from.
|
||||||
*/
|
*/
|
||||||
~Element()
|
~Element()
|
||||||
{
|
{
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
|
|
||||||
DISABLE_COPY_MOVE(Element)
|
DISABLE_COPY_MOVE(Element)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access data represented in the pool
|
* @brief Access data represented in the pool
|
||||||
*/
|
*/
|
||||||
inline uint8_t* data() const
|
inline uint8_t *data() const
|
||||||
{
|
{
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const int64_t& timestamp() const
|
inline const int64_t ×tamp() const
|
||||||
{
|
{
|
||||||
return timestamp_;
|
return timestamp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set_timestamp(const int64_t& timestamp)
|
inline void set_timestamp(const int64_t ×tamp)
|
||||||
{
|
{
|
||||||
timestamp_ = timestamp;
|
timestamp_ = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register that this element has been accessed
|
* @brief Register that this element has been accessed
|
||||||
*
|
*
|
||||||
* \see last_accessed()
|
* \see last_accessed()
|
||||||
*/
|
*/
|
||||||
inline void access()
|
inline void access()
|
||||||
{
|
{
|
||||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the last time `access()` was called on this function
|
* @brief Returns the last time `access()` was called on this function
|
||||||
*
|
*
|
||||||
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of
|
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of
|
||||||
* time, it can probably be freed back into the pool). This requires all usages to call `access()`.
|
* time, it can probably be freed back into the pool). This requires all usages to call `access()`.
|
||||||
*/
|
*/
|
||||||
inline const int64_t& last_accessed() const
|
inline const int64_t &last_accessed() const
|
||||||
{
|
{
|
||||||
return accessed_;
|
return accessed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void release()
|
void release()
|
||||||
{
|
{
|
||||||
if (data_) {
|
if (data_) {
|
||||||
parent_->Release(this);
|
parent_->Release(this);
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Arena* parent_;
|
Arena *parent_;
|
||||||
|
|
||||||
uint8_t* data_;
|
uint8_t *data_;
|
||||||
|
|
||||||
int64_t timestamp_;
|
int64_t timestamp_;
|
||||||
|
|
||||||
int64_t accessed_;
|
int64_t accessed_;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
using ElementPtr = std::shared_ptr<Element>;
|
||||||
|
|
||||||
using ElementPtr = std::shared_ptr<Element>;
|
/**
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A memory pool arena - a subsection of memory
|
* @brief A memory pool arena - a subsection of memory
|
||||||
*
|
*
|
||||||
* The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of
|
* The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of
|
||||||
* an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and continue
|
* an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and continue
|
||||||
* providing memory (and freeing arenas when they're no longer in use).
|
* providing memory (and freeing arenas when they're no longer in use).
|
||||||
*/
|
*/
|
||||||
class Arena {
|
class Arena {
|
||||||
public:
|
public:
|
||||||
Arena(MemoryPool* parent)
|
Arena(MemoryPool *parent)
|
||||||
{
|
{
|
||||||
parent_ = parent;
|
parent_ = parent;
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
allocated_sz_ = 0;
|
allocated_sz_ = 0;
|
||||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
|
|
||||||
~Arena()
|
~Arena()
|
||||||
{
|
{
|
||||||
std::list<Element*> copy = lent_elements_;
|
std::list<Element *> copy = lent_elements_;
|
||||||
foreach (Element* e, copy) {
|
foreach (Element *e, copy) {
|
||||||
e->release();
|
e->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete [] data_;
|
delete[] data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
DISABLE_COPY_MOVE(Arena)
|
DISABLE_COPY_MOVE(Arena)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns an element if there is free memory to do so
|
* @brief Returns an element if there is free memory to do so
|
||||||
*/
|
*/
|
||||||
ElementPtr Get()
|
ElementPtr Get()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&lock_);
|
QMutexLocker locker(&lock_);
|
||||||
|
|
||||||
for (int i=0;i<available_.size();i++) {
|
for (int i = 0; i < available_.size(); i++) {
|
||||||
if (available_.at(i)) {
|
if (available_.at(i)) {
|
||||||
// This buffer is available
|
// This buffer is available
|
||||||
available_.replace(i, false);
|
available_.replace(i, false);
|
||||||
|
|
||||||
ElementPtr e = std::make_shared<Element>(this,
|
ElementPtr e = std::make_shared<Element>(
|
||||||
reinterpret_cast<uint8_t*>(data_ + i * element_sz_));
|
this,
|
||||||
lent_elements_.push_back(e.get());
|
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
|
||||||
|
lent_elements_.push_back(e.get());
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Releases an element back into the pool for use elsewhere
|
* @brief Releases an element back into the pool for use elsewhere
|
||||||
*/
|
*/
|
||||||
void Release(Element* e)
|
void Release(Element *e)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&lock_);
|
QMutexLocker locker(&lock_);
|
||||||
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_);
|
quintptr diff = reinterpret_cast<quintptr>(e->data()) -
|
||||||
|
reinterpret_cast<quintptr>(data_);
|
||||||
|
|
||||||
int index = diff / element_sz_;
|
int index = diff / element_sz_;
|
||||||
|
|
||||||
available_.replace(index, true);
|
available_.replace(index, true);
|
||||||
|
|
||||||
lent_elements_.remove(e);
|
lent_elements_.remove(e);
|
||||||
|
|
||||||
if (lent_elements_.empty()) {
|
if (lent_elements_.empty()) {
|
||||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetUsageCount()
|
int GetUsageCount()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&lock_);
|
QMutexLocker locker(&lock_);
|
||||||
return lent_elements_.size();
|
return lent_elements_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Allocate(size_t ele_sz, size_t nb_elements)
|
bool Allocate(size_t ele_sz, size_t nb_elements)
|
||||||
{
|
{
|
||||||
if (IsAllocated()) {
|
if (IsAllocated()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
element_sz_ = ele_sz;
|
element_sz_ = ele_sz;
|
||||||
|
|
||||||
allocated_sz_ = element_sz_ * nb_elements;
|
allocated_sz_ = element_sz_ * nb_elements;
|
||||||
|
|
||||||
if ((data_ = new uint8_t[allocated_sz_])) {
|
if ((data_ = new uint8_t[allocated_sz_])) {
|
||||||
available_.resize(nb_elements);
|
available_.resize(nb_elements);
|
||||||
available_.fill(true);
|
available_.fill(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
available_.clear();
|
available_.clear();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int GetElementCount() const
|
inline int GetElementCount() const
|
||||||
{
|
{
|
||||||
return available_.size();
|
return available_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsAllocated() const
|
inline bool IsAllocated() const
|
||||||
{
|
{
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline qint64 GetTimeArenaWasMadeEmpty()
|
inline qint64 GetTimeArenaWasMadeEmpty()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&lock_);
|
QMutexLocker locker(&lock_);
|
||||||
return empty_time_;
|
return empty_time_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MemoryPool* parent_;
|
MemoryPool *parent_;
|
||||||
|
|
||||||
uint8_t* data_;
|
uint8_t *data_;
|
||||||
|
|
||||||
size_t allocated_sz_;
|
size_t allocated_sz_;
|
||||||
|
|
||||||
QVector<bool> available_;
|
QVector<bool> available_;
|
||||||
|
|
||||||
QMutex lock_;
|
QMutex lock_;
|
||||||
|
|
||||||
size_t element_sz_;
|
size_t element_sz_;
|
||||||
|
|
||||||
std::list<Element*> lent_elements_;
|
std::list<Element *> lent_elements_;
|
||||||
|
|
||||||
qint64 empty_time_;
|
qint64 empty_time_;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
/**
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Retrieves an element from an available arena
|
* @brief Retrieves an element from an available arena
|
||||||
*/
|
*/
|
||||||
ElementPtr Get()
|
ElementPtr Get()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&lock_);
|
QMutexLocker locker(&lock_);
|
||||||
|
|
||||||
// Attempt to get an element from an arena
|
// Attempt to get an element from an arena
|
||||||
foreach (Arena* a, arenas_) {
|
foreach (Arena *a, arenas_) {
|
||||||
ElementPtr e = a->Get();
|
ElementPtr e = a->Get();
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All arenas were empty, we'll need to create a new one
|
// All arenas were empty, we'll need to create a new one
|
||||||
if (arenas_.empty()) {
|
if (arenas_.empty()) {
|
||||||
qDebug() << "No arenas, creating new...";
|
qDebug() << "No arenas, creating new...";
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "All arenas are full, creating new...";
|
qDebug() << "All arenas are full, creating new...";
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ele_sz = GetElementSize();
|
size_t ele_sz = GetElementSize();
|
||||||
|
|
||||||
if (!ele_sz) {
|
if (!ele_sz) {
|
||||||
qCritical() << "Failed to create arena, element size was 0";
|
qCritical() << "Failed to create arena, element size was 0";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element_count_ <= 0) {
|
if (element_count_ <= 0) {
|
||||||
qCritical() << "Failed to create arena, element count was invalid:" << element_count_;
|
qCritical() << "Failed to create arena, element count was invalid:"
|
||||||
return nullptr;
|
<< element_count_;
|
||||||
}
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Arena* a = new Arena(this);
|
Arena *a = new Arena(this);
|
||||||
if (!a->Allocate(ele_sz, element_count_)) {
|
if (!a->Allocate(ele_sz, element_count_)) {
|
||||||
qCritical() << "Failed to create arena, allocation failed. Out of memory?";
|
qCritical()
|
||||||
delete a;
|
<< "Failed to create arena, allocation failed. Out of memory?";
|
||||||
return nullptr;
|
delete a;
|
||||||
}
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
arenas_.push_back(a);
|
arenas_.push_back(a);
|
||||||
return a->Get();
|
return a->Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief The size of each element
|
* @brief The size of each element
|
||||||
*
|
*
|
||||||
* Override this to use a custom size (e.g. a char array where T = char but the element size is > 1)
|
* Override this to use a custom size (e.g. a char array where T = char but the element size is > 1)
|
||||||
*/
|
*/
|
||||||
virtual size_t GetElementSize()
|
virtual size_t GetElementSize()
|
||||||
{
|
{
|
||||||
return sizeof(uint8_t);
|
return sizeof(uint8_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int element_count_;
|
int element_count_;
|
||||||
|
|
||||||
std::list<Arena*> arenas_;
|
std::list<Arena *> arenas_;
|
||||||
|
|
||||||
QMutex lock_;
|
QMutex lock_;
|
||||||
|
|
||||||
QTimer *clear_timer_;
|
QTimer *clear_timer_;
|
||||||
|
|
||||||
static const qint64 kMaxEmptyArenaLife = 5000;
|
static const qint64 kMaxEmptyArenaLife = 5000;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ClearEmptyArenas()
|
void ClearEmptyArenas()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&lock_);
|
QMutexLocker locker(&lock_);
|
||||||
|
|
||||||
const qint64 min_time = QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
|
const qint64 min_time =
|
||||||
|
QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
|
||||||
|
|
||||||
for (auto it=arenas_.begin(); it!=arenas_.end(); ) {
|
for (auto it = arenas_.begin(); it != arenas_.end();) {
|
||||||
Arena* arena = (*it);
|
Arena *arena = (*it);
|
||||||
|
|
||||||
if (arena->GetUsageCount() == 0 && arena->GetTimeArenaWasMadeEmpty() <= min_time) {
|
|
||||||
qDebug() << "Removing an empty arena";
|
|
||||||
delete arena;
|
|
||||||
it = arenas_.erase(it);
|
|
||||||
} else {
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (arena->GetUsageCount() == 0 &&
|
||||||
|
arena->GetTimeArenaWasMadeEmpty() <= min_time) {
|
||||||
|
qDebug() << "Removing an empty arena";
|
||||||
|
delete arena;
|
||||||
|
it = arenas_.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-18
@@ -20,28 +20,29 @@
|
|||||||
|
|
||||||
#include "ocioutils.h"
|
#include "ocioutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
|
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
|
||||||
{
|
{
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case PixelFormat::U8:
|
case PixelFormat::U8:
|
||||||
return OCIO::BIT_DEPTH_UINT8;
|
return OCIO::BIT_DEPTH_UINT8;
|
||||||
case PixelFormat::U16:
|
case PixelFormat::U16:
|
||||||
return OCIO::BIT_DEPTH_UINT16;
|
return OCIO::BIT_DEPTH_UINT16;
|
||||||
break;
|
break;
|
||||||
case PixelFormat::F16:
|
case PixelFormat::F16:
|
||||||
return OCIO::BIT_DEPTH_F16;
|
return OCIO::BIT_DEPTH_F16;
|
||||||
break;
|
break;
|
||||||
case PixelFormat::F32:
|
case PixelFormat::F32:
|
||||||
return OCIO::BIT_DEPTH_F32;
|
return OCIO::BIT_DEPTH_F32;
|
||||||
break;
|
break;
|
||||||
case PixelFormat::INVALID:
|
case PixelFormat::INVALID:
|
||||||
case PixelFormat::COUNT:
|
case PixelFormat::COUNT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OCIO::BIT_DEPTH_UNKNOWN;
|
return OCIO::BIT_DEPTH_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ namespace OCIO = OCIO_NAMESPACE;
|
|||||||
|
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class OCIOUtils
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class OCIOUtils {
|
||||||
public:
|
public:
|
||||||
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
|
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-41
@@ -22,62 +22,58 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
void OIIOUtils::FrameToBuffer(const Frame* frame, OIIO::ImageBuf *buf)
|
|
||||||
{
|
{
|
||||||
buf->set_pixels(OIIO::ROI(),
|
|
||||||
buf->spec().format,
|
void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
|
||||||
frame->const_data(),
|
{
|
||||||
OIIO::AutoStride,
|
buf->set_pixels(OIIO::ROI(), buf->spec().format, frame->const_data(),
|
||||||
frame->linesize_bytes());
|
OIIO::AutoStride, frame->linesize_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame* frame)
|
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame *frame)
|
||||||
{
|
{
|
||||||
buf->get_pixels(OIIO::ROI(),
|
buf->get_pixels(OIIO::ROI(), buf->spec().format, frame->data(),
|
||||||
buf->spec().format,
|
OIIO::AutoStride, frame->linesize_bytes());
|
||||||
frame->data(),
|
|
||||||
OIIO::AutoStride,
|
|
||||||
frame->linesize_bytes());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
|
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
|
||||||
{
|
{
|
||||||
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1));
|
return rational::fromDouble(
|
||||||
|
spec.get_float_attribute("PixelAspectRatio", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
|
PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case OIIO::TypeDesc::UNKNOWN:
|
case OIIO::TypeDesc::UNKNOWN:
|
||||||
case OIIO::TypeDesc::NONE:
|
case OIIO::TypeDesc::NONE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OIIO::TypeDesc::INT8:
|
case OIIO::TypeDesc::INT8:
|
||||||
case OIIO::TypeDesc::INT16:
|
case OIIO::TypeDesc::INT16:
|
||||||
case OIIO::TypeDesc::INT32:
|
case OIIO::TypeDesc::INT32:
|
||||||
case OIIO::TypeDesc::UINT32:
|
case OIIO::TypeDesc::UINT32:
|
||||||
case OIIO::TypeDesc::INT64:
|
case OIIO::TypeDesc::INT64:
|
||||||
case OIIO::TypeDesc::UINT64:
|
case OIIO::TypeDesc::UINT64:
|
||||||
case OIIO::TypeDesc::STRING:
|
case OIIO::TypeDesc::STRING:
|
||||||
case OIIO::TypeDesc::PTR:
|
case OIIO::TypeDesc::PTR:
|
||||||
case OIIO::TypeDesc::LASTBASE:
|
case OIIO::TypeDesc::LASTBASE:
|
||||||
case OIIO::TypeDesc::DOUBLE:
|
case OIIO::TypeDesc::DOUBLE:
|
||||||
qDebug() << "Tried to use unknown OIIO base type";
|
qDebug() << "Tried to use unknown OIIO base type";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OIIO::TypeDesc::UINT8:
|
case OIIO::TypeDesc::UINT8:
|
||||||
return PixelFormat::U8;
|
return PixelFormat::U8;
|
||||||
case OIIO::TypeDesc::UINT16:
|
case OIIO::TypeDesc::UINT16:
|
||||||
return PixelFormat::U16;
|
return PixelFormat::U16;
|
||||||
case OIIO::TypeDesc::HALF:
|
case OIIO::TypeDesc::HALF:
|
||||||
return PixelFormat::F16;
|
return PixelFormat::F16;
|
||||||
case OIIO::TypeDesc::FLOAT:
|
case OIIO::TypeDesc::FLOAT:
|
||||||
return PixelFormat::F32;
|
return PixelFormat::F32;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PixelFormat::INVALID;
|
return PixelFormat::INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-23
@@ -27,37 +27,38 @@
|
|||||||
#include "codec/frame.h"
|
#include "codec/frame.h"
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class OIIOUtils {
|
class OIIOUtils {
|
||||||
public:
|
public:
|
||||||
static OIIO::TypeDesc::BASETYPE GetOIIOBaseTypeFromFormat(PixelFormat format)
|
static OIIO::TypeDesc::BASETYPE
|
||||||
{
|
GetOIIOBaseTypeFromFormat(PixelFormat format)
|
||||||
switch (format) {
|
{
|
||||||
case PixelFormat::U8:
|
switch (format) {
|
||||||
return OIIO::TypeDesc::UINT8;
|
case PixelFormat::U8:
|
||||||
case PixelFormat::U16:
|
return OIIO::TypeDesc::UINT8;
|
||||||
return OIIO::TypeDesc::UINT16;
|
case PixelFormat::U16:
|
||||||
case PixelFormat::F16:
|
return OIIO::TypeDesc::UINT16;
|
||||||
return OIIO::TypeDesc::HALF;
|
case PixelFormat::F16:
|
||||||
case PixelFormat::F32:
|
return OIIO::TypeDesc::HALF;
|
||||||
return OIIO::TypeDesc::FLOAT;
|
case PixelFormat::F32:
|
||||||
case PixelFormat::INVALID:
|
return OIIO::TypeDesc::FLOAT;
|
||||||
case PixelFormat::COUNT:
|
case PixelFormat::INVALID:
|
||||||
break;
|
case PixelFormat::COUNT:
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return OIIO::TypeDesc::UNKNOWN;
|
return OIIO::TypeDesc::UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf* buf);
|
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf);
|
||||||
|
|
||||||
static void BufferToFrame(OIIO::ImageBuf* buf, Frame* frame);
|
static void BufferToFrame(OIIO::ImageBuf *buf, Frame *frame);
|
||||||
|
|
||||||
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
|
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
|
||||||
|
|
||||||
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
|
|
||||||
|
|
||||||
|
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-15
@@ -25,30 +25,31 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
uint32_t ceil_to_power_of_2(uint32_t v)
|
uint32_t ceil_to_power_of_2(uint32_t v)
|
||||||
{
|
{
|
||||||
v--;
|
v--;
|
||||||
v |= v >> 1;
|
v |= v >> 1;
|
||||||
v |= v >> 2;
|
v |= v >> 2;
|
||||||
v |= v >> 4;
|
v |= v >> 4;
|
||||||
v |= v >> 8;
|
v |= v >> 8;
|
||||||
v |= v >> 16;
|
v |= v >> 16;
|
||||||
v++;
|
v++;
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t floor_to_power_of_2(uint32_t x)
|
uint32_t floor_to_power_of_2(uint32_t x)
|
||||||
{
|
{
|
||||||
x = x | (x >> 1);
|
x = x | (x >> 1);
|
||||||
x = x | (x >> 2);
|
x = x | (x >> 2);
|
||||||
x = x | (x >> 4);
|
x = x | (x >> 4);
|
||||||
x = x | (x >> 8);
|
x = x | (x >> 8);
|
||||||
x = x | (x >> 16);
|
x = x | (x >> 16);
|
||||||
|
|
||||||
return x - (x >> 1);
|
return x - (x >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+125
-114
@@ -22,192 +22,203 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString& s) {
|
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString &s)
|
||||||
|
{
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
||||||
return fm.width(s);
|
return fm.width(s);
|
||||||
#else
|
#else
|
||||||
return fm.horizontalAdvance(s);
|
return fm.horizontalAdvance(s);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QFrame *QtUtils::CreateHorizontalLine()
|
QFrame *QtUtils::CreateHorizontalLine()
|
||||||
{
|
{
|
||||||
QFrame* horizontal_line = new QFrame();
|
QFrame *horizontal_line = new QFrame();
|
||||||
horizontal_line->setFrameShape(QFrame::HLine);
|
horizontal_line->setFrameShape(QFrame::HLine);
|
||||||
horizontal_line->setFrameShadow(QFrame::Sunken);
|
horizontal_line->setFrameShadow(QFrame::Sunken);
|
||||||
return horizontal_line;
|
return horizontal_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFrame *QtUtils::CreateVerticalLine()
|
QFrame *QtUtils::CreateVerticalLine()
|
||||||
{
|
{
|
||||||
QFrame *l = CreateHorizontalLine();
|
QFrame *l = CreateHorizontalLine();
|
||||||
l->setFrameShape(QFrame::VLine);
|
l->setFrameShape(QFrame::VLine);
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons)
|
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon,
|
||||||
|
const QString &title, const QString &message,
|
||||||
|
QMessageBox::StandardButtons buttons)
|
||||||
{
|
{
|
||||||
QMessageBox b(parent);
|
QMessageBox b(parent);
|
||||||
b.setIcon(icon);
|
b.setIcon(icon);
|
||||||
b.setWindowModality(Qt::WindowModal);
|
b.setWindowModality(Qt::WindowModal);
|
||||||
b.setWindowTitle(title);
|
b.setWindowTitle(title);
|
||||||
b.setText(message);
|
b.setText(message);
|
||||||
|
|
||||||
uint mask = QMessageBox::FirstButton;
|
uint mask = QMessageBox::FirstButton;
|
||||||
while (mask <= QMessageBox::LastButton) {
|
while (mask <= QMessageBox::LastButton) {
|
||||||
uint sb = buttons & mask;
|
uint sb = buttons & mask;
|
||||||
if (sb) {
|
if (sb) {
|
||||||
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
|
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
|
||||||
}
|
}
|
||||||
mask <<= 1;
|
mask <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.exec();
|
return b.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime QtUtils::GetCreationDate(const QFileInfo &info)
|
QDateTime QtUtils::GetCreationDate(const QFileInfo &info)
|
||||||
{
|
{
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||||
return info.created();
|
return info.created();
|
||||||
#else
|
#else
|
||||||
QDateTime t = info.birthTime();
|
QDateTime t = info.birthTime();
|
||||||
if (!t.isValid()) {
|
if (!t.isValid()) {
|
||||||
t = info.metadataChangeTime();
|
t = info.metadataChangeTime();
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
|
QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
|
||||||
{
|
{
|
||||||
return dt.toString(Qt::TextDate);
|
return dt.toString(Qt::TextDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width)
|
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm,
|
||||||
|
int bounding_width)
|
||||||
{
|
{
|
||||||
QStringList list;
|
QStringList list;
|
||||||
|
|
||||||
QStringList lines = s.split('\n');
|
QStringList lines = s.split('\n');
|
||||||
|
|
||||||
// Iterate every line
|
// Iterate every line
|
||||||
for (int i=0; i<lines.size(); i++) {
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
QString this_line = lines.at(i);
|
QString this_line = lines.at(i);
|
||||||
|
|
||||||
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) {
|
while (this_line.size() > 1 &&
|
||||||
int old_size = this_line.size();
|
QFontMetricsWidth(fm, this_line) >= bounding_width) {
|
||||||
int hard_break = -1;
|
int old_size = this_line.size();
|
||||||
|
int hard_break = -1;
|
||||||
|
|
||||||
for (int j=this_line.size()-1; j>=0; j--) {
|
for (int j = this_line.size() - 1; j >= 0; j--) {
|
||||||
const QChar &char_test = this_line.at(j);
|
const QChar &char_test = this_line.at(j);
|
||||||
|
|
||||||
if (char_test.isSpace()
|
if (char_test.isSpace() || char_test == '-') {
|
||||||
|| char_test == '-') {
|
if (QFontMetricsWidth(fm, this_line.left(j)) <
|
||||||
if (QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
|
bounding_width) {
|
||||||
if (!char_test.isSpace()) {
|
if (!char_test.isSpace()) {
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString chopped = this_line.left(j);
|
QString chopped = this_line.left(j);
|
||||||
|
|
||||||
list.append(chopped);
|
list.append(chopped);
|
||||||
|
|
||||||
while (j < this_line.size() && this_line.at(j).isSpace()) {
|
while (j < this_line.size() &&
|
||||||
j++;
|
this_line.at(j).isSpace()) {
|
||||||
}
|
j++;
|
||||||
this_line.remove(0, j);
|
}
|
||||||
break;
|
this_line.remove(0, j);
|
||||||
}
|
break;
|
||||||
} else if (hard_break == -1 && QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
|
}
|
||||||
// In case we can't find a better place to split, split at the earliest time the line
|
} else if (hard_break == -1 &&
|
||||||
// goes under the width limit
|
QFontMetricsWidth(fm, this_line.left(j)) <
|
||||||
hard_break = j;
|
bounding_width) {
|
||||||
}
|
// In case we can't find a better place to split, split at the earliest time the line
|
||||||
}
|
// goes under the width limit
|
||||||
|
hard_break = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (old_size == this_line.size()) {
|
if (old_size == this_line.size()) {
|
||||||
if (hard_break != -1) {
|
if (hard_break != -1) {
|
||||||
list.append(this_line.left(hard_break));
|
list.append(this_line.left(hard_break));
|
||||||
this_line.remove(0, hard_break);
|
this_line.remove(0, hard_break);
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Failed to find anywhere to wrap. Returning full line.";
|
qWarning()
|
||||||
break;
|
<< "Failed to find anywhere to wrap. Returning full line.";
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this_line.isEmpty()) {
|
if (!this_line.isEmpty()) {
|
||||||
list.append(this_line);
|
list.append(this_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::KeyboardModifiers QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
|
Qt::KeyboardModifiers
|
||||||
|
QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
|
||||||
{
|
{
|
||||||
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
|
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e & Qt::ShiftModifier) {
|
if (e & Qt::ShiftModifier) {
|
||||||
e |= Qt::ControlModifier;
|
e |= Qt::ControlModifier;
|
||||||
e &= ~Qt::ShiftModifier;
|
e &= ~Qt::ShiftModifier;
|
||||||
} else if (e & Qt::ControlModifier) {
|
} else if (e & Qt::ControlModifier) {
|
||||||
e |= Qt::ShiftModifier;
|
e |= Qt::ShiftModifier;
|
||||||
e &= ~Qt::ControlModifier;
|
e &= ~Qt::ControlModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtUtils::SetComboBoxData(QComboBox *cb, int data)
|
void QtUtils::SetComboBoxData(QComboBox *cb, int data)
|
||||||
{
|
{
|
||||||
for (int i=0; i<cb->count(); i++) {
|
for (int i = 0; i < cb->count(); i++) {
|
||||||
if (cb->itemData(i).toInt() == data) {
|
if (cb->itemData(i).toInt() == data) {
|
||||||
cb->setCurrentIndex(i);
|
cb->setCurrentIndex(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data)
|
void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data)
|
||||||
{
|
{
|
||||||
for (int i=0; i<cb->count(); i++) {
|
for (int i = 0; i < cb->count(); i++) {
|
||||||
if (cb->itemData(i).toString() == data) {
|
if (cb->itemData(i).toString() == data) {
|
||||||
cb->setCurrentIndex(i);
|
cb->setCurrentIndex(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor QtUtils::toQColor(const core::Color &i)
|
QColor QtUtils::toQColor(const core::Color &i)
|
||||||
{
|
{
|
||||||
QColor c;
|
QColor c;
|
||||||
|
|
||||||
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
||||||
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
||||||
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
||||||
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
||||||
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace core {
|
namespace core
|
||||||
|
{
|
||||||
|
|
||||||
uint qHash(const core::rational &r, uint seed)
|
uint qHash(const core::rational &r, uint seed)
|
||||||
{
|
{
|
||||||
return ::qHash(r.toDouble(), seed);
|
return ::qHash(r.toDouble(), seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint qHash(const core::TimeRange &r, uint seed)
|
uint qHash(const core::TimeRange &r, uint seed)
|
||||||
{
|
{
|
||||||
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
|
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-40
@@ -29,76 +29,79 @@
|
|||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class QtUtils {
|
class QtUtils {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the width of a string according to certain QFontMetrics
|
* @brief Retrieves the width of a string according to certain QFontMetrics
|
||||||
*
|
*
|
||||||
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the
|
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the
|
||||||
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
|
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
|
||||||
* earlier.
|
* earlier.
|
||||||
*/
|
*/
|
||||||
static int QFontMetricsWidth(QFontMetrics fm, const QString& s);
|
static int QFontMetricsWidth(QFontMetrics fm, const QString &s);
|
||||||
|
|
||||||
static QFrame* CreateHorizontalLine();
|
static QFrame *CreateHorizontalLine();
|
||||||
|
|
||||||
static QFrame* CreateVerticalLine();
|
static QFrame *CreateVerticalLine();
|
||||||
|
|
||||||
static int MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
static int MsgBox(QWidget *parent, QMessageBox::Icon icon,
|
||||||
|
const QString &title, const QString &message,
|
||||||
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||||
|
|
||||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||||
|
|
||||||
static QString GetFormattedDateTime(const QDateTime &dt);
|
static QString GetFormattedDateTime(const QDateTime &dt);
|
||||||
|
|
||||||
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width);
|
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm,
|
||||||
|
int bounding_width);
|
||||||
|
|
||||||
static Qt::KeyboardModifiers FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
|
static Qt::KeyboardModifiers
|
||||||
|
FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
|
||||||
|
|
||||||
static void SetComboBoxData(QComboBox *cb, int data);
|
static void SetComboBoxData(QComboBox *cb, int data);
|
||||||
static void SetComboBoxData(QComboBox *cb, const QString &data);
|
static void SetComboBoxData(QComboBox *cb, const QString &data);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> static T *GetParentOfType(const QObject *child)
|
||||||
static T *GetParentOfType(const QObject *child)
|
{
|
||||||
{
|
QObject *t = child->parent();
|
||||||
QObject *t = child->parent();
|
|
||||||
|
|
||||||
while (t) {
|
while (t) {
|
||||||
if (T *p = dynamic_cast<T*>(t)) {
|
if (T *p = dynamic_cast<T *>(t)) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
t = t->parent();
|
t = t->parent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QColor toQColor(const core::Color &c);
|
static QColor toQColor(const core::Color &c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert a pointer to a value that can be sent between NodeParams
|
* @brief Convert a pointer to a value that can be sent between NodeParams
|
||||||
*/
|
*/
|
||||||
static QVariant PtrToValue(void* ptr)
|
static QVariant PtrToValue(void *ptr)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<quintptr>(ptr);
|
return reinterpret_cast<quintptr>(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert a NodeParam value to a pointer of any kind
|
* @brief Convert a NodeParam value to a pointer of any kind
|
||||||
*/
|
*/
|
||||||
template<class T>
|
template <class T> static T *ValueToPtr(const QVariant &ptr)
|
||||||
static T* ValueToPtr(const QVariant &ptr)
|
{
|
||||||
{
|
return reinterpret_cast<T *>(ptr.value<quintptr>());
|
||||||
return reinterpret_cast<T*>(ptr.value<quintptr>());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace core {
|
namespace core
|
||||||
|
{
|
||||||
|
|
||||||
uint qHash(const core::rational& r, uint seed = 0);
|
uint qHash(const core::rational &r, uint seed = 0);
|
||||||
uint qHash(const core::TimeRange& r, uint seed = 0);
|
uint qHash(const core::TimeRange &r, uint seed = 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -21,10 +21,9 @@
|
|||||||
#ifndef RANGE_H
|
#ifndef RANGE_H
|
||||||
#define RANGE_H
|
#define RANGE_H
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> bool InRange(T a, T b, T range)
|
||||||
bool InRange(T a, T b, T range)
|
|
||||||
{
|
{
|
||||||
return (a >= b - range && a <= b + range);
|
return (a >= b - range && a <= b + range);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // RANGE_H
|
#endif // RANGE_H
|
||||||
|
|||||||
+54
-52
@@ -24,69 +24,71 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
double GetFloatRatioFromUser(QWidget* parent,
|
|
||||||
const QString& title,
|
|
||||||
bool* ok_in)
|
|
||||||
{
|
{
|
||||||
QString s;
|
|
||||||
|
|
||||||
forever {
|
double GetFloatRatioFromUser(QWidget *parent, const QString &title, bool *ok_in)
|
||||||
bool ok;
|
{
|
||||||
|
QString s;
|
||||||
|
|
||||||
s = QInputDialog::getText(parent,
|
forever
|
||||||
title,
|
{
|
||||||
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
bool ok;
|
||||||
QLineEdit::Normal,
|
|
||||||
s,
|
|
||||||
&ok);
|
|
||||||
|
|
||||||
if (!ok) {
|
s = QInputDialog::getText(
|
||||||
// User cancelled dialog, do nothing
|
parent, title,
|
||||||
if (ok_in) {
|
QCoreApplication::translate(
|
||||||
*ok_in = false;
|
"RatioDialog",
|
||||||
}
|
"Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||||
return qSNaN();
|
QLineEdit::Normal, s, &ok);
|
||||||
}
|
|
||||||
|
|
||||||
QStringList ratio_components = s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
if (!ok) {
|
||||||
|
// User cancelled dialog, do nothing
|
||||||
|
if (ok_in) {
|
||||||
|
*ok_in = false;
|
||||||
|
}
|
||||||
|
return qSNaN();
|
||||||
|
}
|
||||||
|
|
||||||
if (ratio_components.size() == 1) {
|
QStringList ratio_components =
|
||||||
bool float_ok;
|
s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
||||||
|
|
||||||
double flt = ratio_components.at(0).toDouble(&float_ok);
|
if (ratio_components.size() == 1) {
|
||||||
|
bool float_ok;
|
||||||
|
|
||||||
if (float_ok && flt > 0) {
|
double flt = ratio_components.at(0).toDouble(&float_ok);
|
||||||
if (ok_in) {
|
|
||||||
*ok_in = true;
|
|
||||||
}
|
|
||||||
return flt;
|
|
||||||
}
|
|
||||||
} else if (ratio_components.size() == 2) {
|
|
||||||
bool numer_ok, denom_ok;
|
|
||||||
|
|
||||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
if (float_ok && flt > 0) {
|
||||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
if (ok_in) {
|
||||||
|
*ok_in = true;
|
||||||
|
}
|
||||||
|
return flt;
|
||||||
|
}
|
||||||
|
} else if (ratio_components.size() == 2) {
|
||||||
|
bool numer_ok, denom_ok;
|
||||||
|
|
||||||
if (numer_ok
|
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||||
&& denom_ok
|
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||||
&& num > 0
|
|
||||||
&& den > 0) {
|
|
||||||
// Exit loop and set this ratio
|
|
||||||
if (ok_in) {
|
|
||||||
*ok_in = true;
|
|
||||||
}
|
|
||||||
return num / den;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QMessageBox::warning(parent,
|
if (numer_ok && denom_ok && num > 0 && den > 0) {
|
||||||
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
// Exit loop and set this ratio
|
||||||
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a "
|
if (ok_in) {
|
||||||
"rational fraction with a ':' or a '/' separator.").arg(s),
|
*ok_in = true;
|
||||||
QMessageBox::Ok);
|
}
|
||||||
}
|
return num / den;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::warning(
|
||||||
|
parent,
|
||||||
|
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
||||||
|
QCoreApplication::translate(
|
||||||
|
"RatioDialog",
|
||||||
|
"Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||||
|
"rational fraction with a ':' or a '/' separator.")
|
||||||
|
.arg(s),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,11 @@
|
|||||||
|
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
double GetFloatRatioFromUser(QWidget* parent,
|
double GetFloatRatioFromUser(QWidget *parent, const QString &title,
|
||||||
const QString& title,
|
bool *ok_in);
|
||||||
bool* ok_in);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-13
@@ -4,24 +4,21 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
|
||||||
template <typename K, typename V>
|
template <typename K, typename V> class ThreadSafeMap {
|
||||||
class ThreadSafeMap
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
ThreadSafeMap() = default;
|
ThreadSafeMap() = default;
|
||||||
|
|
||||||
void insert(K key, V value)
|
void insert(K key, V value)
|
||||||
{
|
{
|
||||||
mutex_.lock();
|
mutex_.lock();
|
||||||
map_.insert(key, value);
|
map_.insert(key, value);
|
||||||
mutex_.unlock();
|
mutex_.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMutex mutex_;
|
QMutex mutex_;
|
||||||
|
|
||||||
QMap<K, V> map_;
|
|
||||||
|
|
||||||
|
QMap<K, V> map_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // THREADSAFEMAP_H
|
#endif // THREADSAFEMAP_H
|
||||||
|
|||||||
+5
-3
@@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
inline QString ToHex(quint64 t) {
|
inline QString ToHex(quint64 t)
|
||||||
return QStringLiteral("%1").arg(t, 0, 16);
|
{
|
||||||
|
return QStringLiteral("%1").arg(t, 0, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -21,10 +21,9 @@
|
|||||||
#ifndef UTIL_H
|
#ifndef UTIL_H
|
||||||
#define UTIL_H
|
#define UTIL_H
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> inline T mid(T a, T b)
|
||||||
inline T mid(T a, T b)
|
|
||||||
{
|
{
|
||||||
return (a + b) * 0.5;
|
return (a + b) * 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // UTIL_H
|
#endif // UTIL_H
|
||||||
|
|||||||
+13
-12
@@ -23,23 +23,24 @@
|
|||||||
#include "node/block/block.h"
|
#include "node/block/block.h"
|
||||||
#include "node/factory.h"
|
#include "node/factory.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
|
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
|
||||||
{
|
{
|
||||||
QXmlStreamReader::TokenType token;
|
QXmlStreamReader::TokenType token;
|
||||||
|
|
||||||
while ((token = reader->readNext()) != QXmlStreamReader::Invalid
|
while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
|
||||||
&& token != QXmlStreamReader::EndDocument
|
token != QXmlStreamReader::EndDocument &&
|
||||||
&& (!cancel_atom || !cancel_atom->IsCancelled())) {
|
(!cancel_atom || !cancel_atom->IsCancelled())) {
|
||||||
if (reader->isEndElement()) {
|
if (reader->isEndElement()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (reader->isStartElement()) {
|
} else if (reader->isStartElement()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@
|
|||||||
#include "render/cancelatom.h"
|
#include "render/cancelatom.h"
|
||||||
#include "undo/undocommand.h"
|
#include "undo/undocommand.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class Block;
|
class Block;
|
||||||
class Node;
|
class Node;
|
||||||
@@ -35,7 +36,7 @@ class NodeInput;
|
|||||||
class NodeGroup;
|
class NodeGroup;
|
||||||
|
|
||||||
#define XMLAttributeLoop(reader, item) \
|
#define XMLAttributeLoop(reader, item) \
|
||||||
foreach (const QXmlStreamAttribute& item, reader->attributes())
|
foreach (const QXmlStreamAttribute &item, reader->attributes())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
|
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
|
||||||
@@ -46,7 +47,8 @@ class NodeGroup;
|
|||||||
*
|
*
|
||||||
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
|
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
|
||||||
*/
|
*/
|
||||||
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr);
|
bool XMLReadNextStartElement(QXmlStreamReader *reader,
|
||||||
|
CancelAtom *cancel_atom = nullptr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+297
-189
@@ -37,278 +37,386 @@
|
|||||||
#include "ui/style/style.h"
|
#include "ui/style/style.h"
|
||||||
#include "window/mainwindow/mainwindow.h"
|
#include "window/mainwindow/mainwindow.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
Config Config::current_config_;
|
Config Config::current_config_;
|
||||||
|
|
||||||
Config::Config()
|
Config::Config()
|
||||||
{
|
{
|
||||||
SetDefaults();
|
SetDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data)
|
void Config::SetEntryInternal(const QString &key, NodeValue::Type type,
|
||||||
|
const QVariant &data)
|
||||||
{
|
{
|
||||||
config_map_[key] = {type, data};
|
config_map_[key] = { type, data };
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Config::GetConfigFilePath()
|
QString Config::GetConfigFilePath()
|
||||||
{
|
{
|
||||||
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("config.xml"));
|
return QDir(FileFunctions::GetConfigurationLocation())
|
||||||
|
.filePath(QStringLiteral("config.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Config &Config::Current()
|
Config &Config::Current()
|
||||||
{
|
{
|
||||||
return current_config_;
|
return current_config_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SetDefaults()
|
void Config::SetDefaults()
|
||||||
{
|
{
|
||||||
config_map_.clear();
|
config_map_.clear();
|
||||||
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle);
|
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText,
|
||||||
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame);
|
StyleManager::kDefaultStyle);
|
||||||
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2)));
|
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
|
Timecode::kTimecodeDropFrame);
|
||||||
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational,
|
||||||
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true);
|
QVariant::fromValue(rational(2)));
|
||||||
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1);
|
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt, 20);
|
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
|
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false);
|
1);
|
||||||
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true);
|
20);
|
||||||
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false);
|
10000);
|
||||||
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
|
||||||
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"),
|
||||||
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage);
|
NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"),
|
||||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
|
NodeValue::kBoolean, true);
|
||||||
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"),
|
||||||
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk);
|
NodeValue::kBoolean, true);
|
||||||
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
|
||||||
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"),
|
||||||
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
|
NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"),
|
||||||
|
NodeValue::kBoolean, true);
|
||||||
|
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean,
|
||||||
|
false);
|
||||||
|
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt,
|
||||||
|
AutoScroll::kPage);
|
||||||
|
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
|
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
|
||||||
|
false);
|
||||||
|
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
|
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"),
|
||||||
|
NodeValue::kInt, ImportTool::kDWSAsk);
|
||||||
|
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
|
||||||
|
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
|
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
|
||||||
|
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
|
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
|
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
|
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"),
|
||||||
|
NodeValue::kBoolean, true);
|
||||||
|
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"),
|
||||||
|
NodeValue::kBoolean, false);
|
||||||
|
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"),
|
||||||
|
NodeValue::kBoolean, false);
|
||||||
|
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean,
|
||||||
|
false);
|
||||||
|
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"),
|
||||||
|
NodeValue::kBoolean, false);
|
||||||
|
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailInOut);
|
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled);
|
Timeline::kThumbnailInOut);
|
||||||
|
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt,
|
||||||
|
Timeline::kWaveformsEnabled);
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
SetEntryInternal(
|
||||||
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
QStringLiteral("DefaultVideoTransition"), NodeValue::kText,
|
||||||
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1)));
|
QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
||||||
|
SetEntryInternal(
|
||||||
|
QStringLiteral("DefaultAudioTransition"), NodeValue::kText,
|
||||||
|
QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
||||||
|
SetEntryInternal(QStringLiteral("DefaultTransitionLength"),
|
||||||
|
NodeValue::kRational, QVariant::fromValue(rational(1)));
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt, 48);
|
SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText, QString());
|
48);
|
||||||
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt, QFont::Bold);
|
SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText,
|
||||||
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean, true);
|
QString());
|
||||||
|
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt,
|
||||||
|
QFont::Bold);
|
||||||
|
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
|
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt, ColorCoding::kRed);
|
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt, ColorCoding::kMaroon);
|
ColorCoding::kRed);
|
||||||
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt, ColorCoding::kOrange);
|
SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt, ColorCoding::kBrown);
|
ColorCoding::kMaroon);
|
||||||
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt, ColorCoding::kYellow);
|
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt, ColorCoding::kOlive);
|
ColorCoding::kOrange);
|
||||||
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt, ColorCoding::kLime);
|
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt, ColorCoding::kGreen);
|
ColorCoding::kBrown);
|
||||||
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt, ColorCoding::kCyan);
|
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt, ColorCoding::kTeal);
|
ColorCoding::kYellow);
|
||||||
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt, ColorCoding::kBlue);
|
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt, ColorCoding::kNavy);
|
ColorCoding::kOlive);
|
||||||
|
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt,
|
||||||
|
ColorCoding::kLime);
|
||||||
|
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt,
|
||||||
|
ColorCoding::kGreen);
|
||||||
|
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt,
|
||||||
|
ColorCoding::kCyan);
|
||||||
|
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt,
|
||||||
|
ColorCoding::kTeal);
|
||||||
|
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt,
|
||||||
|
ColorCoding::kBlue);
|
||||||
|
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt,
|
||||||
|
ColorCoding::kNavy);
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString());
|
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText,
|
||||||
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
|
QString());
|
||||||
|
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000);
|
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
|
48000);
|
||||||
SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
|
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"),
|
||||||
|
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
|
||||||
|
SetEntryInternal(
|
||||||
|
QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText,
|
||||||
|
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV);
|
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM);
|
ExportFormat::kFormatWAV);
|
||||||
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000);
|
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
|
ExportCodec::kCodecPCM);
|
||||||
SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
|
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"),
|
||||||
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320);
|
NodeValue::kInt, 48000);
|
||||||
|
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"),
|
||||||
|
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
|
||||||
|
SetEntryInternal(
|
||||||
|
QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText,
|
||||||
|
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
|
||||||
|
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt,
|
||||||
|
320);
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0)));
|
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational,
|
||||||
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60)));
|
QVariant::fromValue(rational(0)));
|
||||||
|
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational,
|
||||||
|
QVariant::fromValue(rational(60)));
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920);
|
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080);
|
1920);
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1)));
|
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeValue::kRational, QVariant::fromValue(rational(1001, 30000)));
|
1080);
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeValue::kInt, VideoParams::kInterlaceNone);
|
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"),
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"), NodeValue::kBoolean, false);
|
NodeValue::kRational, QVariant::fromValue(rational(1)));
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeValue::kInt, 48000);
|
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"),
|
||||||
SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt, QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
|
NodeValue::kRational,
|
||||||
|
QVariant::fromValue(rational(1001, 30000)));
|
||||||
|
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"),
|
||||||
|
NodeValue::kInt, VideoParams::kInterlaceNone);
|
||||||
|
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"),
|
||||||
|
NodeValue::kBoolean, false);
|
||||||
|
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"),
|
||||||
|
NodeValue::kInt, 48000);
|
||||||
|
SetEntryInternal(
|
||||||
|
QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt,
|
||||||
|
QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
|
||||||
|
|
||||||
// Online/offline settings
|
// Online/offline settings
|
||||||
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, PixelFormat::F32);
|
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt,
|
||||||
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, PixelFormat::F16);
|
PixelFormat::F32);
|
||||||
|
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt,
|
||||||
|
PixelFormat::F16);
|
||||||
|
|
||||||
SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt, ColorCoding::kLime);
|
SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt,
|
||||||
|
ColorCoding::kLime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Load()
|
void Config::Load()
|
||||||
{
|
{
|
||||||
QFile config_file(GetConfigFilePath());
|
QFile config_file(GetConfigFilePath());
|
||||||
|
|
||||||
if (!config_file.exists()) {
|
if (!config_file.exists()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config_file.open(QFile::ReadOnly)) {
|
if (!config_file.open(QFile::ReadOnly)) {
|
||||||
qWarning() << "Failed to load application settings. This session will use defaults.";
|
qWarning()
|
||||||
return;
|
<< "Failed to load application settings. This session will use defaults.";
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset to defaults
|
// Reset to defaults
|
||||||
current_config_.SetDefaults();
|
current_config_.SetDefaults();
|
||||||
|
|
||||||
QXmlStreamReader reader(&config_file);
|
QXmlStreamReader reader(&config_file);
|
||||||
|
|
||||||
QString config_version;
|
QString config_version;
|
||||||
|
|
||||||
while (XMLReadNextStartElement(&reader)) {
|
while (XMLReadNextStartElement(&reader)) {
|
||||||
if (reader.name() == QStringLiteral("Configuration")) {
|
if (reader.name() == QStringLiteral("Configuration")) {
|
||||||
while (XMLReadNextStartElement(&reader)) {
|
while (XMLReadNextStartElement(&reader)) {
|
||||||
QString key = reader.name().toString();
|
QString key = reader.name().toString();
|
||||||
QString value = reader.readElementText();
|
QString value = reader.readElementText();
|
||||||
|
|
||||||
if (key == QStringLiteral("Version")) {
|
if (key == QStringLiteral("Version")) {
|
||||||
config_version = value;
|
config_version = value;
|
||||||
|
|
||||||
if (!value.contains(".")) {
|
if (!value.contains(".")) {
|
||||||
qDebug() << "CONFIG: This is a 0.1.x config file, upconvert";
|
qDebug()
|
||||||
}
|
<< "CONFIG: This is a 0.1.x config file, upconvert";
|
||||||
} else if (key == QStringLiteral("DefaultSequenceFrameRate") && !config_version.contains('.')) {
|
}
|
||||||
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
|
} else if (key == QStringLiteral("DefaultSequenceFrameRate") &&
|
||||||
// supported rational
|
!config_version.contains('.')) {
|
||||||
qDebug() << " CONFIG: Finding closest match to" << value;
|
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
|
||||||
|
// supported rational
|
||||||
|
qDebug() << " CONFIG: Finding closest match to" << value;
|
||||||
|
|
||||||
double config_fr = value.toDouble();
|
double config_fr = value.toDouble();
|
||||||
|
|
||||||
const QVector<rational>& supported_frame_rates = VideoParams::kSupportedFrameRates;
|
const QVector<rational> &supported_frame_rates =
|
||||||
|
VideoParams::kSupportedFrameRates;
|
||||||
|
|
||||||
rational match = supported_frame_rates.first();
|
rational match = supported_frame_rates.first();
|
||||||
double match_diff = qAbs(match.toDouble() - config_fr);
|
double match_diff = qAbs(match.toDouble() - config_fr);
|
||||||
|
|
||||||
for (int i=1;i<supported_frame_rates.size();i++) {
|
for (int i = 1; i < supported_frame_rates.size(); i++) {
|
||||||
double diff = qAbs(supported_frame_rates.at(i).toDouble() - config_fr);
|
double diff = qAbs(
|
||||||
|
supported_frame_rates.at(i).toDouble() - config_fr);
|
||||||
|
|
||||||
if (diff < match_diff) {
|
if (diff < match_diff) {
|
||||||
match = supported_frame_rates.at(i);
|
match = supported_frame_rates.at(i);
|
||||||
match_diff = diff;
|
match_diff = diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << " CONFIG: Closest match was" << match.toDouble();
|
qDebug()
|
||||||
|
<< " CONFIG: Closest match was" << match.toDouble();
|
||||||
|
|
||||||
current_config_[key] = QVariant::fromValue(match.flipped());
|
current_config_[key] = QVariant::fromValue(match.flipped());
|
||||||
} else {
|
} else {
|
||||||
current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false);
|
current_config_[key] = NodeValue::StringToValue(
|
||||||
}
|
current_config_.GetConfigEntryType(key), value, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//reader.skipCurrentElement();
|
//reader.skipCurrentElement();
|
||||||
} else {
|
} else {
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.hasError()) {
|
if (reader.hasError()) {
|
||||||
QMessageBox::critical(Core::instance()->main_window(),
|
QMessageBox::critical(
|
||||||
QCoreApplication::translate("Config", "Error loading settings"),
|
Core::instance()->main_window(),
|
||||||
QCoreApplication::translate("Config", "Failed to load application settings. This session will "
|
QCoreApplication::translate("Config", "Error loading settings"),
|
||||||
"use defaults.\n\n%1").arg(reader.errorString()),
|
QCoreApplication::translate(
|
||||||
QMessageBox::Ok);
|
"Config",
|
||||||
current_config_.SetDefaults();
|
"Failed to load application settings. This session will "
|
||||||
}
|
"use defaults.\n\n%1")
|
||||||
|
.arg(reader.errorString()),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
current_config_.SetDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
config_file.close();
|
config_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Save()
|
void Config::Save()
|
||||||
{
|
{
|
||||||
QString real_filename = GetConfigFilePath();
|
QString real_filename = GetConfigFilePath();
|
||||||
QString temp_filename = FileFunctions::GetSafeTemporaryFilename(real_filename);
|
QString temp_filename =
|
||||||
|
FileFunctions::GetSafeTemporaryFilename(real_filename);
|
||||||
|
|
||||||
QFile config_file(temp_filename);
|
QFile config_file(temp_filename);
|
||||||
|
|
||||||
if (!config_file.open(QFile::WriteOnly)) {
|
if (!config_file.open(QFile::WriteOnly)) {
|
||||||
QMessageBox::critical(Core::instance()->main_window(),
|
QMessageBox::critical(
|
||||||
QCoreApplication::translate("Config", "Error saving settings"),
|
Core::instance()->main_window(),
|
||||||
QCoreApplication::translate("Config", "Failed to save application settings. The application "
|
QCoreApplication::translate("Config", "Error saving settings"),
|
||||||
"may lack write permissions for this location."),
|
QCoreApplication::translate(
|
||||||
QMessageBox::Ok);
|
"Config",
|
||||||
return;
|
"Failed to save application settings. The application "
|
||||||
}
|
"may lack write permissions for this location."),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QXmlStreamWriter writer(&config_file);
|
QXmlStreamWriter writer(&config_file);
|
||||||
writer.setAutoFormatting(true);
|
writer.setAutoFormatting(true);
|
||||||
|
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
|
|
||||||
writer.writeStartElement("Configuration");
|
writer.writeStartElement("Configuration");
|
||||||
|
|
||||||
// Anything after the hyphen is considered "unimportant" information
|
// Anything after the hyphen is considered "unimportant" information
|
||||||
writer.writeTextElement("Version", QCoreApplication::applicationVersion().split('-').first());
|
writer.writeTextElement(
|
||||||
|
"Version", QCoreApplication::applicationVersion().split('-').first());
|
||||||
|
|
||||||
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
|
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
iterator.next();
|
iterator.next();
|
||||||
|
|
||||||
QString value = NodeValue::ValueToString(iterator.value().type, iterator.value().data, false);
|
QString value = NodeValue::ValueToString(iterator.value().type,
|
||||||
|
iterator.value().data, false);
|
||||||
|
|
||||||
if (iterator.value().type == NodeValue::kNone) {
|
if (iterator.value().type == NodeValue::kNone) {
|
||||||
qWarning() << "Config key" << iterator.key() << "had null type and was discarded";
|
qWarning() << "Config key" << iterator.key()
|
||||||
} else {
|
<< "had null type and was discarded";
|
||||||
writer.writeTextElement(iterator.key(), value);
|
} else {
|
||||||
}
|
writer.writeTextElement(iterator.key(), value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writer.writeEndElement(); // Configuration
|
writer.writeEndElement(); // Configuration
|
||||||
|
|
||||||
writer.writeEndDocument();
|
writer.writeEndDocument();
|
||||||
|
|
||||||
config_file.close();
|
config_file.close();
|
||||||
|
|
||||||
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename, real_filename)) {
|
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename,
|
||||||
qWarning() << QStringLiteral("Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
|
real_filename)) {
|
||||||
.arg(real_filename, temp_filename);
|
qWarning()
|
||||||
}
|
<< QStringLiteral(
|
||||||
|
"Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
|
||||||
|
.arg(real_filename, temp_filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant Config::operator[](const QString &key) const
|
QVariant Config::operator[](const QString &key) const
|
||||||
{
|
{
|
||||||
return config_map_[key].data;
|
return config_map_[key].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant &Config::operator[](const QString &key)
|
QVariant &Config::operator[](const QString &key)
|
||||||
{
|
{
|
||||||
return config_map_[key].data;
|
return config_map_[key].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValue::Type Config::GetConfigEntryType(const QString &key) const
|
NodeValue::Type Config::GetConfigEntryType(const QString &key) const
|
||||||
{
|
{
|
||||||
return config_map_[key].type;
|
return config_map_[key].type;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-17
@@ -27,42 +27,44 @@
|
|||||||
|
|
||||||
#include "node/value.h"
|
#include "node/value.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
|
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
|
||||||
#define OLIVE_CONFIG_STR(x) Config::Current()[x]
|
#define OLIVE_CONFIG_STR(x) Config::Current()[x]
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
public:
|
public:
|
||||||
static Config& Current();
|
static Config &Current();
|
||||||
|
|
||||||
void SetDefaults();
|
void SetDefaults();
|
||||||
|
|
||||||
static void Load();
|
static void Load();
|
||||||
|
|
||||||
static void Save();
|
static void Save();
|
||||||
|
|
||||||
QVariant operator[](const QString&) const;
|
QVariant operator[](const QString &) const;
|
||||||
|
|
||||||
QVariant& operator[](const QString&);
|
QVariant &operator[](const QString &);
|
||||||
|
|
||||||
NodeValue::Type GetConfigEntryType(const QString& key) const;
|
NodeValue::Type GetConfigEntryType(const QString &key) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Config();
|
Config();
|
||||||
|
|
||||||
struct ConfigEntry {
|
struct ConfigEntry {
|
||||||
NodeValue::Type type;
|
NodeValue::Type type;
|
||||||
QVariant data;
|
QVariant data;
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data);
|
void SetEntryInternal(const QString &key, NodeValue::Type type,
|
||||||
|
const QVariant &data);
|
||||||
|
|
||||||
QMap<QString, ConfigEntry> config_map_;
|
QMap<QString, ConfigEntry> config_map_;
|
||||||
|
|
||||||
static Config current_config_;
|
static Config current_config_;
|
||||||
|
|
||||||
static QString GetConfigFilePath();
|
static QString GetConfigFilePath();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1042
-956
File diff suppressed because it is too large
Load Diff
+256
-255
@@ -35,7 +35,8 @@
|
|||||||
#include "undo/undostack.h"
|
#include "undo/undostack.h"
|
||||||
#include "widget/projectexplorer/projectviewmodel.h"
|
#include "widget/projectexplorer/projectviewmodel.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
|
||||||
@@ -48,132 +49,125 @@ class MainWindow;
|
|||||||
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
||||||
* opening the import dialog, etc.)
|
* opening the import dialog, etc.)
|
||||||
*/
|
*/
|
||||||
class Core : public QObject
|
class Core : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
class CoreParams
|
class CoreParams {
|
||||||
{
|
public:
|
||||||
public:
|
CoreParams();
|
||||||
CoreParams();
|
|
||||||
|
|
||||||
enum RunMode {
|
enum RunMode { kRunNormal, kHeadlessExport, kHeadlessPreCache };
|
||||||
kRunNormal,
|
|
||||||
kHeadlessExport,
|
|
||||||
kHeadlessPreCache
|
|
||||||
};
|
|
||||||
|
|
||||||
bool fullscreen() const
|
bool fullscreen() const
|
||||||
{
|
{
|
||||||
return run_fullscreen_;
|
return run_fullscreen_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_fullscreen(bool e)
|
void set_fullscreen(bool e)
|
||||||
{
|
{
|
||||||
run_fullscreen_ = e;
|
run_fullscreen_ = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
RunMode run_mode() const
|
RunMode run_mode() const
|
||||||
{
|
{
|
||||||
return mode_;
|
return mode_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_run_mode(RunMode m)
|
void set_run_mode(RunMode m)
|
||||||
{
|
{
|
||||||
mode_ = m;
|
mode_ = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString startup_project() const
|
const QString startup_project() const
|
||||||
{
|
{
|
||||||
return startup_project_;
|
return startup_project_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_startup_project(const QString& p)
|
void set_startup_project(const QString &p)
|
||||||
{
|
{
|
||||||
startup_project_ = p;
|
startup_project_ = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& startup_language() const
|
const QString &startup_language() const
|
||||||
{
|
{
|
||||||
return startup_language_;
|
return startup_language_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_startup_language(const QString& s)
|
void set_startup_language(const QString &s)
|
||||||
{
|
{
|
||||||
startup_language_ = s;
|
startup_language_ = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool crash_on_startup() const
|
bool crash_on_startup() const
|
||||||
{
|
{
|
||||||
return crash_;
|
return crash_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_crash_on_startup(bool e)
|
void set_crash_on_startup(bool e)
|
||||||
{
|
{
|
||||||
crash_ = true;
|
crash_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RunMode mode_;
|
RunMode mode_;
|
||||||
|
|
||||||
QString startup_project_;
|
QString startup_project_;
|
||||||
|
|
||||||
QString startup_language_;
|
QString startup_language_;
|
||||||
|
|
||||||
bool run_fullscreen_;
|
bool run_fullscreen_;
|
||||||
|
|
||||||
bool crash_;
|
bool crash_;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
/**
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Core Constructor
|
* @brief Core Constructor
|
||||||
*
|
*
|
||||||
* Currently empty
|
* Currently empty
|
||||||
*/
|
*/
|
||||||
Core(const CoreParams& params);
|
Core(const CoreParams ¶ms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Core object accessible from anywhere in the code
|
* @brief Core object accessible from anywhere in the code
|
||||||
*
|
*
|
||||||
* Use this to access Core functions.
|
* Use this to access Core functions.
|
||||||
*/
|
*/
|
||||||
static Core* instance();
|
static Core *instance();
|
||||||
|
|
||||||
const CoreParams& core_params() const
|
const CoreParams &core_params() const
|
||||||
{
|
{
|
||||||
return core_params_;
|
return core_params_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start Olive Core
|
* @brief Start Olive Core
|
||||||
*
|
*
|
||||||
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
|
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
|
||||||
*/
|
*/
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Stop Olive Core
|
* @brief Stop Olive Core
|
||||||
*
|
*
|
||||||
* Ends all threads and frees all memory ready for the application to exit.
|
* Ends all threads and frees all memory ready for the application to exit.
|
||||||
*/
|
*/
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve main window instance
|
* @brief Retrieve main window instance
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
|
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
|
||||||
*/
|
*/
|
||||||
MainWindow* main_window();
|
MainWindow *main_window();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve UndoStack object
|
* @brief Retrieve UndoStack object
|
||||||
*/
|
*/
|
||||||
UndoStack* undo_stack();
|
UndoStack *undo_stack();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Import a list of files
|
* @brief Import a list of files
|
||||||
*
|
*
|
||||||
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
|
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
|
||||||
@@ -181,34 +175,34 @@ public:
|
|||||||
*
|
*
|
||||||
* @param urls
|
* @param urls
|
||||||
*/
|
*/
|
||||||
void ImportFiles(const QStringList& urls, Folder *parent);
|
void ImportFiles(const QStringList &urls, Folder *parent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the currently active tool
|
* @brief Get the currently active tool
|
||||||
*/
|
*/
|
||||||
const Tool::Item& tool() const;
|
const Tool::Item &tool() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the currently selected object that the add tool should make (if the add tool is active)
|
* @brief Get the currently selected object that the add tool should make (if the add tool is active)
|
||||||
*/
|
*/
|
||||||
const Tool::AddableObject& GetSelectedAddableObject() const;
|
const Tool::AddableObject &GetSelectedAddableObject() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
|
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
|
||||||
*/
|
*/
|
||||||
const QString& GetSelectedTransition() const;
|
const QString &GetSelectedTransition() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get current snapping value
|
* @brief Get current snapping value
|
||||||
*/
|
*/
|
||||||
const bool& snapping() const;
|
const bool &snapping() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a list of the most recently opened/saved projects
|
* @brief Returns a list of the most recently opened/saved projects
|
||||||
*/
|
*/
|
||||||
const QStringList& GetRecentProjects() const;
|
const QStringList &GetRecentProjects() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the currently active project
|
* @brief Get the currently active project
|
||||||
*
|
*
|
||||||
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
|
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
|
||||||
@@ -218,402 +212,409 @@ public:
|
|||||||
*
|
*
|
||||||
* The active Project file, or nullptr if the heuristic couldn't find one.
|
* The active Project file, or nullptr if the heuristic couldn't find one.
|
||||||
*/
|
*/
|
||||||
Project* GetActiveProject() const;
|
Project *GetActiveProject() const;
|
||||||
Folder* GetSelectedFolderInActiveProject() const;
|
Folder *GetSelectedFolderInActiveProject() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets current timecode display mode
|
* @brief Gets current timecode display mode
|
||||||
*/
|
*/
|
||||||
Timecode::Display GetTimecodeDisplay() const;
|
Timecode::Display GetTimecodeDisplay() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets current timecode display mode
|
* @brief Sets current timecode display mode
|
||||||
*/
|
*/
|
||||||
void SetTimecodeDisplay(Timecode::Display d);
|
void SetTimecodeDisplay(Timecode::Display d);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
|
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
|
||||||
*/
|
*/
|
||||||
void SetAutorecoveryInterval(int minutes);
|
void SetAutorecoveryInterval(int minutes);
|
||||||
|
|
||||||
static void CopyStringToClipboard(const QString& s);
|
static void CopyStringToClipboard(const QString &s);
|
||||||
|
|
||||||
static QString PasteStringFromClipboard();
|
static QString PasteStringFromClipboard();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Recursively count files in a file/directory list
|
* @brief Recursively count files in a file/directory list
|
||||||
*/
|
*/
|
||||||
static int CountFilesInFileList(const QFileInfoList &filenames);
|
static int CountFilesInFileList(const QFileInfoList &filenames);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show a dialog to the user to rename a set of nodes
|
* @brief Show a dialog to the user to rename a set of nodes
|
||||||
*/
|
*/
|
||||||
bool LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent = nullptr);
|
bool LabelNodes(const QVector<Node *> &nodes,
|
||||||
|
MultiUndoCommand *parent = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new sequence named appropriately for the active project
|
* @brief Create a new sequence named appropriately for the active project
|
||||||
*/
|
*/
|
||||||
static Sequence* CreateNewSequenceForProject(const QString &format, Project *project);
|
static Sequence *CreateNewSequenceForProject(const QString &format,
|
||||||
static Sequence* CreateNewSequenceForProject(Project *project)
|
Project *project);
|
||||||
{
|
static Sequence *CreateNewSequenceForProject(Project *project)
|
||||||
return CreateNewSequenceForProject(tr("Sequence %1"), project);
|
{
|
||||||
}
|
return CreateNewSequenceForProject(tr("Sequence %1"), project);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Opens a project from the recently opened list
|
* @brief Opens a project from the recently opened list
|
||||||
*/
|
*/
|
||||||
void OpenProjectFromRecentList(int index);
|
void OpenProjectFromRecentList(int index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes a project
|
* @brief Closes a project
|
||||||
*/
|
*/
|
||||||
bool CloseProject(bool auto_open_new, bool ignore_modified = false);
|
bool CloseProject(bool auto_open_new, bool ignore_modified = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Runs a modal cache task on the currently active sequence
|
* @brief Runs a modal cache task on the currently active sequence
|
||||||
*/
|
*/
|
||||||
void CacheActiveSequence(bool in_out_only);
|
void CacheActiveSequence(bool in_out_only);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check each footage object for whether it still exists or has changed
|
* @brief Check each footage object for whether it still exists or has changed
|
||||||
*/
|
*/
|
||||||
bool ValidateFootageInLoadedProject(Project* project, const QString &project_saved_url);
|
bool ValidateFootageInLoadedProject(Project *project,
|
||||||
|
const QString &project_saved_url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Changes the current language
|
* @brief Changes the current language
|
||||||
*/
|
*/
|
||||||
bool SetLanguage(const QString& locale);
|
bool SetLanguage(const QString &locale);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show message in main window's status bar
|
* @brief Show message in main window's status bar
|
||||||
*
|
*
|
||||||
* Shorthand for Core::instance()->main_window()->statusBar()->showMessage();
|
* Shorthand for Core::instance()->main_window()->statusBar()->showMessage();
|
||||||
*/
|
*/
|
||||||
void ShowStatusBarMessage(const QString& s, int timeout = 0);
|
void ShowStatusBarMessage(const QString &s, int timeout = 0);
|
||||||
|
|
||||||
void ClearStatusBarMessage();
|
void ClearStatusBarMessage();
|
||||||
|
|
||||||
void OpenRecoveryProject(const QString& filename);
|
void OpenRecoveryProject(const QString &filename);
|
||||||
|
|
||||||
void OpenNodeInViewer(ViewerOutput* viewer);
|
void OpenNodeInViewer(ViewerOutput *viewer);
|
||||||
|
|
||||||
void OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image);
|
void OpenExportDialogForViewer(ViewerOutput *viewer,
|
||||||
|
bool start_still_image);
|
||||||
|
|
||||||
bool IsMagicEnabled() const { return magic_; }
|
bool IsMagicEnabled() const
|
||||||
|
{
|
||||||
|
return magic_;
|
||||||
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* @brief Starts an open file dialog to load a project from file
|
* @brief Starts an open file dialog to load a project from file
|
||||||
*/
|
*/
|
||||||
void OpenProject();
|
void OpenProject();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Saves the current project
|
* @brief Saves the current project
|
||||||
*/
|
*/
|
||||||
bool SaveProject();
|
bool SaveProject();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Performs a "save as" on the current project
|
* @brief Performs a "save as" on the current project
|
||||||
*/
|
*/
|
||||||
bool SaveProjectAs();
|
bool SaveProjectAs();
|
||||||
|
|
||||||
void RevertProject();
|
void RevertProject();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the current application-wide tool
|
* @brief Set the current application-wide tool
|
||||||
*
|
*
|
||||||
* @param tool
|
* @param tool
|
||||||
*/
|
*/
|
||||||
void SetTool(const Tool::Item& tool);
|
void SetTool(const Tool::Item &tool);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the current snapping setting
|
* @brief Set the current snapping setting
|
||||||
*/
|
*/
|
||||||
void SetSnapping(const bool& b);
|
void SetSnapping(const bool &b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show an About dialog
|
* @brief Show an About dialog
|
||||||
*/
|
*/
|
||||||
void DialogAboutShow();
|
void DialogAboutShow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Open the import footage dialog and import the files selected (runs ImportFiles())
|
* @brief Open the import footage dialog and import the files selected (runs ImportFiles())
|
||||||
*/
|
*/
|
||||||
void DialogImportShow();
|
void DialogImportShow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show Preferences dialog
|
* @brief Show Preferences dialog
|
||||||
*/
|
*/
|
||||||
void DialogPreferencesShow();
|
void DialogPreferencesShow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show Project Properties dialog
|
* @brief Show Project Properties dialog
|
||||||
*/
|
*/
|
||||||
void DialogProjectPropertiesShow();
|
void DialogProjectPropertiesShow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show Export dialog
|
* @brief Show Export dialog
|
||||||
*/
|
*/
|
||||||
void DialogExportShow();
|
void DialogExportShow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show OTIO import dialog
|
* @brief Show OTIO import dialog
|
||||||
*/
|
*/
|
||||||
#ifdef USE_OTIO
|
#ifdef USE_OTIO
|
||||||
bool DialogImportOTIOShow(const QList<Sequence*>& sequences);
|
bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new folder in the currently active project
|
* @brief Create a new folder in the currently active project
|
||||||
*/
|
*/
|
||||||
void CreateNewFolder();
|
void CreateNewFolder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new sequence in the currently active project
|
* @brief Create a new sequence in the currently active project
|
||||||
*/
|
*/
|
||||||
void CreateNewSequence();
|
void CreateNewSequence();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the currently selected object that the add tool should make
|
* @brief Set the currently selected object that the add tool should make
|
||||||
*/
|
*/
|
||||||
void SetSelectedAddableObject(const Tool::AddableObject& obj);
|
void SetSelectedAddableObject(const Tool::AddableObject &obj);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the currently selected object that the add tool should make
|
* @brief Set the currently selected object that the add tool should make
|
||||||
*/
|
*/
|
||||||
void SetSelectedTransitionObject(const QString& obj);
|
void SetSelectedTransitionObject(const QString &obj);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clears the list of recently opened/saved projects
|
* @brief Clears the list of recently opened/saved projects
|
||||||
*/
|
*/
|
||||||
void ClearOpenRecentList();
|
void ClearOpenRecentList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new empty project and opens it
|
* @brief Creates a new empty project and opens it
|
||||||
*/
|
*/
|
||||||
void CreateNewProject();
|
void CreateNewProject();
|
||||||
|
|
||||||
void CheckForAutoRecoveries();
|
void CheckForAutoRecoveries();
|
||||||
|
|
||||||
void BrowseAutoRecoveries();
|
void BrowseAutoRecoveries();
|
||||||
|
|
||||||
void RequestPixelSamplingInViewers(bool e);
|
void RequestPixelSamplingInViewers(bool e);
|
||||||
|
|
||||||
void WarnCacheFull();
|
void WarnCacheFull();
|
||||||
|
|
||||||
void SetMagic(bool e)
|
void SetMagic(bool e)
|
||||||
{
|
{
|
||||||
magic_ = e;
|
magic_ = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* @brief Signal emitted when the tool is changed from somewhere
|
* @brief Signal emitted when the tool is changed from somewhere
|
||||||
*/
|
*/
|
||||||
void ToolChanged(const Tool::Item& tool);
|
void ToolChanged(const Tool::Item &tool);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signal emitted when addable object changes through SetSelectedAddableObject
|
* @brief Signal emitted when addable object changes through SetSelectedAddableObject
|
||||||
*/
|
*/
|
||||||
void AddableObjectChanged(Tool::AddableObject o);
|
void AddableObjectChanged(Tool::AddableObject o);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signal emitted when the snapping setting is changed
|
* @brief Signal emitted when the snapping setting is changed
|
||||||
*/
|
*/
|
||||||
void SnappingChanged(const bool& b);
|
void SnappingChanged(const bool &b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signal emitted when the default timecode display mode changed
|
* @brief Signal emitted when the default timecode display mode changed
|
||||||
*/
|
*/
|
||||||
void TimecodeDisplayChanged(Timecode::Display d);
|
void TimecodeDisplayChanged(Timecode::Display d);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signal emitted when a change is made to the open recent list
|
* @brief Signal emitted when a change is made to the open recent list
|
||||||
*/
|
*/
|
||||||
void OpenRecentListChanged();
|
void OpenRecentListChanged();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable mouse color sampling functionality on all viewers
|
* @brief Enable mouse color sampling functionality on all viewers
|
||||||
*
|
*
|
||||||
* This can be slow, so we only turn it on when we need it.
|
* This can be slow, so we only turn it on when we need it.
|
||||||
*/
|
*/
|
||||||
void ColorPickerEnabled(bool e);
|
void ColorPickerEnabled(bool e);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A viewer with color picked enabled has emitted a color
|
* @brief A viewer with color picked enabled has emitted a color
|
||||||
*/
|
*/
|
||||||
void ColorPickerColorEmitted(const Color &reference, const Color &display);
|
void ColorPickerColorEmitted(const Color &reference, const Color &display);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
||||||
*/
|
*/
|
||||||
static QString GetProjectFilter(bool include_any_filter);
|
static QString GetProjectFilter(bool include_any_filter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the filename where the recently opened/saved projects should be stored
|
* @brief Returns the filename where the recently opened/saved projects should be stored
|
||||||
*/
|
*/
|
||||||
static QString GetRecentProjectsFilePath();
|
static QString GetRecentProjectsFilePath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called only on startup to set the locale
|
* @brief Called only on startup to set the locale
|
||||||
*/
|
*/
|
||||||
void SetStartupLocale();
|
void SetStartupLocale();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
|
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
|
||||||
*/
|
*/
|
||||||
void PushRecentlyOpenedProject(const QString &s);
|
void PushRecentlyOpenedProject(const QString &s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Declare custom types/classes for Qt's signal/slot system
|
* @brief Declare custom types/classes for Qt's signal/slot system
|
||||||
*
|
*
|
||||||
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
|
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
|
||||||
* them all in a function here.
|
* them all in a function here.
|
||||||
*/
|
*/
|
||||||
void DeclareTypesForQt();
|
void DeclareTypesForQt();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start GUI portion of Olive
|
* @brief Start GUI portion of Olive
|
||||||
*
|
*
|
||||||
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
|
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
|
||||||
* create an application instance that is completely valid minus the UI (e.g. for CLI modes).
|
* create an application instance that is completely valid minus the UI (e.g. for CLI modes).
|
||||||
*/
|
*/
|
||||||
void StartGUI(bool full_screen);
|
void StartGUI(bool full_screen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal function for saving a project to a file
|
* @brief Internal function for saving a project to a file
|
||||||
*/
|
*/
|
||||||
void SaveProjectInternal(const QString &override_filename = QString());
|
void SaveProjectInternal(const QString &override_filename = QString());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the currently most active sequence for exporting
|
* @brief Retrieves the currently most active sequence for exporting
|
||||||
*/
|
*/
|
||||||
ViewerOutput *GetSequenceToExport();
|
ViewerOutput *GetSequenceToExport();
|
||||||
|
|
||||||
static QString GetAutoRecoveryIndexFilename();
|
static QString GetAutoRecoveryIndexFilename();
|
||||||
|
|
||||||
void SaveUnrecoveredList();
|
void SaveUnrecoveredList();
|
||||||
|
|
||||||
bool RevertProjectInternal(bool by_opening_existing);
|
bool RevertProjectInternal(bool by_opening_existing);
|
||||||
|
|
||||||
void SaveRecentProjectsList();
|
void SaveRecentProjectsList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds a project to the "open projects" list
|
* @brief Adds a project to the "open projects" list
|
||||||
*/
|
*/
|
||||||
void AddOpenProject(olive::Project* p, bool add_to_recents = false);
|
void AddOpenProject(olive::Project *p, bool add_to_recents = false);
|
||||||
|
|
||||||
bool AddOpenProjectFromTask(Task* task, bool add_to_recents);
|
bool AddOpenProjectFromTask(Task *task, bool add_to_recents);
|
||||||
|
|
||||||
void SetActiveProject(Project *p);
|
void SetActiveProject(Project *p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal main window object
|
* @brief Internal main window object
|
||||||
*/
|
*/
|
||||||
MainWindow* main_window_;
|
MainWindow *main_window_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List of currently open projects
|
* @brief List of currently open projects
|
||||||
*/
|
*/
|
||||||
Project *open_project_;
|
Project *open_project_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Currently active tool
|
* @brief Currently active tool
|
||||||
*/
|
*/
|
||||||
Tool::Item tool_;
|
Tool::Item tool_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Currently active addable object
|
* @brief Currently active addable object
|
||||||
*/
|
*/
|
||||||
Tool::AddableObject addable_object_;
|
Tool::AddableObject addable_object_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Currently selected transition
|
* @brief Currently selected transition
|
||||||
*/
|
*/
|
||||||
QString selected_transition_;
|
QString selected_transition_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Current snapping setting
|
* @brief Current snapping setting
|
||||||
*/
|
*/
|
||||||
bool snapping_;
|
bool snapping_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal timer for saving autorecovery files
|
* @brief Internal timer for saving autorecovery files
|
||||||
*/
|
*/
|
||||||
QTimer autorecovery_timer_;
|
QTimer autorecovery_timer_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Application-wide undo stack instance
|
* @brief Application-wide undo stack instance
|
||||||
*/
|
*/
|
||||||
UndoStack undo_stack_;
|
UndoStack undo_stack_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List of most recently opened/saved projects
|
* @brief List of most recently opened/saved projects
|
||||||
*/
|
*/
|
||||||
QStringList recent_projects_;
|
QStringList recent_projects_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parameters set up in main() determining how the program should run
|
* @brief Parameters set up in main() determining how the program should run
|
||||||
*/
|
*/
|
||||||
CoreParams core_params_;
|
CoreParams core_params_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Static singleton core instance
|
* @brief Static singleton core instance
|
||||||
*/
|
*/
|
||||||
static Core* instance_;
|
static Core *instance_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal translator
|
* @brief Internal translator
|
||||||
*/
|
*/
|
||||||
QTranslator* translator_;
|
QTranslator *translator_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List of projects that are unsaved but have autorecovery projects
|
* @brief List of projects that are unsaved but have autorecovery projects
|
||||||
*/
|
*/
|
||||||
QVector<QUuid> autorecovered_projects_;
|
QVector<QUuid> autorecovered_projects_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Do something debug related
|
* @brief Do something debug related
|
||||||
*/
|
*/
|
||||||
bool magic_;
|
bool magic_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief How many widgets currently need pixel sampling access
|
* @brief How many widgets currently need pixel sampling access
|
||||||
*/
|
*/
|
||||||
int pixel_sampling_users_;
|
int pixel_sampling_users_;
|
||||||
|
|
||||||
bool shown_cache_full_warning_;
|
bool shown_cache_full_warning_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void SaveAutorecovery();
|
void SaveAutorecovery();
|
||||||
|
|
||||||
void ProjectSaveSucceeded(Task *task);
|
void ProjectSaveSucceeded(Task *task);
|
||||||
|
|
||||||
bool AddOpenProjectFromTaskAndAddToRecents(Task* task)
|
bool AddOpenProjectFromTaskAndAddToRecents(Task *task)
|
||||||
{
|
{
|
||||||
return AddOpenProjectFromTask(task, true);
|
return AddOpenProjectFromTask(task, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportTaskComplete(Task *task);
|
void ImportTaskComplete(Task *task);
|
||||||
|
|
||||||
bool ConfirmImageSequence(const QString &filename);
|
bool ConfirmImageSequence(const QString &filename);
|
||||||
|
|
||||||
void ProjectWasModified(bool e);
|
void ProjectWasModified(bool e);
|
||||||
|
|
||||||
bool StartHeadlessExport();
|
bool StartHeadlessExport();
|
||||||
|
|
||||||
void OpenStartupProject();
|
void OpenStartupProject();
|
||||||
|
|
||||||
void AddRecoveryProjectFromTask(Task* task);
|
void AddRecoveryProjectFromTask(Task *task);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal project open
|
* @brief Internal project open
|
||||||
*/
|
*/
|
||||||
void OpenProjectInternal(const QString& filename, bool recovery_project = false);
|
void OpenProjectInternal(const QString &filename,
|
||||||
|
bool recovery_project = false);
|
||||||
void ImportSingleFile(const QString &f);
|
|
||||||
|
|
||||||
|
void ImportSingleFile(const QString &f);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+259
-224
@@ -40,335 +40,370 @@
|
|||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
|
|
||||||
{
|
{
|
||||||
setWindowTitle(tr("Olive"));
|
|
||||||
setWindowFlags(Qt::WindowStaysOnTopHint);
|
|
||||||
|
|
||||||
report_filename_ = report_path;
|
CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
|
||||||
waiting_for_upload_ = false;
|
{
|
||||||
|
setWindowTitle(tr("Olive"));
|
||||||
|
setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
report_filename_ = report_path;
|
||||||
|
waiting_for_upload_ = false;
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by "
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
"sending an error report.")));
|
|
||||||
|
|
||||||
QSplitter* splitter = new QSplitter(Qt::Vertical);
|
layout->addWidget(new QLabel(
|
||||||
splitter->setChildrenCollapsible(false);
|
tr("We're sorry, Olive has crashed. Please help us fix it by "
|
||||||
layout->addWidget(splitter);
|
"sending an error report.")));
|
||||||
|
|
||||||
summary_edit_ = new QTextEdit();
|
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
||||||
summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as "
|
splitter->setChildrenCollapsible(false);
|
||||||
"possible. If you can, provide steps to reproduce this crash."));
|
layout->addWidget(splitter);
|
||||||
|
|
||||||
splitter->addWidget(summary_edit_);
|
summary_edit_ = new QTextEdit();
|
||||||
|
summary_edit_->setPlaceholderText(
|
||||||
|
tr("Describe what you were doing in as much detail as "
|
||||||
|
"possible. If you can, provide steps to reproduce this crash."));
|
||||||
|
|
||||||
QWidget* crash_widget = new QWidget();
|
splitter->addWidget(summary_edit_);
|
||||||
QVBoxLayout* crash_widget_layout = new QVBoxLayout(crash_widget);
|
|
||||||
crash_widget_layout->setMargin(0);
|
|
||||||
|
|
||||||
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
|
QWidget *crash_widget = new QWidget();
|
||||||
|
QVBoxLayout *crash_widget_layout = new QVBoxLayout(crash_widget);
|
||||||
|
crash_widget_layout->setMargin(0);
|
||||||
|
|
||||||
crash_report_ = new QTextEdit();
|
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
|
||||||
crash_report_->setReadOnly(true);
|
|
||||||
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
|
||||||
crash_widget_layout->addWidget(crash_report_);
|
|
||||||
|
|
||||||
splitter->addWidget(crash_widget);
|
crash_report_ = new QTextEdit();
|
||||||
|
crash_report_->setReadOnly(true);
|
||||||
|
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||||
|
crash_widget_layout->addWidget(crash_report_);
|
||||||
|
|
||||||
QHBoxLayout* btn_layout = new QHBoxLayout();
|
splitter->addWidget(crash_widget);
|
||||||
btn_layout->setMargin(0);
|
|
||||||
btn_layout->addStretch();
|
|
||||||
|
|
||||||
send_report_btn_ = new QPushButton(tr("Send Error Report"));
|
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||||
connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport);
|
btn_layout->setMargin(0);
|
||||||
btn_layout->addWidget(send_report_btn_);
|
btn_layout->addStretch();
|
||||||
|
|
||||||
dont_send_btn_ = new QPushButton(tr("Don't Send"));
|
send_report_btn_ = new QPushButton(tr("Send Error Report"));
|
||||||
connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject);
|
connect(send_report_btn_, &QPushButton::clicked, this,
|
||||||
btn_layout->addWidget(dont_send_btn_);
|
&CrashHandlerDialog::SendErrorReport);
|
||||||
|
btn_layout->addWidget(send_report_btn_);
|
||||||
|
|
||||||
layout->addLayout(btn_layout);
|
dont_send_btn_ = new QPushButton(tr("Don't Send"));
|
||||||
|
connect(dont_send_btn_, &QPushButton::clicked, this,
|
||||||
|
&CrashHandlerDialog::reject);
|
||||||
|
btn_layout->addWidget(dont_send_btn_);
|
||||||
|
|
||||||
crash_report_->setEnabled(false);
|
layout->addLayout(btn_layout);
|
||||||
send_report_btn_->setEnabled(false);
|
|
||||||
|
|
||||||
crash_report_->setText(tr("Waiting for crash report to be generated..."));
|
crash_report_->setEnabled(false);
|
||||||
|
send_report_btn_->setEnabled(false);
|
||||||
|
|
||||||
GenerateReport();
|
crash_report_->setText(tr("Waiting for crash report to be generated..."));
|
||||||
|
|
||||||
|
GenerateReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::SetGUIObjectsEnabled(bool e)
|
void CrashHandlerDialog::SetGUIObjectsEnabled(bool e)
|
||||||
{
|
{
|
||||||
summary_edit_->setEnabled(e);
|
summary_edit_->setEnabled(e);
|
||||||
crash_report_->setEnabled(e);
|
crash_report_->setEnabled(e);
|
||||||
send_report_btn_->setEnabled(e);
|
send_report_btn_->setEnabled(e);
|
||||||
dont_send_btn_->setEnabled(e);
|
dont_send_btn_->setEnabled(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CrashHandlerDialog::GetSymbolPath()
|
QString CrashHandlerDialog::GetSymbolPath()
|
||||||
{
|
{
|
||||||
QDir app_path(qApp->applicationDirPath());
|
QDir app_path(qApp->applicationDirPath());
|
||||||
QString symbols_path;
|
QString symbols_path;
|
||||||
|
|
||||||
#if BUILDFLAG(IS_WIN)
|
#if BUILDFLAG(IS_WIN)
|
||||||
symbols_path = app_path.filePath(QStringLiteral("symbols"));
|
symbols_path = app_path.filePath(QStringLiteral("symbols"));
|
||||||
#elif BUILDFLAG(IS_LINUX)
|
#elif BUILDFLAG(IS_LINUX)
|
||||||
app_path.cdUp();
|
app_path.cdUp();
|
||||||
symbols_path = app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
|
symbols_path =
|
||||||
|
app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
|
||||||
#elif BUILDFLAG(IS_APPLE)
|
#elif BUILDFLAG(IS_APPLE)
|
||||||
app_path.cdUp();
|
app_path.cdUp();
|
||||||
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
|
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return symbols_path;
|
return symbols_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::GenerateReport()
|
void CrashHandlerDialog::GenerateReport()
|
||||||
{
|
{
|
||||||
QProcess* p = new QProcess();
|
QProcess *p = new QProcess();
|
||||||
|
|
||||||
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||||
this, &CrashHandlerDialog::ReadProcessFinished);
|
this, &CrashHandlerDialog::ReadProcessFinished);
|
||||||
connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData);
|
connect(p, &QProcess::readyReadStandardOutput, this,
|
||||||
|
&CrashHandlerDialog::ReadProcessHasData);
|
||||||
|
|
||||||
QString stackwalk_filename = FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("minidump_stackwalk"));
|
QString stackwalk_filename =
|
||||||
|
FileFunctions::GetFormattedExecutableForPlatform(
|
||||||
|
QStringLiteral("minidump_stackwalk"));
|
||||||
|
|
||||||
QString stackwalk_bin = QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
|
QString stackwalk_bin =
|
||||||
p->start(stackwalk_bin, {report_filename_, GetSymbolPath()});
|
QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
|
||||||
crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
|
p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
|
||||||
|
crash_report_->setText(
|
||||||
|
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
|
void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
|
||||||
{
|
{
|
||||||
waiting_for_upload_ = false;
|
waiting_for_upload_ = false;
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
// Close dialog
|
// Close dialog
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
} else {
|
} else {
|
||||||
QMessageBox b(this);
|
QMessageBox b(this);
|
||||||
b.setIcon(QMessageBox::Critical);
|
b.setIcon(QMessageBox::Critical);
|
||||||
b.setWindowModality(Qt::WindowModal);
|
b.setWindowModality(Qt::WindowModal);
|
||||||
b.setWindowTitle(tr("Upload Failed"));
|
b.setWindowTitle(tr("Upload Failed"));
|
||||||
b.setText(tr("Failed to send error report (%1). Please try again later.").arg(QString::number(reply->error())));
|
b.setText(
|
||||||
b.addButton(QMessageBox::Ok);
|
tr("Failed to send error report (%1). Please try again later.")
|
||||||
b.exec();
|
.arg(QString::number(reply->error())));
|
||||||
|
b.addButton(QMessageBox::Ok);
|
||||||
|
b.exec();
|
||||||
|
|
||||||
SetGUIObjectsEnabled(true);
|
SetGUIObjectsEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &se)
|
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply,
|
||||||
|
const QList<QSslError> &se)
|
||||||
{
|
{
|
||||||
QStringList errors;
|
QStringList errors;
|
||||||
for (const QSslError &err : se) {
|
for (const QSslError &err : se) {
|
||||||
errors.append(err.errorString());
|
errors.append(err.errorString());
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox b(this);
|
QMessageBox b(this);
|
||||||
b.setIcon(QMessageBox::Critical);
|
b.setIcon(QMessageBox::Critical);
|
||||||
b.setWindowModality(Qt::WindowModal);
|
b.setWindowModality(Qt::WindowModal);
|
||||||
b.setWindowTitle(tr("SSL Error"));
|
b.setWindowTitle(tr("SSL Error"));
|
||||||
b.setText(tr("Encountered the following SSL errors:\n\n%1").arg(errors.join('\n')));
|
b.setText(tr("Encountered the following SSL errors:\n\n%1")
|
||||||
b.addButton(QMessageBox::Ok);
|
.arg(errors.join('\n')));
|
||||||
b.exec();
|
b.addButton(QMessageBox::Ok);
|
||||||
|
b.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::AttemptToFindReport()
|
void CrashHandlerDialog::AttemptToFindReport()
|
||||||
{
|
{
|
||||||
// If we found it, use it, otherwise wait a second and try again
|
// If we found it, use it, otherwise wait a second and try again
|
||||||
if (report_filename_.isEmpty()) {
|
if (report_filename_.isEmpty()) {
|
||||||
// Couldn't find report, try again in one second
|
// Couldn't find report, try again in one second
|
||||||
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
|
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
|
||||||
} else {
|
} else {
|
||||||
GenerateReport();
|
GenerateReport();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::ReadProcessHasData()
|
void CrashHandlerDialog::ReadProcessHasData()
|
||||||
{
|
{
|
||||||
report_data_.append(static_cast<QProcess*>(sender())->readAllStandardOutput());
|
report_data_.append(
|
||||||
|
static_cast<QProcess *>(sender())->readAllStandardOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::ReadProcessFinished()
|
void CrashHandlerDialog::ReadProcessFinished()
|
||||||
{
|
{
|
||||||
SetGUIObjectsEnabled(true);
|
SetGUIObjectsEnabled(true);
|
||||||
crash_report_->setText(report_data_);
|
crash_report_->setText(report_data_);
|
||||||
delete sender();
|
delete sender();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::SendErrorReport()
|
void CrashHandlerDialog::SendErrorReport()
|
||||||
{
|
{
|
||||||
if (summary_edit_->document()->isEmpty()) {
|
if (summary_edit_->document()->isEmpty()) {
|
||||||
QMessageBox b(this);
|
QMessageBox b(this);
|
||||||
b.setIcon(QMessageBox::Question);
|
b.setIcon(QMessageBox::Question);
|
||||||
b.setWindowModality(Qt::WindowModal);
|
b.setWindowModality(Qt::WindowModal);
|
||||||
b.setText(tr("You must write a description to submit this crash report."));
|
b.setText(
|
||||||
b.addButton(QMessageBox::Ok);
|
tr("You must write a description to submit this crash report."));
|
||||||
b.exec();
|
b.addButton(QMessageBox::Ok);
|
||||||
return;
|
b.exec();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkAccessManager* manager = new QNetworkAccessManager();
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
connect(manager, &QNetworkAccessManager::finished, this, &CrashHandlerDialog::ReplyFinished);
|
connect(manager, &QNetworkAccessManager::finished, this,
|
||||||
connect(manager, &QNetworkAccessManager::sslErrors, this, &CrashHandlerDialog::HandleSslErrors);
|
&CrashHandlerDialog::ReplyFinished);
|
||||||
|
connect(manager, &QNetworkAccessManager::sslErrors, this,
|
||||||
|
&CrashHandlerDialog::HandleSslErrors);
|
||||||
|
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
||||||
request.setUrl(QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
|
request.setUrl(
|
||||||
|
QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
|
||||||
|
|
||||||
// Create HTTP form
|
// Create HTTP form
|
||||||
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
QHttpMultiPart *multipart =
|
||||||
|
new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||||
|
|
||||||
// Create description section
|
// Create description section
|
||||||
QHttpPart desc_part;
|
QHttpPart desc_part;
|
||||||
desc_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
|
desc_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||||
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"description\""));
|
QStringLiteral("text/plain; charset=UTF-8"));
|
||||||
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
|
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||||
multipart->append(desc_part);
|
QStringLiteral("form-data; name=\"description\""));
|
||||||
|
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
|
||||||
|
multipart->append(desc_part);
|
||||||
|
|
||||||
// Create report section
|
// Create report section
|
||||||
QHttpPart report_part;
|
QHttpPart report_part;
|
||||||
report_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
|
report_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||||
report_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"report\""));
|
QStringLiteral("text/plain; charset=UTF-8"));
|
||||||
report_part.setBody(report_data_);
|
report_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||||
multipart->append(report_part);
|
QStringLiteral("form-data; name=\"report\""));
|
||||||
|
report_part.setBody(report_data_);
|
||||||
|
multipart->append(report_part);
|
||||||
|
|
||||||
// Create commit section
|
// Create commit section
|
||||||
QHttpPart commit_part;
|
QHttpPart commit_part;
|
||||||
commit_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
|
commit_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||||
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"commit\""));
|
QStringLiteral("text/plain; charset=UTF-8"));
|
||||||
commit_part.setBody(kAppVersionLong.toUtf8());
|
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||||
multipart->append(commit_part);
|
QStringLiteral("form-data; name=\"commit\""));
|
||||||
|
commit_part.setBody(kAppVersionLong.toUtf8());
|
||||||
|
multipart->append(commit_part);
|
||||||
|
|
||||||
// Create dump section
|
// Create dump section
|
||||||
QHttpPart dump_part;
|
QHttpPart dump_part;
|
||||||
dump_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream"));
|
dump_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||||
dump_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
|
QStringLiteral("application/octet-stream"));
|
||||||
.arg(QFileInfo(report_filename_).fileName()));
|
dump_part.setHeader(
|
||||||
QFile* dump_file = new QFile(report_filename_);
|
QNetworkRequest::ContentDispositionHeader,
|
||||||
dump_file->open(QFile::ReadOnly);
|
QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
|
||||||
dump_part.setBodyDevice(dump_file);
|
.arg(QFileInfo(report_filename_).fileName()));
|
||||||
dump_file->setParent(multipart); // Delete file with multipart
|
QFile *dump_file = new QFile(report_filename_);
|
||||||
multipart->append(dump_part);
|
dump_file->open(QFile::ReadOnly);
|
||||||
|
dump_part.setBodyDevice(dump_file);
|
||||||
|
dump_file->setParent(multipart); // Delete file with multipart
|
||||||
|
multipart->append(dump_part);
|
||||||
|
|
||||||
// Find symbol file
|
// Find symbol file
|
||||||
QDir symbol_dir(GetSymbolPath());
|
QDir symbol_dir(GetSymbolPath());
|
||||||
|
|
||||||
QString symbol_bin_name;
|
QString symbol_bin_name;
|
||||||
#if BUILDFLAG(IS_WIN)
|
#if BUILDFLAG(IS_WIN)
|
||||||
symbol_bin_name = QStringLiteral("olive-editor.pdb");
|
symbol_bin_name = QStringLiteral("olive-editor.pdb");
|
||||||
#elif BUILDFLAG(IS_APPLE)
|
#elif BUILDFLAG(IS_APPLE)
|
||||||
symbol_bin_name = QStringLiteral("Olive");
|
symbol_bin_name = QStringLiteral("Olive");
|
||||||
#else
|
#else
|
||||||
symbol_bin_name = QStringLiteral("olive-editor");
|
symbol_bin_name = QStringLiteral("olive-editor");
|
||||||
#endif
|
#endif
|
||||||
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
|
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
|
||||||
|
|
||||||
QStringList folders_in_symbol_path = symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
QStringList folders_in_symbol_path =
|
||||||
|
symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
if (folders_in_symbol_path.size() > 0) {
|
if (folders_in_symbol_path.size() > 0) {
|
||||||
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
|
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
|
||||||
} else {
|
} else {
|
||||||
QMessageBox b(this);
|
QMessageBox b(this);
|
||||||
b.setIcon(QMessageBox::Critical);
|
b.setIcon(QMessageBox::Critical);
|
||||||
b.setWindowModality(Qt::WindowModal);
|
b.setWindowModality(Qt::WindowModal);
|
||||||
b.setWindowTitle(tr("Failed to send report"));
|
b.setWindowTitle(tr("Failed to send report"));
|
||||||
b.setText(tr("Failed to find symbols necessary to send report. "
|
b.setText(tr("Failed to find symbols necessary to send report. "
|
||||||
"This is a packaging issue. Please notify "
|
"This is a packaging issue. Please notify "
|
||||||
"the maintainers of this package."));
|
"the maintainers of this package."));
|
||||||
b.addButton(QMessageBox::Ok);
|
b.addButton(QMessageBox::Ok);
|
||||||
b.exec();
|
b.exec();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create sym section
|
// Create sym section
|
||||||
QString symbol_filename;
|
QString symbol_filename;
|
||||||
#if BUILDFLAG(IS_APPLE)
|
#if BUILDFLAG(IS_APPLE)
|
||||||
symbol_filename = QStringLiteral("Olive.sym");
|
symbol_filename = QStringLiteral("Olive.sym");
|
||||||
#else
|
#else
|
||||||
symbol_filename = QStringLiteral("olive-editor.sym");
|
symbol_filename = QStringLiteral("olive-editor.sym");
|
||||||
#endif
|
#endif
|
||||||
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
|
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
|
||||||
QHttpPart sym_part;
|
QHttpPart sym_part;
|
||||||
sym_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream"));
|
sym_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||||
sym_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
|
QStringLiteral("application/octet-stream"));
|
||||||
.arg(symbol_filename));
|
sym_part.setHeader(
|
||||||
QFile sym_file(symbol_full_path);
|
QNetworkRequest::ContentDispositionHeader,
|
||||||
|
QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
|
||||||
|
.arg(symbol_filename));
|
||||||
|
QFile sym_file(symbol_full_path);
|
||||||
|
|
||||||
if (!sym_file.open(QFile::ReadOnly)) {
|
if (!sym_file.open(QFile::ReadOnly)) {
|
||||||
QMessageBox b(this);
|
QMessageBox b(this);
|
||||||
b.setIcon(QMessageBox::Critical);
|
b.setIcon(QMessageBox::Critical);
|
||||||
b.setWindowModality(Qt::WindowModal);
|
b.setWindowModality(Qt::WindowModal);
|
||||||
b.setWindowTitle(tr("Failed to send report"));
|
b.setWindowTitle(tr("Failed to send report"));
|
||||||
b.setText(tr("Failed to open symbol file. You may not have "
|
b.setText(tr("Failed to open symbol file. You may not have "
|
||||||
"permission to access it."));
|
"permission to access it."));
|
||||||
b.addButton(QMessageBox::Ok);
|
b.addButton(QMessageBox::Ok);
|
||||||
b.exec();
|
b.exec();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
|
QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
|
||||||
sym_file.close();
|
sym_file.close();
|
||||||
|
|
||||||
sym_part.setBody(symbol_data);
|
sym_part.setBody(symbol_data);
|
||||||
multipart->append(sym_part);
|
multipart->append(sym_part);
|
||||||
|
|
||||||
manager->post(request, multipart);
|
manager->post(request, multipart);
|
||||||
|
|
||||||
SetGUIObjectsEnabled(false);
|
SetGUIObjectsEnabled(false);
|
||||||
waiting_for_upload_ = true;
|
waiting_for_upload_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::closeEvent(QCloseEvent* e)
|
void CrashHandlerDialog::closeEvent(QCloseEvent *e)
|
||||||
{
|
{
|
||||||
QMessageBox b(this);
|
QMessageBox b(this);
|
||||||
b.setIcon(QMessageBox::Warning);
|
b.setIcon(QMessageBox::Warning);
|
||||||
b.setWindowModality(Qt::WindowModal);
|
b.setWindowModality(Qt::WindowModal);
|
||||||
b.setWindowTitle(tr("Confirm Close"));
|
b.setWindowTitle(tr("Confirm Close"));
|
||||||
b.setText(tr("Crash report is still uploading. Closing now may result in no "
|
b.setText(
|
||||||
"report being sent. Are you sure you wish to close?"));
|
tr("Crash report is still uploading. Closing now may result in no "
|
||||||
b.addButton(QMessageBox::Ok);
|
"report being sent. Are you sure you wish to close?"));
|
||||||
b.addButton(QMessageBox::Cancel);
|
b.addButton(QMessageBox::Ok);
|
||||||
|
b.addButton(QMessageBox::Cancel);
|
||||||
|
|
||||||
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
|
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
|
||||||
e->ignore();
|
e->ignore();
|
||||||
} else {
|
} else {
|
||||||
e->accept();
|
e->accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QString report;
|
QString report;
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
int num_args;
|
int num_args;
|
||||||
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
|
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
|
||||||
if (num_args < 2) {
|
if (num_args < 2) {
|
||||||
LocalFree(args);
|
LocalFree(args);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
report = QString::fromWCharArray(args[1]);
|
report = QString::fromWCharArray(args[1]);
|
||||||
LocalFree(args);
|
LocalFree(args);
|
||||||
#else
|
#else
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
report = argv[1];
|
report = argv[1];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
olive::CrashHandlerDialog chd(report);
|
olive::CrashHandlerDialog chd(report);
|
||||||
chd.open();
|
chd.open();
|
||||||
|
|
||||||
return a.exec();
|
return a.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,51 +30,50 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CrashHandlerDialog : public QDialog
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class CrashHandlerDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CrashHandlerDialog(const QString& report_path);
|
CrashHandlerDialog(const QString &report_path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetGUIObjectsEnabled(bool e);
|
void SetGUIObjectsEnabled(bool e);
|
||||||
|
|
||||||
void GenerateReport();
|
void GenerateReport();
|
||||||
|
|
||||||
static QString GetSymbolPath();
|
static QString GetSymbolPath();
|
||||||
|
|
||||||
QTextEdit* summary_edit_;
|
QTextEdit *summary_edit_;
|
||||||
|
|
||||||
QTextEdit* crash_report_;
|
QTextEdit *crash_report_;
|
||||||
|
|
||||||
QPushButton* send_report_btn_;
|
QPushButton *send_report_btn_;
|
||||||
|
|
||||||
QPushButton* dont_send_btn_;
|
QPushButton *dont_send_btn_;
|
||||||
|
|
||||||
QString report_filename_;
|
QString report_filename_;
|
||||||
|
|
||||||
QByteArray report_data_;
|
QByteArray report_data_;
|
||||||
|
|
||||||
bool waiting_for_upload_;
|
bool waiting_for_upload_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void closeEvent(QCloseEvent* e) override;
|
virtual void closeEvent(QCloseEvent *e) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ReplyFinished(QNetworkReply *reply);
|
void ReplyFinished(QNetworkReply *reply);
|
||||||
|
|
||||||
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||||
|
|
||||||
void AttemptToFindReport();
|
void AttemptToFindReport();
|
||||||
|
|
||||||
void ReadProcessHasData();
|
void ReadProcessHasData();
|
||||||
|
|
||||||
void ReadProcessFinished();
|
void ReadProcessFinished();
|
||||||
|
|
||||||
void SendErrorReport();
|
|
||||||
|
|
||||||
|
void SendErrorReport();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+96
-83
@@ -30,114 +30,127 @@
|
|||||||
#include "patreon.h"
|
#include "patreon.h"
|
||||||
#include "scrollinglabel.h"
|
#include "scrollinglabel.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
|
||||||
QDialog(parent)
|
|
||||||
{
|
{
|
||||||
if (welcome_dialog) {
|
|
||||||
setWindowTitle(tr("Welcome to %1").arg(QApplication::applicationName()));
|
|
||||||
} else {
|
|
||||||
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
QFontMetrics fm = fontMetrics();
|
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
{
|
||||||
|
if (welcome_dialog) {
|
||||||
|
setWindowTitle(
|
||||||
|
tr("Welcome to %1").arg(QApplication::applicationName()));
|
||||||
|
} else {
|
||||||
|
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
|
||||||
|
}
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
QFontMetrics fm = fontMetrics();
|
||||||
layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
|
|
||||||
|
|
||||||
QHBoxLayout *horiz_layout = new QHBoxLayout();
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
|
layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||||
horiz_layout->setSpacing(fm.height()*2);
|
fm.height());
|
||||||
|
|
||||||
QLabel* icon = new QLabel(QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
|
QHBoxLayout *horiz_layout = new QHBoxLayout();
|
||||||
icon->setAlignment(Qt::AlignCenter);
|
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||||
horiz_layout->addWidget(icon);
|
fm.height());
|
||||||
|
horiz_layout->setSpacing(fm.height() * 2);
|
||||||
|
|
||||||
// Construct About text
|
QLabel *icon = new QLabel(
|
||||||
QLabel* label =
|
QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
|
||||||
new QLabel(QStringLiteral("<html><head/><body>"
|
icon->setAlignment(Qt::AlignCenter);
|
||||||
"<p><b>%1</b> %2</p>" // AppName (version identifier)
|
horiz_layout->addWidget(icon);
|
||||||
"<p><a href=\"https://www.olivevideoeditor.org/\">"
|
|
||||||
"https://www.olivevideoeditor.org/"
|
|
||||||
"</a></p>"
|
|
||||||
"<p>%3</p>" // First statement
|
|
||||||
"</body></html>").arg(QApplication::applicationName(),
|
|
||||||
QApplication::applicationVersion(),
|
|
||||||
tr("Olive is a free open source non-linear video editor. "
|
|
||||||
"This software is licensed under the GNU GPL Version 3.")));
|
|
||||||
|
|
||||||
// Set text formatting
|
// Construct About text
|
||||||
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
QLabel *label = new QLabel(
|
||||||
label->setWordWrap(true);
|
QStringLiteral("<html><head/><body>"
|
||||||
label->setOpenExternalLinks(true);
|
"<p><b>%1</b> %2</p>" // AppName (version identifier)
|
||||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
"<p><a href=\"https://www.olivevideoeditor.org/\">"
|
||||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
"https://www.olivevideoeditor.org/"
|
||||||
label->setCursor(Qt::IBeamCursor);
|
"</a></p>"
|
||||||
horiz_layout->addWidget(label);
|
"<p>%3</p>" // First statement
|
||||||
|
"</body></html>")
|
||||||
|
.arg(QApplication::applicationName(),
|
||||||
|
QApplication::applicationVersion(),
|
||||||
|
tr("Olive is a free open source non-linear video editor. "
|
||||||
|
"This software is licensed under the GNU GPL Version 3.")));
|
||||||
|
|
||||||
layout->addLayout(horiz_layout);
|
// Set text formatting
|
||||||
|
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
|
label->setWordWrap(true);
|
||||||
|
label->setOpenExternalLinks(true);
|
||||||
|
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
label->setTextInteractionFlags(Qt::TextSelectableByMouse |
|
||||||
|
Qt::LinksAccessibleByMouse);
|
||||||
|
label->setCursor(Qt::IBeamCursor);
|
||||||
|
horiz_layout->addWidget(label);
|
||||||
|
|
||||||
// Patrons where possible
|
layout->addLayout(horiz_layout);
|
||||||
layout->addWidget(new QLabel());
|
|
||||||
|
|
||||||
QString opening_statement;
|
// Patrons where possible
|
||||||
|
layout->addWidget(new QLabel());
|
||||||
|
|
||||||
if (welcome_dialog || patrons.isEmpty()) {
|
QString opening_statement;
|
||||||
opening_statement = tr("<b>Olive relies on support from the community to continue its development.</b>");
|
|
||||||
} else {
|
|
||||||
opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people.");
|
|
||||||
}
|
|
||||||
|
|
||||||
QLabel* support_lbl = new QLabel(tr("<html>%1 "
|
if (welcome_dialog || patrons.isEmpty()) {
|
||||||
"If you like this project, please consider making a "
|
opening_statement = tr(
|
||||||
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
|
"<b>Olive relies on support from the community to continue its development.</b>");
|
||||||
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
|
} else {
|
||||||
"support its development.</html>").arg(opening_statement));
|
opening_statement = tr(
|
||||||
support_lbl->setWordWrap(true);
|
"Olive wouldn't be possible without the support of gracious donations from the following people.");
|
||||||
support_lbl->setAlignment(Qt::AlignCenter);
|
}
|
||||||
support_lbl->setOpenExternalLinks(true);
|
|
||||||
layout->addWidget(support_lbl);
|
|
||||||
|
|
||||||
if (!patrons.isEmpty()) {
|
QLabel *support_lbl = new QLabel(
|
||||||
ScrollingLabel* scroll = new ScrollingLabel(patrons);
|
tr("<html>%1 "
|
||||||
scroll->StartAnimating();
|
"If you like this project, please consider making a "
|
||||||
layout->addWidget(scroll);
|
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
|
||||||
}
|
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
|
||||||
|
"support its development.</html>")
|
||||||
|
.arg(opening_statement));
|
||||||
|
support_lbl->setWordWrap(true);
|
||||||
|
support_lbl->setAlignment(Qt::AlignCenter);
|
||||||
|
support_lbl->setOpenExternalLinks(true);
|
||||||
|
layout->addWidget(support_lbl);
|
||||||
|
|
||||||
layout->addWidget(new QLabel());
|
if (!patrons.isEmpty()) {
|
||||||
|
ScrollingLabel *scroll = new ScrollingLabel(patrons);
|
||||||
|
scroll->StartAnimating();
|
||||||
|
layout->addWidget(scroll);
|
||||||
|
}
|
||||||
|
|
||||||
QHBoxLayout *btn_layout = new QHBoxLayout();
|
layout->addWidget(new QLabel());
|
||||||
btn_layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
btn_layout->setSpacing(0);
|
|
||||||
|
|
||||||
if (welcome_dialog) {
|
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||||
dont_show_again_checkbox_ = new QCheckBox(tr("Don't show this message again"));
|
btn_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
btn_layout->addWidget(dont_show_again_checkbox_);
|
btn_layout->setSpacing(0);
|
||||||
} else {
|
|
||||||
dont_show_again_checkbox_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
if (welcome_dialog) {
|
||||||
if (!welcome_dialog) {
|
dont_show_again_checkbox_ =
|
||||||
buttons->setCenterButtons(true);
|
new QCheckBox(tr("Don't show this message again"));
|
||||||
}
|
btn_layout->addWidget(dont_show_again_checkbox_);
|
||||||
btn_layout->addWidget(buttons);
|
} else {
|
||||||
|
dont_show_again_checkbox_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
layout->addLayout(btn_layout);
|
QDialogButtonBox *buttons =
|
||||||
|
new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
||||||
|
if (!welcome_dialog) {
|
||||||
|
buttons->setCenterButtons(true);
|
||||||
|
}
|
||||||
|
btn_layout->addWidget(buttons);
|
||||||
|
|
||||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
layout->addLayout(btn_layout);
|
||||||
|
|
||||||
setFixedSize(sizeHint());
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
|
||||||
|
setFixedSize(sizeHint());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AboutDialog::accept()
|
void AboutDialog::accept()
|
||||||
{
|
{
|
||||||
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
|
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
|
||||||
OLIVE_CONFIG("ShowWelcomeDialog") = false;
|
OLIVE_CONFIG("ShowWelcomeDialog") = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The AboutDialog class
|
* @brief The AboutDialog class
|
||||||
@@ -34,11 +35,10 @@ namespace olive {
|
|||||||
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
|
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
|
||||||
* anywhere
|
* anywhere
|
||||||
*/
|
*/
|
||||||
class AboutDialog : public QDialog
|
class AboutDialog : public QDialog {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief AboutDialog Constructor
|
* @brief AboutDialog Constructor
|
||||||
*
|
*
|
||||||
* Creates About dialog.
|
* Creates About dialog.
|
||||||
@@ -47,14 +47,13 @@ public:
|
|||||||
*
|
*
|
||||||
* QWidget parent object. Usually this will be MainWindow.
|
* QWidget parent object. Usually this will be MainWindow.
|
||||||
*/
|
*/
|
||||||
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
|
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCheckBox *dont_show_again_checkbox_;
|
QCheckBox *dont_show_again_checkbox_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,98 +24,101 @@
|
|||||||
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
const int ScrollingLabel::kMinLineHeight = 10;
|
const int ScrollingLabel::kMinLineHeight = 10;
|
||||||
|
|
||||||
ScrollingLabel::ScrollingLabel(QWidget *parent) :
|
ScrollingLabel::ScrollingLabel(QWidget *parent)
|
||||||
QWidget(parent),
|
: QWidget(parent)
|
||||||
animate_(0)
|
, animate_(0)
|
||||||
{
|
{
|
||||||
timer_.setInterval(50);
|
timer_.setInterval(50);
|
||||||
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
|
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent) :
|
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent)
|
||||||
ScrollingLabel(parent)
|
: ScrollingLabel(parent)
|
||||||
{
|
{
|
||||||
SetText(text);
|
SetText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollingLabel::SetText(const QStringList &text)
|
void ScrollingLabel::SetText(const QStringList &text)
|
||||||
{
|
{
|
||||||
text_ = text;
|
text_ = text;
|
||||||
|
|
||||||
QFontMetrics fm = fontMetrics();
|
QFontMetrics fm = fontMetrics();
|
||||||
text_height_ = fm.height();
|
text_height_ = fm.height();
|
||||||
|
|
||||||
int width = 0;
|
int width = 0;
|
||||||
foreach (const QString& s, text_) {
|
foreach (const QString &s, text_) {
|
||||||
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
|
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
|
||||||
}
|
}
|
||||||
|
|
||||||
setMinimumSize(width, text_height_ * kMinLineHeight);
|
setMinimumSize(width, text_height_ * kMinLineHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollingLabel::paintEvent(QPaintEvent *e)
|
void ScrollingLabel::paintEvent(QPaintEvent *e)
|
||||||
{
|
{
|
||||||
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
|
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
|
||||||
map.fill(Qt::transparent);
|
map.fill(Qt::transparent);
|
||||||
|
|
||||||
{
|
{
|
||||||
QPainter p(&map);
|
QPainter p(&map);
|
||||||
p.setPen(palette().text().color());
|
p.setPen(palette().text().color());
|
||||||
|
|
||||||
QFontMetrics fm = p.fontMetrics();
|
QFontMetrics fm = p.fontMetrics();
|
||||||
int half_width = width();
|
int half_width = width();
|
||||||
|
|
||||||
for (int i=0; i<text_.size(); i++) {
|
for (int i = 0; i < text_.size(); i++) {
|
||||||
int text_y = fm.ascent() + height() - animate_ + (fm.height()*i);
|
int text_y = fm.ascent() + height() - animate_ + (fm.height() * i);
|
||||||
int text_bottom = text_y + fm.descent();
|
int text_bottom = text_y + fm.descent();
|
||||||
int text_top = text_y - fm.ascent();
|
int text_top = text_y - fm.ascent();
|
||||||
|
|
||||||
if (text_bottom < 0 || text_top >= height()) {
|
if (text_bottom < 0 || text_top >= height()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& s = text_.at(i);
|
const QString &s = text_.at(i);
|
||||||
|
|
||||||
int width = QtUtils::QFontMetricsWidth(fm, s);
|
int width = QtUtils::QFontMetricsWidth(fm, s);
|
||||||
p.drawText(half_width/2 - width/2, text_y, s);
|
p.drawText(half_width / 2 - width / 2, text_y, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y=0; y<text_height_; y++) {
|
for (int y = 0; y < text_height_; y++) {
|
||||||
double mul = double(y)/double(text_height_);
|
double mul = double(y) / double(text_height_);
|
||||||
|
|
||||||
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
|
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
|
||||||
SetOpacityOfScanLine(map.scanLine(map.height()-1-y), map.width(), 4, mul);
|
SetOpacityOfScanLine(map.scanLine(map.height() - 1 - y),
|
||||||
}
|
map.width(), 4, mul);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QPainter wp(this);
|
QPainter wp(this);
|
||||||
wp.drawImage(0, 0, map);
|
wp.drawImage(0, 0, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width, int channels, double mul)
|
void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width,
|
||||||
|
int channels, double mul)
|
||||||
{
|
{
|
||||||
for (int x=0; x<width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
uchar* pixel = &scan_line[x*4];
|
uchar *pixel = &scan_line[x * 4];
|
||||||
|
|
||||||
for (int c=0; c<channels; c++) {
|
for (int c = 0; c < channels; c++) {
|
||||||
pixel[c] *= mul;
|
pixel[c] *= mul;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollingLabel::AnimationUpdate()
|
void ScrollingLabel::AnimationUpdate()
|
||||||
{
|
{
|
||||||
animate_++;
|
animate_++;
|
||||||
|
|
||||||
if (animate_ >= height() + text_.size() * text_height_) {
|
if (animate_ >= height() + text_.size() * text_height_) {
|
||||||
animate_ = 0;
|
animate_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,46 +24,46 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ScrollingLabel : public QWidget
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class ScrollingLabel : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ScrollingLabel(QWidget* parent = nullptr);
|
ScrollingLabel(QWidget *parent = nullptr);
|
||||||
ScrollingLabel(const QStringList& text, QWidget* parent = nullptr);
|
ScrollingLabel(const QStringList &text, QWidget *parent = nullptr);
|
||||||
|
|
||||||
void SetText(const QStringList& text);
|
void SetText(const QStringList &text);
|
||||||
|
|
||||||
void StartAnimating()
|
void StartAnimating()
|
||||||
{
|
{
|
||||||
timer_.start();
|
timer_.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopAnimating()
|
void StopAnimating()
|
||||||
{
|
{
|
||||||
timer_.stop();
|
timer_.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void paintEvent(QPaintEvent* e) override;
|
virtual void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void SetOpacityOfScanLine(uchar* scan_line, int width, int channels, double mul);
|
static void SetOpacityOfScanLine(uchar *scan_line, int width, int channels,
|
||||||
|
double mul);
|
||||||
|
|
||||||
static const int kMinLineHeight;
|
static const int kMinLineHeight;
|
||||||
|
|
||||||
QStringList text_;
|
QStringList text_;
|
||||||
|
|
||||||
int text_height_;
|
int text_height_;
|
||||||
|
|
||||||
QTimer timer_;
|
QTimer timer_;
|
||||||
|
|
||||||
int animate_;
|
int animate_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void AnimationUpdate();
|
void AnimationUpdate();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,247 +25,251 @@
|
|||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
ActionSearch::ActionSearch(QWidget *parent) :
|
|
||||||
QDialog(parent),
|
|
||||||
menu_bar_(nullptr)
|
|
||||||
{
|
{
|
||||||
// ActionSearch requires a parent widget
|
|
||||||
Q_ASSERT(parent != nullptr);
|
|
||||||
|
|
||||||
// Set styling (object name is required for CSS specific to this object)
|
ActionSearch::ActionSearch(QWidget *parent)
|
||||||
setObjectName("ASDiag");
|
: QDialog(parent)
|
||||||
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
|
, menu_bar_(nullptr)
|
||||||
|
{
|
||||||
|
// ActionSearch requires a parent widget
|
||||||
|
Q_ASSERT(parent != nullptr);
|
||||||
|
|
||||||
// Size proportionally to the parent (usually MainWindow).
|
// Set styling (object name is required for CSS specific to this object)
|
||||||
resize(parent->width()/3, parent->height()/3);
|
setObjectName("ASDiag");
|
||||||
|
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
|
||||||
|
|
||||||
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
|
// Size proportionally to the parent (usually MainWindow).
|
||||||
setWindowFlags(Qt::Popup);
|
resize(parent->width() / 3, parent->height() / 3);
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
|
||||||
|
setWindowFlags(Qt::Popup);
|
||||||
|
|
||||||
// Construct the main entry text field.
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
|
|
||||||
|
|
||||||
// Set the main entry field font size to 1.2x its standard font size.
|
// Construct the main entry text field.
|
||||||
QFont entry_field_font = entry_field->font();
|
ActionSearchEntry *entry_field = new ActionSearchEntry(this);
|
||||||
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
|
|
||||||
entry_field->setFont(entry_field_font);
|
|
||||||
|
|
||||||
// Set placeholder text for the main entry field
|
// Set the main entry field font size to 1.2x its standard font size.
|
||||||
entry_field->setPlaceholderText(tr("Search for action..."));
|
QFont entry_field_font = entry_field->font();
|
||||||
|
entry_field_font.setPointSize(qRound(entry_field_font.pointSize() * 1.2));
|
||||||
|
entry_field->setFont(entry_field_font);
|
||||||
|
|
||||||
// Connect signals/slots
|
// Set placeholder text for the main entry field
|
||||||
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
|
entry_field->setPlaceholderText(tr("Search for action..."));
|
||||||
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
|
|
||||||
|
|
||||||
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
|
// Connect signals/slots
|
||||||
// We override it here to select the upper or lower item in the list.
|
connect(entry_field, SIGNAL(textChanged(const QString &)), this,
|
||||||
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up()));
|
SLOT(search_update(const QString &)));
|
||||||
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
|
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
|
||||||
layout->addWidget(entry_field);
|
|
||||||
|
|
||||||
// Construct list of actions
|
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
|
||||||
list_widget = new ActionSearchList(this);
|
// We override it here to select the upper or lower item in the list.
|
||||||
|
connect(entry_field, SIGNAL(moveSelectionUp()), this,
|
||||||
|
SLOT(move_selection_up()));
|
||||||
|
connect(entry_field, SIGNAL(moveSelectionDown()), this,
|
||||||
|
SLOT(move_selection_down()));
|
||||||
|
layout->addWidget(entry_field);
|
||||||
|
|
||||||
// Set list's font to 1.2x its standard font size
|
// Construct list of actions
|
||||||
QFont list_widget_font = list_widget->font();
|
list_widget = new ActionSearchList(this);
|
||||||
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
|
|
||||||
list_widget->setFont(list_widget_font);
|
|
||||||
|
|
||||||
layout->addWidget(list_widget);
|
// Set list's font to 1.2x its standard font size
|
||||||
|
QFont list_widget_font = list_widget->font();
|
||||||
|
list_widget_font.setPointSize(qRound(list_widget_font.pointSize() * 1.2));
|
||||||
|
list_widget->setFont(list_widget_font);
|
||||||
|
|
||||||
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
|
layout->addWidget(list_widget);
|
||||||
|
|
||||||
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
|
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
|
||||||
// shortcut for example).
|
|
||||||
entry_field->setFocus();
|
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
|
||||||
|
// shortcut for example).
|
||||||
|
entry_field->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionSearch::SetMenuBar(QMenuBar *menu_bar)
|
void ActionSearch::SetMenuBar(QMenuBar *menu_bar)
|
||||||
{
|
{
|
||||||
menu_bar_ = menu_bar;
|
menu_bar_ = menu_bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionSearch::search_update(const QString &s, const QString &p, QMenu *parent)
|
void ActionSearch::search_update(const QString &s, const QString &p,
|
||||||
|
QMenu *parent)
|
||||||
{
|
{
|
||||||
// Do nothing if there's no menu bar to work with
|
// Do nothing if there's no menu bar to work with
|
||||||
if (menu_bar_ == nullptr) {
|
if (menu_bar_ == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
|
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
|
||||||
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
|
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
|
||||||
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
|
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
|
||||||
// submenus).
|
// submenus).
|
||||||
|
|
||||||
if (parent == nullptr) {
|
if (parent == nullptr) {
|
||||||
|
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
|
||||||
|
// (and their submenus).
|
||||||
|
|
||||||
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
|
// We'll clear all the current items in the list since if we're here, we're just starting.
|
||||||
// (and their submenus).
|
list_widget->clear();
|
||||||
|
|
||||||
// We'll clear all the current items in the list since if we're here, we're just starting.
|
QList<QAction *> menus = menu_bar_->actions();
|
||||||
list_widget->clear();
|
|
||||||
|
|
||||||
QList<QAction*> menus = menu_bar_->actions();
|
// Loop through all menus from the menubar and run this function on each one.
|
||||||
|
for (int i = 0; i < menus.size(); i++) {
|
||||||
|
QMenu *menu = menus.at(i)->menu();
|
||||||
|
|
||||||
// Loop through all menus from the menubar and run this function on each one.
|
search_update(s, p, menu);
|
||||||
for (int i=0;i<menus.size();i++) {
|
}
|
||||||
QMenu* menu = menus.at(i)->menu();
|
|
||||||
|
|
||||||
search_update(s, p, menu);
|
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
|
||||||
}
|
// keyboard-exclusive functionality.
|
||||||
|
if (list_widget->count() > 0) {
|
||||||
|
list_widget->item(0)->setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
|
} else {
|
||||||
// keyboard-exclusive functionality.
|
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
|
||||||
if (list_widget->count() > 0) {
|
|
||||||
list_widget->item(0)->setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
|
||||||
|
// adding the current menu's text to the existing hierarchy (passed in `p`).
|
||||||
|
QString menu_text;
|
||||||
|
if (!p.isEmpty())
|
||||||
|
menu_text += p + " > ";
|
||||||
|
menu_text += parent->title().replace(
|
||||||
|
"&", ""); // Strip out any &s used in menu action names
|
||||||
|
|
||||||
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
|
// Loop over the menu's actions
|
||||||
|
QList<QAction *> actions = parent->actions();
|
||||||
|
for (int i = 0; i < actions.size(); i++) {
|
||||||
|
QAction *a = actions.at(i);
|
||||||
|
|
||||||
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
|
// Ignore separator actions
|
||||||
// adding the current menu's text to the existing hierarchy (passed in `p`).
|
if (!a->isSeparator()) {
|
||||||
QString menu_text;
|
if (a->menu() != nullptr) {
|
||||||
if (!p.isEmpty()) menu_text += p + " > ";
|
// If the action is a menu, run this function recursively on it
|
||||||
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names
|
search_update(s, menu_text, a->menu());
|
||||||
|
|
||||||
// Loop over the menu's actions
|
} else {
|
||||||
QList<QAction*> actions = parent->actions();
|
// This is a valid non-separator non-menu action, so check it against the currently entered string.
|
||||||
for (int i=0;i<actions.size();i++) {
|
|
||||||
|
|
||||||
QAction* a = actions.at(i);
|
// Strip out all &s from the action's name
|
||||||
|
QString comp = a->text().replace("&", "");
|
||||||
|
|
||||||
// Ignore separator actions
|
// See if the action's name contains any of the currently entered string
|
||||||
if (!a->isSeparator()) {
|
if (comp.contains(s, Qt::CaseInsensitive)) {
|
||||||
|
// If so, we add it to the list widget.
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(
|
||||||
|
QStringLiteral("%1\n(%2)").arg(comp, menu_text),
|
||||||
|
list_widget);
|
||||||
|
|
||||||
if (a->menu() != nullptr) {
|
// Add a pointer to the original QAction in the item's data
|
||||||
|
item->setData(Qt::UserRole + 1,
|
||||||
|
reinterpret_cast<quintptr>(a));
|
||||||
|
|
||||||
// If the action is a menu, run this function recursively on it
|
list_widget->addItem(item);
|
||||||
search_update(s, menu_text, a->menu());
|
}
|
||||||
|
}
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
// This is a valid non-separator non-menu action, so check it against the currently entered string.
|
}
|
||||||
|
|
||||||
// Strip out all &s from the action's name
|
|
||||||
QString comp = a->text().replace("&", "");
|
|
||||||
|
|
||||||
// See if the action's name contains any of the currently entered string
|
|
||||||
if (comp.contains(s, Qt::CaseInsensitive)) {
|
|
||||||
|
|
||||||
// If so, we add it to the list widget.
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(QStringLiteral("%1\n(%2)").arg(comp, menu_text), list_widget);
|
|
||||||
|
|
||||||
// Add a pointer to the original QAction in the item's data
|
|
||||||
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
|
|
||||||
|
|
||||||
list_widget->addItem(item);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionSearch::perform_action() {
|
void ActionSearch::perform_action()
|
||||||
|
{
|
||||||
|
// Loop over all the items in the list and if we find one that's selected, we trigger it.
|
||||||
|
QList<QListWidgetItem *> selected_items = list_widget->selectedItems();
|
||||||
|
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
||||||
|
QListWidgetItem *item = selected_items.at(0);
|
||||||
|
|
||||||
// Loop over all the items in the list and if we find one that's selected, we trigger it.
|
// Get QAction pointer from item's data
|
||||||
QList<QListWidgetItem*> selected_items = list_widget->selectedItems();
|
QAction *a = reinterpret_cast<QAction *>(
|
||||||
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
item->data(Qt::UserRole + 1).value<quintptr>());
|
||||||
|
|
||||||
QListWidgetItem* item = selected_items.at(0);
|
a->trigger();
|
||||||
|
}
|
||||||
// Get QAction pointer from item's data
|
|
||||||
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
|
|
||||||
|
|
||||||
a->trigger();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close this popup
|
|
||||||
accept();
|
|
||||||
|
|
||||||
|
// Close this popup
|
||||||
|
accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionSearch::move_selection_up() {
|
void ActionSearch::move_selection_up()
|
||||||
|
{
|
||||||
|
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
|
||||||
|
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
|
||||||
|
// bottom item).
|
||||||
|
|
||||||
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
|
int lim = list_widget->count();
|
||||||
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
|
for (int i = 1; i < lim; i++) {
|
||||||
// bottom item).
|
if (list_widget->item(i)->isSelected()) {
|
||||||
|
list_widget->item(i - 1)->setSelected(true);
|
||||||
int lim = list_widget->count();
|
list_widget->scrollToItem(list_widget->item(i - 1));
|
||||||
for (int i=1;i<lim;i++) {
|
break;
|
||||||
if (list_widget->item(i)->isSelected()) {
|
}
|
||||||
list_widget->item(i-1)->setSelected(true);
|
}
|
||||||
list_widget->scrollToItem(list_widget->item(i-1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionSearch::move_selection_down() {
|
void ActionSearch::move_selection_down()
|
||||||
|
{
|
||||||
|
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
|
||||||
|
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
|
||||||
|
// bottom item).
|
||||||
|
|
||||||
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
|
int lim = list_widget->count() - 1;
|
||||||
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
|
for (int i = 0; i < lim; i++) {
|
||||||
// bottom item).
|
if (list_widget->item(i)->isSelected()) {
|
||||||
|
list_widget->item(i + 1)->setSelected(true);
|
||||||
int lim = list_widget->count()-1;
|
list_widget->scrollToItem(list_widget->item(i + 1));
|
||||||
for (int i=0;i<lim;i++) {
|
break;
|
||||||
if (list_widget->item(i)->isSelected()) {
|
}
|
||||||
list_widget->item(i+1)->setSelected(true);
|
}
|
||||||
list_widget->scrollToItem(list_widget->item(i+1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {}
|
ActionSearchEntry::ActionSearchEntry(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool ActionSearchEntry::event(QEvent *e)
|
bool ActionSearchEntry::event(QEvent *e)
|
||||||
{
|
{
|
||||||
switch (e->type()) {
|
switch (e->type()) {
|
||||||
case QEvent::ShortcutOverride:
|
case QEvent::ShortcutOverride:
|
||||||
switch (static_cast<QKeyEvent*>(e)->key()) {
|
switch (static_cast<QKeyEvent *>(e)->key()) {
|
||||||
case Qt::Key_Up:
|
case Qt::Key_Up:
|
||||||
case Qt::Key_Down:
|
case Qt::Key_Down:
|
||||||
e->accept();
|
e->accept();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEvent::KeyPress:
|
case QEvent::KeyPress:
|
||||||
// Listen for up/down, otherwise pass the key event to the base class.
|
// Listen for up/down, otherwise pass the key event to the base class.
|
||||||
switch (static_cast<QKeyEvent*>(e)->key()) {
|
switch (static_cast<QKeyEvent *>(e)->key()) {
|
||||||
case Qt::Key_Up:
|
case Qt::Key_Up:
|
||||||
e->accept();
|
e->accept();
|
||||||
emit moveSelectionUp();
|
emit moveSelectionUp();
|
||||||
return true;
|
return true;
|
||||||
case Qt::Key_Down:
|
case Qt::Key_Down:
|
||||||
e->accept();
|
e->accept();
|
||||||
emit moveSelectionDown();
|
emit moveSelectionDown();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QLineEdit::event(e);
|
return QLineEdit::event(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {}
|
ActionSearchList::ActionSearchList(QWidget *parent)
|
||||||
|
: QListWidget(parent)
|
||||||
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) {
|
{
|
||||||
|
}
|
||||||
// Indiscriminately emit a signal on any double click
|
|
||||||
emit dbl_click();
|
|
||||||
|
|
||||||
|
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *)
|
||||||
|
{
|
||||||
|
// Indiscriminately emit a signal on any double click
|
||||||
|
emit dbl_click();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class ActionSearchList;
|
class ActionSearchList;
|
||||||
|
|
||||||
@@ -39,11 +40,10 @@ class ActionSearchList;
|
|||||||
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
|
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
|
||||||
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
|
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
|
||||||
*/
|
*/
|
||||||
class ActionSearch : public QDialog
|
class ActionSearch : public QDialog {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief ActionSearch Constructor
|
* @brief ActionSearch Constructor
|
||||||
*
|
*
|
||||||
* Create ActionSearch popup.
|
* Create ActionSearch popup.
|
||||||
@@ -52,14 +52,14 @@ public:
|
|||||||
*
|
*
|
||||||
* QWidget parent. Usually MainWindow.
|
* QWidget parent. Usually MainWindow.
|
||||||
*/
|
*/
|
||||||
ActionSearch(QWidget* parent);
|
ActionSearch(QWidget *parent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the menu bar to use in this action search
|
* @brief Set the menu bar to use in this action search
|
||||||
*/
|
*/
|
||||||
void SetMenuBar(QMenuBar* menu_bar);
|
void SetMenuBar(QMenuBar *menu_bar);
|
||||||
private slots:
|
private slots:
|
||||||
/**
|
/**
|
||||||
* @brief Update the list of actions according to a search query
|
* @brief Update the list of actions according to a search query
|
||||||
*
|
*
|
||||||
* This function adds/removes actions in the action list according to a given search query entered by the user.
|
* This function adds/removes actions in the action list according to a given search query entered by the user.
|
||||||
@@ -82,42 +82,44 @@ private slots:
|
|||||||
* The current menu to loop over. In most cases, this should be left as nullptr when called externally.
|
* The current menu to loop over. In most cases, this should be left as nullptr when called externally.
|
||||||
* search_update() will fill this automatically as it needs while calling itself recursively.
|
* search_update() will fill this automatically as it needs while calling itself recursively.
|
||||||
*/
|
*/
|
||||||
void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr);
|
void search_update(const QString &s, const QString &p = nullptr,
|
||||||
|
QMenu *parent = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Perform the currently selected action
|
* @brief Perform the currently selected action
|
||||||
*
|
*
|
||||||
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
|
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
|
||||||
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
|
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
|
||||||
* triggered and the popup closes anyway.
|
* triggered and the popup closes anyway.
|
||||||
*/
|
*/
|
||||||
void perform_action();
|
void perform_action();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move selection up
|
* @brief Move selection up
|
||||||
*
|
*
|
||||||
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
|
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
|
||||||
* selection is already at the top of the list, this is a no-op.
|
* selection is already at the top of the list, this is a no-op.
|
||||||
*/
|
*/
|
||||||
void move_selection_up();
|
void move_selection_up();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move selection down
|
* @brief Move selection down
|
||||||
*
|
*
|
||||||
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
|
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
|
||||||
* selection is already at the bottom of the list, this is a no-op.
|
* selection is already at the bottom of the list, this is a no-op.
|
||||||
*/
|
*/
|
||||||
void move_selection_down();
|
void move_selection_down();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Main widget that shows the list of commands
|
* @brief Main widget that shows the list of commands
|
||||||
*/
|
*/
|
||||||
ActionSearchList* list_widget;
|
ActionSearchList *list_widget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Attached menu bar object
|
* @brief Attached menu bar object
|
||||||
*/
|
*/
|
||||||
QMenuBar* menu_bar_;
|
QMenuBar *menu_bar_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,25 +129,26 @@ private:
|
|||||||
* to a slot that triggers the currently selected action.
|
* to a slot that triggers the currently selected action.
|
||||||
*/
|
*/
|
||||||
class ActionSearchList : public QListWidget {
|
class ActionSearchList : public QListWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief ActionSearchList Constructor
|
* @brief ActionSearchList Constructor
|
||||||
* @param parent
|
* @param parent
|
||||||
*
|
*
|
||||||
* Usually ActionSearch.
|
* Usually ActionSearch.
|
||||||
*/
|
*/
|
||||||
ActionSearchList(QWidget* parent);
|
ActionSearchList(QWidget *parent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Override of QListWidget's double click event that emits a signal.
|
* @brief Override of QListWidget's double click event that emits a signal.
|
||||||
*/
|
*/
|
||||||
void mouseDoubleClickEvent(QMouseEvent *);
|
void mouseDoubleClickEvent(QMouseEvent *);
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* @brief Signal emitted when a QListWidget item is double clicked.
|
* @brief Signal emitted when a QListWidget item is double clicked.
|
||||||
*/
|
*/
|
||||||
void dbl_click();
|
void dbl_click();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,32 +158,33 @@ signals:
|
|||||||
* can connect them to moving the current selection up or down.
|
* can connect them to moving the current selection up or down.
|
||||||
*/
|
*/
|
||||||
class ActionSearchEntry : public QLineEdit {
|
class ActionSearchEntry : public QLineEdit {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief ActionSearchEntry
|
* @brief ActionSearchEntry
|
||||||
* @param parent
|
* @param parent
|
||||||
*
|
*
|
||||||
* Usually ActionSearch.
|
* Usually ActionSearch.
|
||||||
*/
|
*/
|
||||||
ActionSearchEntry(QWidget* parent);
|
ActionSearchEntry(QWidget *parent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Override of QLineEdit's key press event that listens for up/down key presses.
|
* @brief Override of QLineEdit's key press event that listens for up/down key presses.
|
||||||
* @param event
|
* @param event
|
||||||
*/
|
*/
|
||||||
virtual bool event(QEvent *e) override;
|
virtual bool event(QEvent *e) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* @brief Emitted when the user presses the up arrow key.
|
* @brief Emitted when the user presses the up arrow key.
|
||||||
*/
|
*/
|
||||||
void moveSelectionUp();
|
void moveSelectionUp();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Emitted when the user presses the down arrow key.
|
* @brief Emitted when the user presses the down arrow key.
|
||||||
*/
|
*/
|
||||||
void moveSelectionDown();
|
void moveSelectionDown();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,108 +29,127 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
#define super QDialog
|
#define super QDialog
|
||||||
|
|
||||||
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message, const QStringList &recoveries, bool autocheck_latest, QWidget* parent) :
|
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message,
|
||||||
QDialog(parent)
|
const QStringList &recoveries,
|
||||||
|
bool autocheck_latest, QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
Init(message);
|
Init(message);
|
||||||
|
|
||||||
PopulateTree(recoveries, autocheck_latest);
|
PopulateTree(recoveries, autocheck_latest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoRecoveryDialog::accept()
|
void AutoRecoveryDialog::accept()
|
||||||
{
|
{
|
||||||
foreach (QTreeWidgetItem* checkable, checkable_items_) {
|
foreach (QTreeWidgetItem *checkable, checkable_items_) {
|
||||||
if (checkable->checkState(0) == Qt::Checked) {
|
if (checkable->checkState(0) == Qt::Checked) {
|
||||||
QString filename = checkable->data(0, kFilenameRole).toString();
|
QString filename = checkable->data(0, kFilenameRole).toString();
|
||||||
Core::instance()->OpenRecoveryProject(filename);
|
Core::instance()->OpenRecoveryProject(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super::accept();
|
super::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoRecoveryDialog::Init(const QString& header_text)
|
void AutoRecoveryDialog::Init(const QString &header_text)
|
||||||
{
|
{
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
setWindowTitle(tr("Auto-Recovery"));
|
setWindowTitle(tr("Auto-Recovery"));
|
||||||
|
|
||||||
layout->addWidget(new QLabel(header_text));
|
layout->addWidget(new QLabel(header_text));
|
||||||
|
|
||||||
tree_widget_ = new QTreeWidget();
|
tree_widget_ = new QTreeWidget();
|
||||||
tree_widget_->setHeaderHidden(true);
|
tree_widget_->setHeaderHidden(true);
|
||||||
layout->addWidget(tree_widget_);
|
layout->addWidget(tree_widget_);
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
QDialogButtonBox *buttons =
|
||||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
|
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
connect(buttons, &QDialogButtonBox::accepted, this, &AutoRecoveryDialog::accept);
|
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
|
||||||
connect(buttons, &QDialogButtonBox::rejected, this, &AutoRecoveryDialog::reject);
|
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||||
layout->addWidget(buttons);
|
&AutoRecoveryDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||||
|
&AutoRecoveryDialog::reject);
|
||||||
|
layout->addWidget(buttons);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autocheck_latest)
|
void AutoRecoveryDialog::PopulateTree(const QStringList &recoveries,
|
||||||
|
bool autocheck_latest)
|
||||||
{
|
{
|
||||||
// Each entry in `recoveries` is a directory with 1+ recovery projects in it
|
// Each entry in `recoveries` is a directory with 1+ recovery projects in it
|
||||||
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
|
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
|
||||||
|
|
||||||
foreach (const QString& recovery_folder, recoveries) {
|
foreach (const QString &recovery_folder, recoveries) {
|
||||||
QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
|
QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
|
||||||
|
|
||||||
QString pretty_name;
|
QString pretty_name;
|
||||||
{
|
{
|
||||||
// Retrieve pretty name
|
// Retrieve pretty name
|
||||||
QFile pretty_name_file(recovery_dir.filePath(QStringLiteral("realname.txt")));
|
QFile pretty_name_file(
|
||||||
if (pretty_name_file.open(QFile::ReadOnly)) {
|
recovery_dir.filePath(QStringLiteral("realname.txt")));
|
||||||
// Read pretty name that we should have written in the autorecovery process
|
if (pretty_name_file.open(QFile::ReadOnly)) {
|
||||||
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
|
// Read pretty name that we should have written in the autorecovery process
|
||||||
pretty_name_file.close();
|
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
|
||||||
}
|
pretty_name_file.close();
|
||||||
|
}
|
||||||
|
|
||||||
if (pretty_name.isEmpty()) {
|
if (pretty_name.isEmpty()) {
|
||||||
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
|
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
|
||||||
pretty_name = recovery_dir.dirName();
|
pretty_name = recovery_dir.dirName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QTreeWidgetItem* top_level = new QTreeWidgetItem(tree_widget_);
|
QTreeWidgetItem *top_level = new QTreeWidgetItem(tree_widget_);
|
||||||
top_level->setText(0, pretty_name);
|
top_level->setText(0, pretty_name);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Populate with recoveries
|
// Populate with recoveries
|
||||||
QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
|
QStringList entries =
|
||||||
for (int i=0; i<entries.size(); i++) {
|
recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
|
||||||
const QString& entry = entries.at(i);
|
QDir::Name | QDir::Reversed);
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
const QString &entry = entries.at(i);
|
||||||
|
|
||||||
if (entry.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) {
|
if (entry.endsWith(QStringLiteral(".ove"),
|
||||||
QTreeWidgetItem* entry_item = new QTreeWidgetItem(top_level);
|
Qt::CaseInsensitive)) {
|
||||||
|
QTreeWidgetItem *entry_item =
|
||||||
|
new QTreeWidgetItem(top_level);
|
||||||
|
|
||||||
bool ok;
|
bool ok;
|
||||||
qint64 recovery_time = entry.left(entry.indexOf('.')).toLongLong(&ok);
|
qint64 recovery_time =
|
||||||
QString entry_name;
|
entry.left(entry.indexOf('.')).toLongLong(&ok);
|
||||||
|
QString entry_name;
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
// Set as time/date of recovery
|
// Set as time/date of recovery
|
||||||
entry_name = QDateTime::fromSecsSinceEpoch(recovery_time).toString();
|
entry_name =
|
||||||
} else {
|
QDateTime::fromSecsSinceEpoch(recovery_time)
|
||||||
// Fallback if we couldn't discern a date from this
|
.toString();
|
||||||
entry_name = entry;
|
} else {
|
||||||
}
|
// Fallback if we couldn't discern a date from this
|
||||||
|
entry_name = entry;
|
||||||
|
}
|
||||||
|
|
||||||
entry_item->setText(0, entry_name);
|
entry_item->setText(0, entry_name);
|
||||||
entry_item->setData(0, kFilenameRole, recovery_dir.filePath(entry));
|
entry_item->setData(0, kFilenameRole,
|
||||||
|
recovery_dir.filePath(entry));
|
||||||
|
|
||||||
// Allow to be checked, auto-checking the first entry
|
// Allow to be checked, auto-checking the first entry
|
||||||
entry_item->setCheckState(0, (autocheck_latest && top_level->childCount() == 1) ? Qt::Checked : Qt::Unchecked);
|
entry_item->setCheckState(
|
||||||
|
0, (autocheck_latest && top_level->childCount() == 1) ?
|
||||||
|
Qt::Checked :
|
||||||
|
Qt::Unchecked);
|
||||||
|
|
||||||
checkable_items_.append(entry_item);
|
checkable_items_.append(entry_item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,30 +26,28 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class AutoRecoveryDialog : public QDialog
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class AutoRecoveryDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AutoRecoveryDialog(const QString& message, const QStringList& recoveries, bool autocheck_latest, QWidget* parent);
|
AutoRecoveryDialog(const QString &message, const QStringList &recoveries,
|
||||||
|
bool autocheck_latest, QWidget *parent);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Init(const QString &header_text);
|
void Init(const QString &header_text);
|
||||||
|
|
||||||
void PopulateTree(const QStringList &recoveries, bool autocheck);
|
void PopulateTree(const QStringList &recoveries, bool autocheck);
|
||||||
|
|
||||||
QTreeWidget* tree_widget_;
|
QTreeWidget *tree_widget_;
|
||||||
|
|
||||||
QVector<QTreeWidgetItem*> checkable_items_;
|
QVector<QTreeWidgetItem *> checkable_items_;
|
||||||
|
|
||||||
enum DataRole {
|
|
||||||
kFilenameRole = Qt::UserRole
|
|
||||||
};
|
|
||||||
|
|
||||||
|
enum DataRole { kFilenameRole = Qt::UserRole };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+120
-101
@@ -26,167 +26,186 @@
|
|||||||
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start, QWidget *parent) :
|
|
||||||
QDialog(parent),
|
|
||||||
color_manager_(color_manager)
|
|
||||||
{
|
{
|
||||||
setWindowTitle(tr("Select Color"));
|
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
|
||||||
|
QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, color_manager_(color_manager)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Select Color"));
|
||||||
|
|
||||||
QSplitter* splitter = new QSplitter(Qt::Horizontal);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
splitter->setChildrenCollapsible(false);
|
|
||||||
layout->addWidget(splitter);
|
|
||||||
|
|
||||||
QWidget* graphics_area = new QWidget();
|
QSplitter *splitter = new QSplitter(Qt::Horizontal);
|
||||||
splitter->addWidget(graphics_area);
|
splitter->setChildrenCollapsible(false);
|
||||||
|
layout->addWidget(splitter);
|
||||||
|
|
||||||
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
|
QWidget *graphics_area = new QWidget();
|
||||||
|
splitter->addWidget(graphics_area);
|
||||||
|
|
||||||
QHBoxLayout* wheel_layout = new QHBoxLayout();
|
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
|
||||||
graphics_layout->addLayout(wheel_layout);
|
|
||||||
|
|
||||||
color_wheel_ = new ColorWheelWidget();
|
QHBoxLayout *wheel_layout = new QHBoxLayout();
|
||||||
wheel_layout->addWidget(color_wheel_);
|
graphics_layout->addLayout(wheel_layout);
|
||||||
|
|
||||||
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
|
color_wheel_ = new ColorWheelWidget();
|
||||||
hsv_value_gradient_->setFixedWidth(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
|
wheel_layout->addWidget(color_wheel_);
|
||||||
wheel_layout->addWidget(hsv_value_gradient_);
|
|
||||||
|
|
||||||
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
|
||||||
graphics_layout->addLayout(swatch_layout);
|
hsv_value_gradient_->setFixedWidth(
|
||||||
|
QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
|
||||||
|
wheel_layout->addWidget(hsv_value_gradient_);
|
||||||
|
|
||||||
swatch_layout->addStretch();
|
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
||||||
|
graphics_layout->addLayout(swatch_layout);
|
||||||
|
|
||||||
swatch_ = new ColorSwatchChooser(color_manager_);
|
swatch_layout->addStretch();
|
||||||
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
|
||||||
swatch_layout->addWidget(swatch_);
|
|
||||||
|
|
||||||
swatch_layout->addStretch();
|
swatch_ = new ColorSwatchChooser(color_manager_);
|
||||||
|
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||||
|
swatch_layout->addWidget(swatch_);
|
||||||
|
|
||||||
QWidget* value_area = new QWidget();
|
swatch_layout->addStretch();
|
||||||
QVBoxLayout* value_layout = new QVBoxLayout(value_area);
|
|
||||||
value_layout->setSpacing(0);
|
|
||||||
splitter->addWidget(value_area);
|
|
||||||
|
|
||||||
color_values_widget_ = new ColorValuesWidget(color_manager_);
|
QWidget *value_area = new QWidget();
|
||||||
color_values_widget_->IgnorePickFrom(this);
|
QVBoxLayout *value_layout = new QVBoxLayout(value_area);
|
||||||
value_layout->addWidget(color_values_widget_);
|
value_layout->setSpacing(0);
|
||||||
|
splitter->addWidget(value_area);
|
||||||
|
|
||||||
chooser_ = new ColorSpaceChooser(color_manager_);
|
color_values_widget_ = new ColorValuesWidget(color_manager_);
|
||||||
|
color_values_widget_->IgnorePickFrom(this);
|
||||||
|
value_layout->addWidget(color_values_widget_);
|
||||||
|
|
||||||
value_layout->addWidget(chooser_);
|
chooser_ = new ColorSpaceChooser(color_manager_);
|
||||||
|
|
||||||
// Split window 50/50
|
value_layout->addWidget(chooser_);
|
||||||
splitter->setSizes({INT_MAX, INT_MAX});
|
|
||||||
|
|
||||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
// Split window 50/50
|
||||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
splitter->setSizes({ INT_MAX, INT_MAX });
|
||||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
|
||||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
|
||||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
|
||||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
|
||||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
|
||||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
|
||||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
|
||||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
|
||||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
|
||||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_, &ColorValuesWidget::SetColor);
|
|
||||||
|
|
||||||
connect(color_wheel_, &ColorWheelWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
|
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
|
||||||
|
color_values_widget_, &ColorValuesWidget::SetColor);
|
||||||
|
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
|
||||||
|
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||||
|
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_,
|
||||||
|
&ColorSwatchChooser::SetCurrentColor);
|
||||||
|
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||||
|
color_values_widget_, &ColorValuesWidget::SetColor);
|
||||||
|
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||||
|
color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||||
|
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||||
|
swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||||
|
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
|
||||||
|
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||||
|
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
|
||||||
|
color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||||
|
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_,
|
||||||
|
&ColorSwatchChooser::SetCurrentColor);
|
||||||
|
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_,
|
||||||
|
&ColorGradientWidget::SetSelectedColor);
|
||||||
|
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_,
|
||||||
|
&ColorWheelWidget::SetSelectedColor);
|
||||||
|
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_,
|
||||||
|
&ColorValuesWidget::SetColor);
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
connect(color_wheel_, &ColorWheelWidget::DiameterChanged,
|
||||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
|
||||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
||||||
layout->addWidget(buttons);
|
|
||||||
|
|
||||||
SetColor(start);
|
QDialogButtonBox *buttons =
|
||||||
|
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
layout->addWidget(buttons);
|
||||||
|
|
||||||
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged);
|
SetColor(start);
|
||||||
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
|
||||||
|
|
||||||
// Set default size ratio to 2:1
|
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this,
|
||||||
resize(sizeHint().height() * 2, sizeHint().height());
|
&ColorDialog::ColorSpaceChanged);
|
||||||
|
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
||||||
|
|
||||||
|
// Set default size ratio to 2:1
|
||||||
|
resize(sizeHint().height() * 2, sizeHint().height());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorDialog::SetColor(const ManagedColor &start)
|
void ColorDialog::SetColor(const ManagedColor &start)
|
||||||
{
|
{
|
||||||
chooser_->set_input(start.color_input());
|
chooser_->set_input(start.color_input());
|
||||||
chooser_->set_output(start.color_output());
|
chooser_->set_output(start.color_output());
|
||||||
|
|
||||||
Color managed_start;
|
Color managed_start;
|
||||||
|
|
||||||
if (start.color_input().isEmpty()) {
|
if (start.color_input().isEmpty()) {
|
||||||
|
managed_start = start;
|
||||||
|
|
||||||
managed_start = start;
|
} else {
|
||||||
|
// Convert reference color to the input space
|
||||||
|
ColorProcessorPtr linear_to_input = ColorProcessor::Create(
|
||||||
|
color_manager_, color_manager_->GetReferenceColorSpace(),
|
||||||
|
start.color_input());
|
||||||
|
|
||||||
} else {
|
managed_start = linear_to_input->ConvertColor(start);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert reference color to the input space
|
color_wheel_->SetSelectedColor(managed_start);
|
||||||
ColorProcessorPtr linear_to_input = ColorProcessor::Create(color_manager_,
|
hsv_value_gradient_->SetSelectedColor(managed_start);
|
||||||
color_manager_->GetReferenceColorSpace(),
|
color_values_widget_->SetColor(managed_start);
|
||||||
start.color_input());
|
swatch_->SetCurrentColor(managed_start);
|
||||||
|
|
||||||
managed_start = linear_to_input->ConvertColor(start);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
color_wheel_->SetSelectedColor(managed_start);
|
|
||||||
hsv_value_gradient_->SetSelectedColor(managed_start);
|
|
||||||
color_values_widget_->SetColor(managed_start);
|
|
||||||
swatch_->SetCurrentColor(managed_start);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ManagedColor ColorDialog::GetSelectedColor() const
|
ManagedColor ColorDialog::GetSelectedColor() const
|
||||||
{
|
{
|
||||||
ManagedColor selected = color_wheel_->GetSelectedColor();
|
ManagedColor selected = color_wheel_->GetSelectedColor();
|
||||||
|
|
||||||
// Convert to linear and return a linear color
|
// Convert to linear and return a linear color
|
||||||
if (input_to_ref_processor_) {
|
if (input_to_ref_processor_) {
|
||||||
selected = input_to_ref_processor_->ConvertColor(selected);
|
selected = input_to_ref_processor_->ConvertColor(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
selected.set_color_input(GetColorSpaceInput());
|
selected.set_color_input(GetColorSpaceInput());
|
||||||
selected.set_color_output(GetColorSpaceOutput());
|
selected.set_color_output(GetColorSpaceOutput());
|
||||||
|
|
||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ColorDialog::GetColorSpaceInput() const
|
QString ColorDialog::GetColorSpaceInput() const
|
||||||
{
|
{
|
||||||
return chooser_->input();
|
return chooser_->input();
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorTransform ColorDialog::GetColorSpaceOutput() const
|
ColorTransform ColorDialog::GetColorSpaceOutput() const
|
||||||
{
|
{
|
||||||
return chooser_->output();
|
return chooser_->output();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorDialog::ColorSpaceChanged(const QString &input, const ColorTransform &output)
|
void ColorDialog::ColorSpaceChanged(const QString &input,
|
||||||
|
const ColorTransform &output)
|
||||||
{
|
{
|
||||||
input_to_ref_processor_ = ColorProcessor::Create(color_manager_, input, color_manager_->GetReferenceColorSpace());
|
input_to_ref_processor_ = ColorProcessor::Create(
|
||||||
|
color_manager_, input, color_manager_->GetReferenceColorSpace());
|
||||||
|
|
||||||
ColorProcessorPtr ref_to_display = ColorProcessor::Create(color_manager_,
|
ColorProcessorPtr ref_to_display = ColorProcessor::Create(
|
||||||
color_manager_->GetReferenceColorSpace(),
|
color_manager_, color_manager_->GetReferenceColorSpace(), output);
|
||||||
output);
|
|
||||||
|
|
||||||
ColorProcessorPtr ref_to_input = ColorProcessor::Create(color_manager_, color_manager_->GetReferenceColorSpace(), input);
|
ColorProcessorPtr ref_to_input = ColorProcessor::Create(
|
||||||
|
color_manager_, color_manager_->GetReferenceColorSpace(), input);
|
||||||
|
|
||||||
// FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to
|
// FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to
|
||||||
// crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget)
|
// crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget)
|
||||||
|
|
||||||
/*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(),
|
/*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(),
|
||||||
color_manager_->GetReferenceColorSpace(),
|
color_manager_->GetReferenceColorSpace(),
|
||||||
display,
|
display,
|
||||||
view,
|
view,
|
||||||
look,
|
look,
|
||||||
ColorProcessor::kInverse);*/
|
ColorProcessor::kInverse);*/
|
||||||
|
|
||||||
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
|
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
|
||||||
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
|
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_,
|
||||||
color_values_widget_->SetColorProcessor(input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
|
ref_to_display);
|
||||||
|
color_values_widget_->SetColorProcessor(
|
||||||
|
input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,13 +31,13 @@
|
|||||||
#include "widget/colorwheel/colorvalueswidget.h"
|
#include "widget/colorwheel/colorvalueswidget.h"
|
||||||
#include "widget/colorwheel/colorwheelwidget.h"
|
#include "widget/colorwheel/colorwheelwidget.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ColorDialog : public QDialog
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class ColorDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief ColorDialog Constructor
|
* @brief ColorDialog Constructor
|
||||||
*
|
*
|
||||||
* @param color_manager
|
* @param color_manager
|
||||||
@@ -56,40 +56,41 @@ public:
|
|||||||
*
|
*
|
||||||
* QWidget parent.
|
* QWidget parent.
|
||||||
*/
|
*/
|
||||||
ColorDialog(ColorManager* color_manager, const ManagedColor &start = Color(1.0f, 1.0f, 1.0f), QWidget* parent = nullptr);
|
ColorDialog(ColorManager *color_manager,
|
||||||
|
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the color selected by the user
|
* @brief Retrieves the color selected by the user
|
||||||
*
|
*
|
||||||
* The color is always returned in the ColorManager's reference space (usually scene linear).
|
* The color is always returned in the ColorManager's reference space (usually scene linear).
|
||||||
*/
|
*/
|
||||||
ManagedColor GetSelectedColor() const;
|
ManagedColor GetSelectedColor() const;
|
||||||
|
|
||||||
QString GetColorSpaceInput() const;
|
QString GetColorSpaceInput() const;
|
||||||
|
|
||||||
ColorTransform GetColorSpaceOutput() const;
|
ColorTransform GetColorSpaceOutput() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetColor(const ManagedColor &c);
|
void SetColor(const ManagedColor &c);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ColorManager* color_manager_;
|
ColorManager *color_manager_;
|
||||||
|
|
||||||
ColorWheelWidget* color_wheel_;
|
ColorWheelWidget *color_wheel_;
|
||||||
|
|
||||||
ColorValuesWidget* color_values_widget_;
|
ColorValuesWidget *color_values_widget_;
|
||||||
|
|
||||||
ColorGradientWidget* hsv_value_gradient_;
|
ColorGradientWidget *hsv_value_gradient_;
|
||||||
|
|
||||||
ColorProcessorPtr input_to_ref_processor_;
|
ColorProcessorPtr input_to_ref_processor_;
|
||||||
|
|
||||||
ColorSpaceChooser* chooser_;
|
ColorSpaceChooser *chooser_;
|
||||||
|
|
||||||
ColorSwatchChooser *swatch_;
|
ColorSwatchChooser *swatch_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ColorSpaceChanged(const QString& input, const ColorTransform &output);
|
void ColorSpaceChanged(const QString &input, const ColorTransform &output);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,66 +26,68 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
|
|
||||||
QDialog(parent)
|
|
||||||
{
|
{
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
|
||||||
|
|
||||||
QSplitter* splitter = new QSplitter();
|
ConfigDialogBase::ConfigDialogBase(QWidget *parent)
|
||||||
splitter->setChildrenCollapsible(false);
|
: QDialog(parent)
|
||||||
layout->addWidget(splitter);
|
{
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
list_widget_ = new QListWidget();
|
QSplitter *splitter = new QSplitter();
|
||||||
|
splitter->setChildrenCollapsible(false);
|
||||||
|
layout->addWidget(splitter);
|
||||||
|
|
||||||
preference_pane_stack_ = new QStackedWidget(this);
|
list_widget_ = new QListWidget();
|
||||||
|
|
||||||
splitter->addWidget(list_widget_);
|
preference_pane_stack_ = new QStackedWidget(this);
|
||||||
splitter->addWidget(preference_pane_stack_);
|
|
||||||
|
|
||||||
QDialogButtonBox* button_box = new QDialogButtonBox(this);
|
splitter->addWidget(list_widget_);
|
||||||
button_box->setOrientation(Qt::Horizontal);
|
splitter->addWidget(preference_pane_stack_);
|
||||||
button_box->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
|
||||||
|
|
||||||
layout->addWidget(button_box);
|
QDialogButtonBox *button_box = new QDialogButtonBox(this);
|
||||||
|
button_box->setOrientation(Qt::Horizontal);
|
||||||
|
button_box->setStandardButtons(QDialogButtonBox::Cancel |
|
||||||
|
QDialogButtonBox::Ok);
|
||||||
|
|
||||||
connect(button_box, &QDialogButtonBox::accepted, this, &ConfigDialogBase::accept);
|
layout->addWidget(button_box);
|
||||||
connect(button_box, &QDialogButtonBox::rejected, this, &ConfigDialogBase::reject);
|
|
||||||
|
|
||||||
connect(list_widget_,
|
connect(button_box, &QDialogButtonBox::accepted, this,
|
||||||
&QListWidget::currentRowChanged,
|
&ConfigDialogBase::accept);
|
||||||
preference_pane_stack_,
|
connect(button_box, &QDialogButtonBox::rejected, this,
|
||||||
&QStackedWidget::setCurrentIndex);
|
&ConfigDialogBase::reject);
|
||||||
|
|
||||||
|
connect(list_widget_, &QListWidget::currentRowChanged,
|
||||||
|
preference_pane_stack_, &QStackedWidget::setCurrentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigDialogBase::accept()
|
void ConfigDialogBase::accept()
|
||||||
{
|
{
|
||||||
foreach (ConfigDialogBaseTab* tab, tabs_) {
|
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
||||||
if (!tab->Validate()) {
|
if (!tab->Validate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiUndoCommand* command = new MultiUndoCommand();
|
MultiUndoCommand *command = new MultiUndoCommand();
|
||||||
|
|
||||||
foreach (ConfigDialogBaseTab* tab, tabs_) {
|
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
||||||
tab->Accept(command);
|
tab->Accept(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
|
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
|
||||||
|
|
||||||
AcceptEvent();
|
AcceptEvent();
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigDialogBase::AddTab(ConfigDialogBaseTab *tab, const QString &title)
|
void ConfigDialogBase::AddTab(ConfigDialogBaseTab *tab, const QString &title)
|
||||||
{
|
{
|
||||||
list_widget_->addItem(title);
|
list_widget_->addItem(title);
|
||||||
preference_pane_stack_->addWidget(tab);
|
preference_pane_stack_->addWidget(tab);
|
||||||
|
|
||||||
tabs_.append(tab);
|
tabs_.append(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,32 +27,33 @@
|
|||||||
|
|
||||||
#include "configdialogbasetab.h"
|
#include "configdialogbasetab.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ConfigDialogBase : public QDialog
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class ConfigDialogBase : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ConfigDialogBase(QWidget* parent = nullptr);
|
ConfigDialogBase(QWidget *parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
/**
|
/**
|
||||||
* @brief Override of accept to save preferences to Config.
|
* @brief Override of accept to save preferences to Config.
|
||||||
*/
|
*/
|
||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void AddTab(ConfigDialogBaseTab* tab, const QString& title);
|
void AddTab(ConfigDialogBaseTab *tab, const QString &title);
|
||||||
|
|
||||||
virtual void AcceptEvent(){}
|
virtual void AcceptEvent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QListWidget* list_widget_;
|
QListWidget *list_widget_;
|
||||||
|
|
||||||
QStackedWidget* preference_pane_stack_;
|
QStackedWidget *preference_pane_stack_;
|
||||||
|
|
||||||
QList<ConfigDialogBaseTab*> tabs_;
|
|
||||||
|
|
||||||
|
QList<ConfigDialogBaseTab *> tabs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,12 @@
|
|||||||
|
|
||||||
#include "configdialogbasetab.h"
|
#include "configdialogbasetab.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
bool ConfigDialogBaseTab::Validate()
|
bool ConfigDialogBaseTab::Validate()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,16 +26,16 @@
|
|||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "undo/undocommand.h"
|
#include "undo/undocommand.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ConfigDialogBaseTab : public QWidget
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ConfigDialogBaseTab : public QWidget {
|
||||||
public:
|
public:
|
||||||
ConfigDialogBaseTab() = default;
|
ConfigDialogBaseTab() = default;
|
||||||
|
|
||||||
virtual bool Validate();
|
virtual bool Validate();
|
||||||
|
|
||||||
virtual void Accept(MultiUndoCommand *parent) = 0;
|
virtual void Accept(MultiUndoCommand *parent) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,86 +28,101 @@
|
|||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) :
|
|
||||||
QDialog(parent),
|
|
||||||
folder_(folder)
|
|
||||||
{
|
{
|
||||||
QGridLayout* layout = new QGridLayout(this);
|
|
||||||
|
|
||||||
int row = 0;
|
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, folder_(folder)
|
||||||
|
{
|
||||||
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())), row, 0, 1, 2);
|
int row = 0;
|
||||||
setWindowTitle(tr("Disk Cache Settings"));
|
|
||||||
|
|
||||||
row++;
|
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())),
|
||||||
|
row, 0, 1, 2);
|
||||||
|
setWindowTitle(tr("Disk Cache Settings"));
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
|
row++;
|
||||||
|
|
||||||
maximum_cache_slider_ = new FloatSlider();
|
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
|
||||||
maximum_cache_slider_->SetFormat(tr("%1 GB"));
|
|
||||||
maximum_cache_slider_->SetMinimum(1.0);
|
|
||||||
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) / static_cast<double>(kBytesInGigabyte));
|
|
||||||
layout->addWidget(maximum_cache_slider_, row, 1);
|
|
||||||
|
|
||||||
row++;
|
maximum_cache_slider_ = new FloatSlider();
|
||||||
|
maximum_cache_slider_->SetFormat(tr("%1 GB"));
|
||||||
|
maximum_cache_slider_->SetMinimum(1.0);
|
||||||
|
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) /
|
||||||
|
static_cast<double>(kBytesInGigabyte));
|
||||||
|
layout->addWidget(maximum_cache_slider_, row, 1);
|
||||||
|
|
||||||
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
|
row++;
|
||||||
connect(clear_cache_btn_, &QPushButton::clicked, this, static_cast<void(DiskCacheDialog::*)()>(&DiskCacheDialog::ClearDiskCache));
|
|
||||||
layout->addWidget(clear_cache_btn_, row, 1);
|
|
||||||
|
|
||||||
row++;
|
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
|
||||||
|
connect(clear_cache_btn_, &QPushButton::clicked, this,
|
||||||
|
static_cast<void (DiskCacheDialog::*)()>(
|
||||||
|
&DiskCacheDialog::ClearDiskCache));
|
||||||
|
layout->addWidget(clear_cache_btn_, row, 1);
|
||||||
|
|
||||||
clear_disk_cache_ = new QCheckBox(tr("Automatically clear disk cache on close"));
|
row++;
|
||||||
clear_disk_cache_->setChecked(folder->GetClearOnClose());
|
|
||||||
layout->addWidget(clear_disk_cache_, row, 1);
|
|
||||||
|
|
||||||
row++;
|
clear_disk_cache_ =
|
||||||
|
new QCheckBox(tr("Automatically clear disk cache on close"));
|
||||||
|
clear_disk_cache_->setChecked(folder->GetClearOnClose());
|
||||||
|
layout->addWidget(clear_disk_cache_, row, 1);
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
row++;
|
||||||
connect(buttons, &QDialogButtonBox::accepted, this, &DiskCacheDialog::accept);
|
|
||||||
connect(buttons, &QDialogButtonBox::rejected, this, &DiskCacheDialog::reject);
|
QDialogButtonBox *buttons =
|
||||||
layout->addWidget(buttons, row, 0, 1, 2);
|
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||||
|
&DiskCacheDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||||
|
&DiskCacheDialog::reject);
|
||||||
|
layout->addWidget(buttons, row, 0, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskCacheDialog::accept()
|
void DiskCacheDialog::accept()
|
||||||
{
|
{
|
||||||
qint64 new_disk_cache_limit = qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
|
qint64 new_disk_cache_limit =
|
||||||
if (new_disk_cache_limit != folder_->GetLimit()) {
|
qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
|
||||||
folder_->SetLimit(new_disk_cache_limit);
|
if (new_disk_cache_limit != folder_->GetLimit()) {
|
||||||
}
|
folder_->SetLimit(new_disk_cache_limit);
|
||||||
|
}
|
||||||
|
|
||||||
if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) {
|
if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) {
|
||||||
folder_->SetClearOnClose(clear_disk_cache_->isChecked());
|
folder_->SetClearOnClose(clear_disk_cache_->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskCacheDialog::ClearDiskCache()
|
void DiskCacheDialog::ClearDiskCache()
|
||||||
{
|
{
|
||||||
ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_);
|
ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn)
|
void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent,
|
||||||
|
QPushButton *clear_btn)
|
||||||
{
|
{
|
||||||
if (QMessageBox::question(parent,
|
if (QMessageBox::question(
|
||||||
tr("Clear Disk Cache"),
|
parent, tr("Clear Disk Cache"),
|
||||||
tr("Are you sure you want to clear the disk cache in '%1'?").arg(path),
|
tr("Are you sure you want to clear the disk cache in '%1'?")
|
||||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
.arg(path),
|
||||||
if (clear_btn) clear_btn->setEnabled(false);
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||||
|
if (clear_btn)
|
||||||
|
clear_btn->setEnabled(false);
|
||||||
|
|
||||||
if (DiskManager::instance()->ClearDiskCache(path)) {
|
if (DiskManager::instance()->ClearDiskCache(path)) {
|
||||||
if (clear_btn) clear_btn->setText(tr("Disk Cache Cleared"));
|
if (clear_btn)
|
||||||
} else {
|
clear_btn->setText(tr("Disk Cache Cleared"));
|
||||||
QMessageBox::information(parent,
|
} else {
|
||||||
tr("Clear Disk Cache"),
|
QMessageBox::information(
|
||||||
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
|
parent, tr("Clear Disk Cache"),
|
||||||
QMessageBox::Ok);
|
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
|
||||||
if (clear_btn) clear_btn->setText(tr("Disk Cache Partially Cleared"));
|
QMessageBox::Ok);
|
||||||
}
|
if (clear_btn)
|
||||||
}
|
clear_btn->setText(tr("Disk Cache Partially Cleared"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,31 +28,31 @@
|
|||||||
#include "render/diskmanager.h"
|
#include "render/diskmanager.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class DiskCacheDialog : public QDialog
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
DiskCacheDialog(DiskCacheFolder* folder, QWidget* parent = nullptr);
|
|
||||||
|
|
||||||
static void ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn = nullptr);
|
class DiskCacheDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
static void ClearDiskCache(const QString &path, QWidget *parent,
|
||||||
|
QPushButton *clear_btn = nullptr);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DiskCacheFolder* folder_;
|
DiskCacheFolder *folder_;
|
||||||
|
|
||||||
FloatSlider* maximum_cache_slider_;
|
FloatSlider *maximum_cache_slider_;
|
||||||
|
|
||||||
QCheckBox* clear_disk_cache_;
|
QCheckBox *clear_disk_cache_;
|
||||||
|
|
||||||
QPushButton* clear_cache_btn_;
|
QPushButton *clear_cache_btn_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ClearDiskCache();
|
void ClearDiskCache();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,105 +28,111 @@
|
|||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
AV1Section::AV1Section(QWidget *parent) :
|
AV1Section::AV1Section(QWidget *parent)
|
||||||
AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
|
: AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AV1Section::AV1Section(int default_crf, QWidget *parent) :
|
AV1Section::AV1Section(int default_crf, QWidget *parent)
|
||||||
CodecSection(parent)
|
: CodecSection(parent)
|
||||||
{
|
{
|
||||||
QGridLayout* layout = new QGridLayout(this);
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
layout->addWidget(new QLabel(tr("Preset:")), row, 0);
|
layout->addWidget(new QLabel(tr("Preset:")), row, 0);
|
||||||
|
|
||||||
preset_combobox_ = new QComboBox();
|
preset_combobox_ = new QComboBox();
|
||||||
preset_combobox_->setToolTip(tr("This parameter governs the efficiency/encode-time trade-off.\n"
|
preset_combobox_->setToolTip(tr(
|
||||||
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
|
"This parameter governs the efficiency/encode-time trade-off.\n"
|
||||||
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
|
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
|
||||||
|
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
|
||||||
|
|
||||||
for (int i = 0; i <= 13; i++)
|
for (int i = 0; i <= 13; i++)
|
||||||
preset_combobox_->addItem(QString::number(i));
|
preset_combobox_->addItem(QString::number(i));
|
||||||
|
|
||||||
preset_combobox_->setCurrentIndex(8);
|
preset_combobox_->setCurrentIndex(8);
|
||||||
|
|
||||||
layout->addWidget(preset_combobox_, row, 1);
|
layout->addWidget(preset_combobox_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
||||||
|
|
||||||
QComboBox* compression_box = new QComboBox();
|
QComboBox *compression_box = new QComboBox();
|
||||||
compression_box->setToolTip(tr("This parameter governs the quality/size trade-off.\n"
|
compression_box->setToolTip(tr(
|
||||||
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
|
"This parameter governs the quality/size trade-off.\n"
|
||||||
"Lower CRF values retain more detail at the cost of larger file sizes.\n"
|
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
|
||||||
"The possible range of CRF in SVT-AV1 is 1-63."));
|
"Lower CRF values retain more detail at the cost of larger file sizes.\n"
|
||||||
|
"The possible range of CRF in SVT-AV1 is 1-63."));
|
||||||
|
|
||||||
// These items must correspond to the CompressionMethod enum
|
// These items must correspond to the CompressionMethod enum
|
||||||
compression_box->addItem(tr("Constant Rate Factor"));
|
compression_box->addItem(tr("Constant Rate Factor"));
|
||||||
|
|
||||||
layout->addWidget(compression_box, row, 1);
|
layout->addWidget(compression_box, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
compression_method_stack_ = new QStackedWidget();
|
compression_method_stack_ = new QStackedWidget();
|
||||||
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
||||||
|
|
||||||
crf_section_ = new AV1CRFSection(default_crf);
|
crf_section_ = new AV1CRFSection(default_crf);
|
||||||
compression_method_stack_->addWidget(crf_section_);
|
compression_method_stack_->addWidget(crf_section_);
|
||||||
|
|
||||||
connect(compression_box,
|
connect(
|
||||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
compression_box,
|
||||||
compression_method_stack_,
|
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||||
&QStackedWidget::setCurrentIndex);
|
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AV1Section::AddOpts(EncodingParams *params)
|
void AV1Section::AddOpts(EncodingParams *params)
|
||||||
{
|
{
|
||||||
|
CompressionMethod method = static_cast<CompressionMethod>(
|
||||||
|
compression_method_stack_->currentIndex());
|
||||||
|
|
||||||
CompressionMethod method = static_cast<CompressionMethod>(compression_method_stack_->currentIndex());
|
if (method == kConstantRateFactor) {
|
||||||
|
// Set Quantizer value
|
||||||
|
params->set_video_option(QStringLiteral("qp"),
|
||||||
|
QString::number(crf_section_->GetValue()));
|
||||||
|
}
|
||||||
|
|
||||||
if (method == kConstantRateFactor) {
|
params->set_video_option(QStringLiteral("preset"),
|
||||||
|
QString::number(preset_combobox_->currentIndex()));
|
||||||
// Set Quantizer value
|
|
||||||
params->set_video_option(QStringLiteral("qp"), QString::number(crf_section_->GetValue()));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) :
|
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
|
||||||
QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
crf_slider_ = new QSlider(Qt::Horizontal);
|
crf_slider_ = new QSlider(Qt::Horizontal);
|
||||||
crf_slider_->setMinimum(kMinimumCRF);
|
crf_slider_->setMinimum(kMinimumCRF);
|
||||||
crf_slider_->setMaximum(kMaximumCRF);
|
crf_slider_->setMaximum(kMaximumCRF);
|
||||||
crf_slider_->setValue(default_crf);
|
crf_slider_->setValue(default_crf);
|
||||||
layout->addWidget(crf_slider_);
|
layout->addWidget(crf_slider_);
|
||||||
|
|
||||||
IntegerSlider* crf_input = new IntegerSlider();
|
IntegerSlider *crf_input = new IntegerSlider();
|
||||||
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(
|
||||||
crf_input->SetMinimum(kMinimumCRF);
|
crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
||||||
crf_input->SetMaximum(kMaximumCRF);
|
crf_input->SetMinimum(kMinimumCRF);
|
||||||
crf_input->SetValue(default_crf);
|
crf_input->SetMaximum(kMaximumCRF);
|
||||||
crf_input->SetDefaultValue(default_crf);
|
crf_input->SetValue(default_crf);
|
||||||
layout->addWidget(crf_input);
|
crf_input->SetDefaultValue(default_crf);
|
||||||
|
layout->addWidget(crf_input);
|
||||||
|
|
||||||
connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue);
|
connect(crf_slider_, &QSlider::valueChanged, crf_input,
|
||||||
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_, &QSlider::setValue);
|
&IntegerSlider::SetValue);
|
||||||
|
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_,
|
||||||
|
&QSlider::setValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
int AV1CRFSection::GetValue() const
|
int AV1CRFSection::GetValue() const
|
||||||
{
|
{
|
||||||
return crf_slider_->value();
|
return crf_slider_->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,45 +28,43 @@
|
|||||||
#include "codecsection.h"
|
#include "codecsection.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class AV1CRFSection : public QWidget
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class AV1CRFSection : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AV1CRFSection(int default_crf, QWidget* parent = nullptr);
|
AV1CRFSection(int default_crf, QWidget *parent = nullptr);
|
||||||
|
|
||||||
int GetValue() const;
|
int GetValue() const;
|
||||||
|
|
||||||
static const int kDefaultAV1CRF = 30;
|
static const int kDefaultAV1CRF = 30;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kMinimumCRF = 0;
|
static const int kMinimumCRF = 0;
|
||||||
static const int kMaximumCRF = 63;
|
static const int kMaximumCRF = 63;
|
||||||
|
|
||||||
QSlider* crf_slider_;
|
|
||||||
|
|
||||||
|
QSlider *crf_slider_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AV1Section : public CodecSection
|
class AV1Section : public CodecSection {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
enum CompressionMethod {
|
enum CompressionMethod {
|
||||||
kConstantRateFactor,
|
kConstantRateFactor,
|
||||||
};
|
};
|
||||||
|
|
||||||
AV1Section(QWidget* parent = nullptr);
|
AV1Section(QWidget *parent = nullptr);
|
||||||
AV1Section(int default_crf, QWidget* parent);
|
AV1Section(int default_crf, QWidget *parent);
|
||||||
|
|
||||||
virtual void AddOpts(EncodingParams* params) override;
|
virtual void AddOpts(EncodingParams *params) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStackedWidget* compression_method_stack_;
|
QStackedWidget *compression_method_stack_;
|
||||||
|
|
||||||
AV1CRFSection* crf_section_;
|
AV1CRFSection *crf_section_;
|
||||||
|
|
||||||
QComboBox *preset_combobox_;
|
QComboBox *preset_combobox_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,22 +23,23 @@
|
|||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
CineformSection::CineformSection(QWidget *parent) :
|
|
||||||
CodecSection(parent)
|
|
||||||
{
|
{
|
||||||
QGridLayout *layout = new QGridLayout(this);
|
|
||||||
|
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
CineformSection::CineformSection(QWidget *parent)
|
||||||
|
: CodecSection(parent)
|
||||||
|
{
|
||||||
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
|
|
||||||
int row = 0;
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
int row = 0;
|
||||||
|
|
||||||
quality_combobox_ = new QComboBox();
|
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||||
|
|
||||||
/* Correspond to the following indexes for FFmpeg
|
quality_combobox_ = new QComboBox();
|
||||||
|
|
||||||
|
/* Correspond to the following indexes for FFmpeg
|
||||||
*
|
*
|
||||||
* -quality <int> E..V....... set quality (from 0 to 12) (default film3+)
|
* -quality <int> E..V....... set quality (from 0 to 12) (default film3+)
|
||||||
* film3+ 0 E..V.......
|
* film3+ 0 E..V.......
|
||||||
@@ -57,34 +58,37 @@ CineformSection::CineformSection(QWidget *parent) :
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
quality_combobox_->addItem(tr("Film Scan 3+"));
|
quality_combobox_->addItem(tr("Film Scan 3+"));
|
||||||
quality_combobox_->addItem(tr("Film Scan 3"));
|
quality_combobox_->addItem(tr("Film Scan 3"));
|
||||||
quality_combobox_->addItem(tr("Film Scan 2+"));
|
quality_combobox_->addItem(tr("Film Scan 2+"));
|
||||||
quality_combobox_->addItem(tr("Film Scan 2"));
|
quality_combobox_->addItem(tr("Film Scan 2"));
|
||||||
quality_combobox_->addItem(tr("Film Scan 1.5"));
|
quality_combobox_->addItem(tr("Film Scan 1.5"));
|
||||||
quality_combobox_->addItem(tr("Film Scan 1+"));
|
quality_combobox_->addItem(tr("Film Scan 1+"));
|
||||||
quality_combobox_->addItem(tr("Film Scan 1"));
|
quality_combobox_->addItem(tr("Film Scan 1"));
|
||||||
quality_combobox_->addItem(tr("High+"));
|
quality_combobox_->addItem(tr("High+"));
|
||||||
quality_combobox_->addItem(tr("High"));
|
quality_combobox_->addItem(tr("High"));
|
||||||
quality_combobox_->addItem(tr("Medium+"));
|
quality_combobox_->addItem(tr("Medium+"));
|
||||||
quality_combobox_->addItem(tr("Medium"));
|
quality_combobox_->addItem(tr("Medium"));
|
||||||
quality_combobox_->addItem(tr("Low+"));
|
quality_combobox_->addItem(tr("Low+"));
|
||||||
quality_combobox_->addItem(tr("Low"));
|
quality_combobox_->addItem(tr("Low"));
|
||||||
|
|
||||||
// Default to "medium"
|
// Default to "medium"
|
||||||
quality_combobox_->setCurrentIndex(10);
|
quality_combobox_->setCurrentIndex(10);
|
||||||
|
|
||||||
layout->addWidget(quality_combobox_, row, 1);
|
layout->addWidget(quality_combobox_, row, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CineformSection::AddOpts(EncodingParams *params)
|
void CineformSection::AddOpts(EncodingParams *params)
|
||||||
{
|
{
|
||||||
params->set_video_option(QStringLiteral("quality"), QString::number(quality_combobox_->currentIndex()));
|
params->set_video_option(
|
||||||
|
QStringLiteral("quality"),
|
||||||
|
QString::number(quality_combobox_->currentIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CineformSection::SetOpts(const EncodingParams *p)
|
void CineformSection::SetOpts(const EncodingParams *p)
|
||||||
{
|
{
|
||||||
quality_combobox_->setCurrentIndex(p->video_option(QStringLiteral("quality")).toInt());
|
quality_combobox_->setCurrentIndex(
|
||||||
|
p->video_option(QStringLiteral("quality")).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,21 +25,20 @@
|
|||||||
|
|
||||||
#include "codecsection.h"
|
#include "codecsection.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CineformSection : public CodecSection
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class CineformSection : public CodecSection {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CineformSection(QWidget *parent = nullptr);
|
CineformSection(QWidget *parent = nullptr);
|
||||||
|
|
||||||
virtual void AddOpts(EncodingParams* params) override;
|
virtual void AddOpts(EncodingParams *params) override;
|
||||||
|
|
||||||
virtual void SetOpts(const EncodingParams *p) override;
|
virtual void SetOpts(const EncodingParams *p) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QComboBox *quality_combobox_;
|
QComboBox *quality_combobox_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,11 @@
|
|||||||
|
|
||||||
#include "codecsection.h"
|
#include "codecsection.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
CodecSection::CodecSection(QWidget *parent) :
|
CodecSection::CodecSection(QWidget *parent)
|
||||||
QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,18 +25,23 @@
|
|||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "codec/encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CodecSection : public QWidget
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
class CodecSection : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CodecSection(QWidget* parent = nullptr);
|
CodecSection(QWidget *parent = nullptr);
|
||||||
|
|
||||||
virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)}
|
virtual void AddOpts(EncodingParams *params)
|
||||||
|
{
|
||||||
virtual void SetOpts(const EncodingParams *p){Q_UNUSED(p)}
|
Q_UNUSED(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetOpts(const EncodingParams *p)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user