change: change code style to Linux style except indent.
This commit is contained in:
+55
-87
@@ -1,31 +1,39 @@
|
|||||||
|
# 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
|
||||||
@@ -37,43 +45,33 @@ BraceWrapping:
|
|||||||
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:
|
|
||||||
- foreach
|
|
||||||
- Q_FOREACH
|
|
||||||
- BOOST_FOREACH
|
|
||||||
IncludeBlocks: Preserve
|
IncludeBlocks: Preserve
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
- Regex: '^<ext/.*\.h>'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '^<.*\.h>'
|
|
||||||
Priority: 1
|
|
||||||
- Regex: '^<.*'
|
|
||||||
Priority: 2
|
|
||||||
- Regex: '.*'
|
- Regex: '.*'
|
||||||
Priority: 3
|
Priority: 1
|
||||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
IndentCaseLabels: true
|
IndentCaseLabels: false
|
||||||
|
IndentGotoLabels: false
|
||||||
IndentPPDirectives: None
|
IndentPPDirectives: None
|
||||||
IndentWidth: 2
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
JavaScriptWrapImports: true
|
JavaScriptWrapImports: true
|
||||||
@@ -82,67 +80,37 @@ 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'
|
||||||
+57
-28
@@ -28,7 +28,8 @@
|
|||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
AudioManager *AudioManager::instance_ = nullptr;
|
AudioManager *AudioManager::instance_ = nullptr;
|
||||||
|
|
||||||
@@ -55,35 +56,45 @@ 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 =
|
||||||
|
device->read(reinterpret_cast<char *>(output), max_read);
|
||||||
if (read_count < max_read) {
|
if (read_count < max_read) {
|
||||||
memset(reinterpret_cast<uint8_t*>(output) + read_count, 0, max_read - read_count);
|
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)
|
||||||
|
*error = tr("No output device is set");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,11 +105,15 @@ bool AudioManager::PushToOutput(const AudioParams ¶ms, const QByteArray &sam
|
|||||||
|
|
||||||
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,
|
||||||
|
output_params_.sample_rate(),
|
||||||
|
paFramesPerBufferUnspecified, paNoFlag,
|
||||||
|
OutputCallback, output_buffer_);
|
||||||
if (r != paNoError) {
|
if (r != paNoError) {
|
||||||
// Unhandled error
|
// Unhandled error
|
||||||
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
|
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
|
||||||
if (error) *error = Pa_GetErrorText(r);
|
if (error)
|
||||||
|
*error = Pa_GetErrorText(r);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +186,8 @@ 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;
|
||||||
@@ -184,7 +200,8 @@ 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;
|
||||||
@@ -197,7 +214,8 @@ void AudioManager::HardReset()
|
|||||||
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;
|
||||||
@@ -209,9 +227,13 @@ bool AudioManager::StartRecording(const EncodingParams ¶ms, QString *error_s
|
|||||||
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,
|
||||||
|
params.audio_params().sample_rate(),
|
||||||
|
paFramesPerBufferUnspecified, paNoFlag,
|
||||||
|
InputCallback, input_encoder_);
|
||||||
if (r == paNoError) {
|
if (r == paNoError) {
|
||||||
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
|
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
|
||||||
r = Pa_StartStream(input_stream_);
|
r = Pa_StartStream(input_stream_);
|
||||||
@@ -248,28 +270,34 @@ void AudioManager::StopRecording()
|
|||||||
|
|
||||||
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)) &&
|
||||||
|
!s.compare(device->name)) {
|
||||||
return i;
|
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;
|
||||||
|
|
||||||
@@ -282,10 +310,10 @@ PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams ¶ms, P
|
|||||||
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
|
||||||
@@ -304,7 +332,8 @@ AudioManager::AudioManager() :
|
|||||||
|
|
||||||
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()
|
||||||
|
|||||||
@@ -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,8 +42,7 @@ 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();
|
||||||
@@ -52,7 +52,8 @@ public:
|
|||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -74,14 +75,17 @@ public:
|
|||||||
|
|
||||||
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();
|
||||||
@@ -108,7 +112,6 @@ private:
|
|||||||
PaStream *input_stream_;
|
PaStream *input_stream_;
|
||||||
|
|
||||||
FFmpegEncoder *input_encoder_;
|
FFmpegEncoder *input_encoder_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ extern "C" {
|
|||||||
|
|
||||||
#include "common/ffmpegutils.h"
|
#include "common/ffmpegutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
AudioProcessor::AudioProcessor()
|
AudioProcessor::AudioProcessor()
|
||||||
{
|
{
|
||||||
@@ -43,7 +44,8 @@ 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";
|
||||||
@@ -61,17 +63,18 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
|
|||||||
|
|
||||||
// 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_,
|
||||||
|
avfilter_get_by_name("abuffer"), "in",
|
||||||
|
filter_args, nullptr, filter_graph_);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to create buffersrc:" << r;
|
qCritical() << "Failed to create buffersrc:" << r;
|
||||||
Close();
|
Close();
|
||||||
@@ -96,16 +99,16 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
|
|||||||
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";
|
||||||
@@ -118,19 +121,24 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
|
|||||||
// 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() ||
|
||||||
|
(to.format().is_planar() &&
|
||||||
|
create_tempo)) { // Tempo processor automatically converts to packed,
|
||||||
// so if the desired output is planar, it'll need
|
// so if the desired output is planar, it'll need
|
||||||
// to be converted
|
// to be converted
|
||||||
snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
|
snprintf(filter_args, 200,
|
||||||
av_get_sample_fmt_name(to_fmt_),
|
"sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
|
||||||
to.sample_rate(),
|
av_get_sample_fmt_name(to_fmt_), to.sample_rate(),
|
||||||
to.channel_layout().u.mask);
|
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"),
|
||||||
|
"fmt", filter_args, nullptr,
|
||||||
|
filter_graph_);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to create format conversion filter:" << r << filter_args;
|
qCritical() << "Failed to create format conversion filter:" << r
|
||||||
|
<< filter_args;
|
||||||
Close();
|
Close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -146,7 +154,9 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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_,
|
||||||
|
avfilter_get_by_name("abuffersink"), "out",
|
||||||
|
nullptr, nullptr, filter_graph_);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to create buffersink:" << r;
|
qCritical() << "Failed to create buffersink:" << r;
|
||||||
Close();
|
Close();
|
||||||
@@ -214,7 +224,8 @@ void AudioProcessor::Close()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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";
|
||||||
@@ -231,7 +242,8 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe
|
|||||||
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_,
|
||||||
|
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to add frame to buffersrc:" << r;
|
qCritical() << "Failed to add frame to buffersrc:" << r;
|
||||||
return r;
|
return r;
|
||||||
@@ -263,14 +275,16 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nb_bytes = out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
|
int nb_bytes =
|
||||||
|
out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
|
||||||
if (to_.format().is_packed()) {
|
if (to_.format().is_packed()) {
|
||||||
nb_bytes *= to_.channel_count();
|
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;
|
||||||
}
|
}
|
||||||
@@ -282,13 +296,16 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe
|
|||||||
|
|
||||||
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,
|
||||||
|
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to flush:" << r;
|
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];
|
||||||
@@ -296,8 +313,10 @@ AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilte
|
|||||||
|
|
||||||
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,
|
||||||
|
graph) >= 0 &&
|
||||||
|
avfilter_link(link, 0, tempo_ctx, 0) == 0) {
|
||||||
return tempo_ctx;
|
return tempo_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ 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();
|
||||||
|
|
||||||
@@ -44,22 +44,34 @@ public:
|
|||||||
|
|
||||||
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_;
|
||||||
|
|
||||||
@@ -76,7 +88,6 @@ private:
|
|||||||
AVFrame *in_frame_;
|
AVFrame *in_frame_;
|
||||||
|
|
||||||
AVFrame *out_frame_;
|
AVFrame *out_frame_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,23 +25,30 @@
|
|||||||
|
|
||||||
#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) {
|
||||||
@@ -52,22 +59,26 @@ void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples
|
|||||||
|
|
||||||
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) {
|
||||||
@@ -78,10 +89,11 @@ void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::
|
|||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +110,9 @@ void AudioVisualWaveform::ValidateVirtualStart(const rational &new_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";
|
||||||
@@ -110,7 +124,10 @@ void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int samp
|
|||||||
// 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
|
||||||
@@ -121,16 +138,20 @@ void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int samp
|
|||||||
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);
|
||||||
|
|
||||||
@@ -143,7 +164,8 @@ void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const r
|
|||||||
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);
|
||||||
@@ -166,15 +188,19 @@ void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const r
|
|||||||
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),
|
||||||
|
reinterpret_cast<const char *>(their_arr.data()) +
|
||||||
|
their_start_index * sizeof(SamplePerChannel),
|
||||||
copy_len * 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);
|
||||||
|
|
||||||
@@ -186,7 +212,8 @@ void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational
|
|||||||
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 =
|
||||||
|
time_to_samples(start - virtual_start_, rate_dbl);
|
||||||
size_t our_length_index = time_to_samples(length, rate_dbl);
|
size_t our_length_index = time_to_samples(length, rate_dbl);
|
||||||
size_t our_end_index = our_start_index + our_length_index;
|
size_t our_end_index = our_start_index + our_length_index;
|
||||||
|
|
||||||
@@ -194,7 +221,9 @@ void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational
|
|||||||
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);
|
||||||
@@ -242,7 +271,8 @@ AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const
|
|||||||
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;
|
||||||
|
|
||||||
@@ -276,7 +306,9 @@ void AudioVisualWaveform::TrimRange(const rational &in, const rational &length)
|
|||||||
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());
|
||||||
@@ -294,14 +326,16 @@ AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const ration
|
|||||||
// 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
|
||||||
@@ -344,13 +378,18 @@ void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &m
|
|||||||
#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
|
||||||
@@ -361,15 +400,16 @@ AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &
|
|||||||
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;
|
||||||
@@ -384,13 +424,13 @@ AudioVisualWaveform::Sample AudioVisualWaveform::ReSumSamples(const SamplePerCha
|
|||||||
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;
|
||||||
@@ -408,24 +448,29 @@ void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample& sample, in
|
|||||||
|
|
||||||
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,
|
|
||||||
x,
|
|
||||||
channel_bottom);
|
|
||||||
} else {
|
} else {
|
||||||
int channel_mid = y + channel_height * i + channel_half_height;
|
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 - round_away_from_zero(max * static_cast<float>(channel_half_height)));
|
channel_mid -
|
||||||
|
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;
|
||||||
@@ -437,7 +482,8 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
|
|||||||
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;
|
||||||
@@ -464,12 +510,18 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
|
|||||||
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),
|
||||||
|
qMax(size_t(samples.channel_count()),
|
||||||
|
next_sample_index - sample_index),
|
||||||
samples.channel_count());
|
samples.channel_count());
|
||||||
summary_index = sample_index;
|
summary_index = sample_index;
|
||||||
}
|
}
|
||||||
@@ -478,20 +530,24 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
it++) {
|
||||||
if (it->first.toDouble() >= scale) {
|
if (it->first.toDouble() >= scale) {
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
@@ -66,7 +67,8 @@ public:
|
|||||||
*
|
*
|
||||||
* 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
|
||||||
@@ -87,42 +89,60 @@ 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);
|
||||||
|
|
||||||
@@ -133,7 +153,6 @@ private:
|
|||||||
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,10 +23,10 @@
|
|||||||
|
|
||||||
#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,13 +22,14 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) :
|
CLIProgressDialog::CLIProgressDialog(const QString &title, QObject *parent)
|
||||||
QObject(parent),
|
: QObject(parent)
|
||||||
title_(title),
|
, title_(title)
|
||||||
progress_(-1),
|
, progress_(-1)
|
||||||
drawn_(false)
|
, drawn_(false)
|
||||||
{
|
{
|
||||||
SetProgress(0);
|
SetProgress(0);
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,8 @@ void CLIProgressDialog::Update()
|
|||||||
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 =
|
||||||
|
title_.left(title_columns - 3).append(QStringLiteral("..."));
|
||||||
} else {
|
} else {
|
||||||
sized_title = title_;
|
sized_title = title_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
@@ -44,7 +44,6 @@ private:
|
|||||||
double progress_;
|
double progress_;
|
||||||
|
|
||||||
bool drawn_;
|
bool drawn_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,12 @@
|
|||||||
|
|
||||||
#include "clitaskdialog.h"
|
#include "clitaskdialog.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) :
|
CLITaskDialog::CLITaskDialog(Task *task, QObject *parent)
|
||||||
CLIProgressDialog(task->GetTitle(), parent),
|
: CLIProgressDialog(task->GetTitle(), parent)
|
||||||
task_(task)
|
, task_(task)
|
||||||
{
|
{
|
||||||
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
|
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,10 @@
|
|||||||
#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
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CLITaskDialog : public CLIProgressDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CLITaskDialog(Task *task, QObject *parent = nullptr);
|
CLITaskDialog(Task *task, QObject *parent = nullptr);
|
||||||
@@ -36,7 +36,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Task *task_;
|
Task *task_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,21 @@
|
|||||||
|
|
||||||
#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 =
|
||||||
|
GetConformedFilename(cache_path, stream, params);
|
||||||
if (AllConformsExist(filenames)) {
|
if (AllConformsExist(filenames)) {
|
||||||
return { kConformExists, filenames, nullptr };
|
return { kConformExists, filenames, nullptr };
|
||||||
}
|
}
|
||||||
@@ -39,12 +43,17 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_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);
|
||||||
|
connect(conforming_task, &ConformTask::Finished, this,
|
||||||
|
&ConformManager::ConformTaskFinished);
|
||||||
conforming_task->moveToThread(TaskManager::instance()->thread());
|
conforming_task->moveToThread(TaskManager::instance()->thread());
|
||||||
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task));
|
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) {
|
||||||
@@ -57,12 +66,17 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
|
|||||||
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 =
|
||||||
|
QStringLiteral("%1-%2.%3.%4.%5.%6.pcm")
|
||||||
|
.arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
|
||||||
QString::number(stream.stream()),
|
QString::number(stream.stream()),
|
||||||
QString::number(params.sample_rate()),
|
QString::number(params.sample_rate()),
|
||||||
QString::number(params.format()),
|
QString::number(params.format()),
|
||||||
|
|||||||
+13
-11
@@ -7,10 +7,10 @@
|
|||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "task/conform/conform.h"
|
#include "task/conform/conform.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ConformManager : public QObject
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ConformManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
static void CreateInstance()
|
static void CreateInstance()
|
||||||
@@ -31,10 +31,7 @@ public:
|
|||||||
return instance_;
|
return instance_;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConformState {
|
enum ConformState { kConformExists, kConformGenerating };
|
||||||
kConformExists,
|
|
||||||
kConformGenerating
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Conform {
|
struct Conform {
|
||||||
ConformState state;
|
ConformState state;
|
||||||
@@ -47,7 +44,10 @@ public:
|
|||||||
*
|
*
|
||||||
* 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();
|
||||||
@@ -74,15 +74,17 @@ private:
|
|||||||
/**
|
/**
|
||||||
* @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
|
||||||
|
|||||||
+55
-25
@@ -33,12 +33,13 @@
|
|||||||
#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();
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,8 @@ bool Decoder::Open(const CodecStream &stream)
|
|||||||
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()
|
||||||
|
<< "Tried to open a decoder that was already open with another stream";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -84,7 +86,8 @@ bool Decoder::Open(const CodecStream &stream)
|
|||||||
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"
|
||||||
|
<< stream_.stream();
|
||||||
CloseInternal();
|
CloseInternal();
|
||||||
stream_.Reset();
|
stream_.Reset();
|
||||||
return false;
|
return false;
|
||||||
@@ -112,7 +115,8 @@ TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cached_texture_ && cached_time_ == p.time && cached_divider_ == p.divider) {
|
if (cached_texture_ && cached_time_ == p.time &&
|
||||||
|
cached_divider_ == p.divider) {
|
||||||
return cached_texture_;
|
return cached_texture_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +127,10 @@ TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
|
|||||||
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_);
|
||||||
|
|
||||||
@@ -140,14 +147,17 @@ Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const Ti
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 =
|
||||||
|
ConformManager::instance()->GetConformState(
|
||||||
|
id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
|
||||||
if (conform.state == ConformManager::kConformGenerating) {
|
if (conform.state == ConformManager::kConformGenerating) {
|
||||||
// If we need the task, it's available in `conform.task`
|
// If we need the task, it's available in `conform.task`
|
||||||
return kWaitingForConform;
|
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,
|
||||||
|
params)) {
|
||||||
return kOK;
|
return kOK;
|
||||||
} else {
|
} else {
|
||||||
return kUnknownError;
|
return kUnknownError;
|
||||||
@@ -175,7 +185,8 @@ void Decoder::Close()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -217,11 +228,13 @@ DecoderPtr Decoder::CreateFromID(const QString &id)
|
|||||||
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);
|
||||||
|
|
||||||
@@ -229,10 +242,13 @@ QString Decoder::TransformImageSequenceFileName(const QString &filename, const i
|
|||||||
|
|
||||||
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)
|
||||||
@@ -261,7 +277,8 @@ int64_t Decoder::GetImageSequenceIndex(const QString &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();
|
||||||
}
|
}
|
||||||
@@ -272,7 +289,9 @@ TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &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)
|
||||||
@@ -280,17 +299,22 @@ bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const Audi
|
|||||||
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()) /
|
||||||
|
input_params.channel_count();
|
||||||
qint64 write_index = 0;
|
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) {
|
||||||
@@ -308,15 +332,20 @@ bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVecto
|
|||||||
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,
|
||||||
|
write_index + write_count);
|
||||||
} else if (read_index >= input.size()) {
|
} else if (read_index >= input.size()) {
|
||||||
// Reading after data length, write silence until the end of the buffer
|
// Reading after data length, write silence until the end of the buffer
|
||||||
write_count = buffer_length_in_bytes - write_index;
|
write_count = buffer_length_in_bytes - write_index;
|
||||||
sample_buffer.silence_bytes(write_index, write_index + write_count);
|
sample_buffer.silence_bytes(write_index,
|
||||||
|
write_index + write_count);
|
||||||
} else {
|
} else {
|
||||||
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
|
write_count = qMin(input.size() - read_index,
|
||||||
|
buffer_length_in_bytes - write_index);
|
||||||
input.seek(read_index);
|
input.seek(read_index);
|
||||||
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index);
|
input.read(reinterpret_cast<char **>(
|
||||||
|
sample_buffer.to_raw_ptrs().data()),
|
||||||
|
write_count, write_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
read_index += write_count;
|
read_index += write_count;
|
||||||
@@ -338,7 +367,8 @@ void Decoder::UpdateLastAccessed()
|
|||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-31
@@ -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,15 +64,10 @@ 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();
|
||||||
|
|
||||||
@@ -76,24 +76,29 @@ public:
|
|||||||
*/
|
*/
|
||||||
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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +143,6 @@ public:
|
|||||||
int stream_;
|
int stream_;
|
||||||
|
|
||||||
Block *block_;
|
Block *block_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,8 +158,7 @@ public:
|
|||||||
|
|
||||||
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;
|
||||||
@@ -192,7 +195,10 @@ 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
|
||||||
@@ -210,7 +216,8 @@ 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
|
||||||
@@ -222,7 +229,9 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @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
|
||||||
@@ -233,7 +242,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
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);
|
||||||
|
|
||||||
@@ -272,7 +282,9 @@ protected:
|
|||||||
*/
|
*/
|
||||||
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);
|
||||||
|
|
||||||
@@ -286,7 +298,10 @@ protected:
|
|||||||
return stream_;
|
return stream_;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual rational GetAudioStartOffset() const { return 0; }
|
virtual rational GetAudioStartOffset() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
@@ -298,7 +313,10 @@ signals:
|
|||||||
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_;
|
||||||
|
|
||||||
@@ -309,7 +327,6 @@ private:
|
|||||||
TexturePtr cached_texture_;
|
TexturePtr cached_texture_;
|
||||||
rational cached_time_;
|
rational cached_time_;
|
||||||
int cached_divider_;
|
int cached_divider_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uint qHash(Decoder::CodecStream stream, uint seed = 0);
|
uint qHash(Decoder::CodecStream stream, uint seed = 0);
|
||||||
|
|||||||
+161
-85
@@ -26,13 +26,16 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,9 +48,11 @@ 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(
|
||||||
|
frame, params().video_params().frame_rate_as_time_base());
|
||||||
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
|
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
|
||||||
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
|
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);
|
||||||
@@ -82,26 +87,27 @@ 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()
|
||||||
@@ -110,14 +116,16 @@ QStringList EncodingParams::GetListOfPresets()
|
|||||||
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;
|
||||||
@@ -130,7 +138,8 @@ void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
|
|||||||
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;
|
||||||
@@ -159,7 +168,8 @@ bool EncodingParams::Load(QXmlStreamReader *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")) {
|
if (attr.name() == QStringLiteral("version")) {
|
||||||
version = attr.value().toInt();
|
version = attr.value().toInt();
|
||||||
}
|
}
|
||||||
@@ -195,46 +205,73 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
|
|||||||
|
|
||||||
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("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("pixfmt"), video_pix_fmt_);
|
||||||
writer->writeTextElement(QStringLiteral("imgseq"), QString::number(video_is_image_sequence_));
|
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"),
|
||||||
|
color_transform_.output());
|
||||||
writer->writeEndElement(); // colortransform
|
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();
|
||||||
|
i++) {
|
||||||
writer->writeStartElement(QStringLiteral("entry"));
|
writer->writeStartElement(QStringLiteral("entry"));
|
||||||
|
|
||||||
writer->writeTextElement(QStringLiteral("key"), i.key());
|
writer->writeTextElement(QStringLiteral("key"), i.key());
|
||||||
@@ -251,27 +288,38 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
|
|||||||
|
|
||||||
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(
|
||||||
|
QStringLiteral("channellayout"),
|
||||||
QString::number(audio_params().channel_layout().u.mask));
|
QString::number(audio_params().channel_layout().u.mask));
|
||||||
writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string()));
|
writer->writeTextElement(
|
||||||
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_));
|
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
|
||||||
@@ -324,7 +372,8 @@ Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -339,12 +388,14 @@ 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
|
||||||
|
EncodingParams::GenerateMatrix(EncodingParams::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)
|
||||||
{
|
{
|
||||||
@@ -354,8 +405,10 @@ QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod met
|
|||||||
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;
|
||||||
@@ -378,15 +431,19 @@ bool EncodingParams::LoadV1(QXmlStreamReader *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>(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("range")) {
|
} else if (reader->name() == QStringLiteral("range")) {
|
||||||
has_custom_range_ = reader->readElementText().toInt();
|
has_custom_range_ = reader->readElementText().toInt();
|
||||||
} else if (reader->name() == QStringLiteral("customrangein")) {
|
} else if (reader->name() == QStringLiteral("customrangein")) {
|
||||||
custom_range_in = rational::fromString(reader->readElementText().toStdString());
|
custom_range_in =
|
||||||
|
rational::fromString(reader->readElementText().toStdString());
|
||||||
} else if (reader->name() == QStringLiteral("customrangeout")) {
|
} else if (reader->name() == QStringLiteral("customrangeout")) {
|
||||||
custom_range_out = rational::fromString(reader->readElementText().toStdString());
|
custom_range_out =
|
||||||
|
rational::fromString(reader->readElementText().toStdString());
|
||||||
} else if (reader->name() == QStringLiteral("video")) {
|
} else if (reader->name() == QStringLiteral("video")) {
|
||||||
XMLAttributeLoop(reader, attr) {
|
XMLAttributeLoop(reader, attr)
|
||||||
|
{
|
||||||
if (attr.name() == QStringLiteral("enabled")) {
|
if (attr.name() == QStringLiteral("enabled")) {
|
||||||
video_enabled_ = attr.value().toInt();
|
video_enabled_ = attr.value().toInt();
|
||||||
}
|
}
|
||||||
@@ -394,25 +451,32 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
|||||||
|
|
||||||
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>(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("width")) {
|
} else if (reader->name() == QStringLiteral("width")) {
|
||||||
video_params_.set_width(reader->readElementText().toInt());
|
video_params_.set_width(reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("height")) {
|
} else if (reader->name() == QStringLiteral("height")) {
|
||||||
video_params_.set_height(reader->readElementText().toInt());
|
video_params_.set_height(reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("format")) {
|
} else if (reader->name() == QStringLiteral("format")) {
|
||||||
video_params_.set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt()));
|
video_params_.set_format(static_cast<PixelFormat::Format>(
|
||||||
|
reader->readElementText().toInt()));
|
||||||
} else if (reader->name() == QStringLiteral("pixelaspect")) {
|
} else if (reader->name() == QStringLiteral("pixelaspect")) {
|
||||||
video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString()));
|
video_params_.set_pixel_aspect_ratio(rational::fromString(
|
||||||
|
reader->readElementText().toStdString()));
|
||||||
} else if (reader->name() == QStringLiteral("timebase")) {
|
} else if (reader->name() == QStringLiteral("timebase")) {
|
||||||
video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString()));
|
video_params_.set_time_base(rational::fromString(
|
||||||
|
reader->readElementText().toStdString()));
|
||||||
} else if (reader->name() == QStringLiteral("divider")) {
|
} else if (reader->name() == QStringLiteral("divider")) {
|
||||||
video_params_.set_divider(reader->readElementText().toInt());
|
video_params_.set_divider(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||||
video_bit_rate_ = reader->readElementText().toLongLong();
|
video_bit_rate_ = reader->readElementText().toLongLong();
|
||||||
} else if (reader->name() == QStringLiteral("minbitrate")) {
|
} else if (reader->name() == QStringLiteral("minbitrate")) {
|
||||||
video_min_bit_rate_ = reader->readElementText().toLongLong();
|
video_min_bit_rate_ =
|
||||||
|
reader->readElementText().toLongLong();
|
||||||
} else if (reader->name() == QStringLiteral("maxbitrate")) {
|
} else if (reader->name() == QStringLiteral("maxbitrate")) {
|
||||||
video_max_bit_rate_ = reader->readElementText().toLongLong();
|
video_max_bit_rate_ =
|
||||||
|
reader->readElementText().toLongLong();
|
||||||
} else if (reader->name() == QStringLiteral("bufsize")) {
|
} else if (reader->name() == QStringLiteral("bufsize")) {
|
||||||
video_buffer_size_ = reader->readElementText().toLongLong();
|
video_buffer_size_ = reader->readElementText().toLongLong();
|
||||||
} else if (reader->name() == QStringLiteral("threads")) {
|
} else if (reader->name() == QStringLiteral("threads")) {
|
||||||
@@ -420,7 +484,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
|||||||
} else if (reader->name() == QStringLiteral("pixfmt")) {
|
} else if (reader->name() == QStringLiteral("pixfmt")) {
|
||||||
video_pix_fmt_ = reader->readElementText();
|
video_pix_fmt_ = reader->readElementText();
|
||||||
} else if (reader->name() == QStringLiteral("imgseq")) {
|
} else if (reader->name() == QStringLiteral("imgseq")) {
|
||||||
video_is_image_sequence_ = reader->readElementText().toInt();
|
video_is_image_sequence_ =
|
||||||
|
reader->readElementText().toInt();
|
||||||
} else if (reader->name() == QStringLiteral("color")) {
|
} else if (reader->name() == QStringLiteral("color")) {
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("output")) {
|
if (reader->name() == QStringLiteral("output")) {
|
||||||
@@ -430,7 +495,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (reader->name() == QStringLiteral("vscale")) {
|
} else if (reader->name() == QStringLiteral("vscale")) {
|
||||||
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt());
|
video_scaling_method_ = static_cast<VideoScalingMethod>(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("opts")) {
|
} else if (reader->name() == QStringLiteral("opts")) {
|
||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("entry")) {
|
if (reader->name() == QStringLiteral("entry")) {
|
||||||
@@ -438,7 +504,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
|||||||
while (XMLReadNextStartElement(reader)) {
|
while (XMLReadNextStartElement(reader)) {
|
||||||
if (reader->name() == QStringLiteral("key")) {
|
if (reader->name() == QStringLiteral("key")) {
|
||||||
key = reader->readElementText();
|
key = reader->readElementText();
|
||||||
} else if (reader->name() == QStringLiteral("value")) {
|
} else if (reader->name() ==
|
||||||
|
QStringLiteral("value")) {
|
||||||
value = reader->readElementText();
|
value = reader->readElementText();
|
||||||
} else {
|
} else {
|
||||||
reader->skipCurrentElement();
|
reader->skipCurrentElement();
|
||||||
@@ -459,7 +526,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
|||||||
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")) {
|
if (attr.name() == QStringLiteral("enabled")) {
|
||||||
audio_enabled_ = attr.value().toInt();
|
audio_enabled_ = attr.value().toInt();
|
||||||
}
|
}
|
||||||
@@ -467,16 +535,21 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
|||||||
|
|
||||||
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>(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("samplerate")) {
|
} else if (reader->name() == QStringLiteral("samplerate")) {
|
||||||
audio_params_.set_sample_rate(reader->readElementText().toInt());
|
audio_params_.set_sample_rate(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("channellayout")) {
|
} else if (reader->name() == QStringLiteral("channellayout")) {
|
||||||
AVChannelLayout av_channel_layout;
|
AVChannelLayout av_channel_layout;
|
||||||
av_channel_layout_from_mask(&av_channel_layout, reader->readElementText().toLongLong());
|
av_channel_layout_from_mask(
|
||||||
|
&av_channel_layout,
|
||||||
|
reader->readElementText().toLongLong());
|
||||||
audio_params_.set_channel_layout(av_channel_layout);
|
audio_params_.set_channel_layout(av_channel_layout);
|
||||||
av_channel_layout_uninit(&av_channel_layout);
|
av_channel_layout_uninit(&av_channel_layout);
|
||||||
} else if (reader->name() == QStringLiteral("format")) {
|
} else if (reader->name() == QStringLiteral("format")) {
|
||||||
audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString()));
|
audio_params_.set_format(SampleFormat::from_string(
|
||||||
|
reader->readElementText().toStdString()));
|
||||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||||
audio_bit_rate_ = reader->readElementText().toLongLong();
|
audio_bit_rate_ = reader->readElementText().toLongLong();
|
||||||
} else {
|
} else {
|
||||||
@@ -489,7 +562,8 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
|||||||
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")) {
|
if (attr.name() == QStringLiteral("enabled")) {
|
||||||
subtitles_enabled_ = attr.value().toInt();
|
subtitles_enabled_ = attr.value().toInt();
|
||||||
}
|
}
|
||||||
@@ -499,9 +573,11 @@ bool EncodingParams::LoadV1(QXmlStreamReader *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>(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("codec")) {
|
} else if (reader->name() == QStringLiteral("codec")) {
|
||||||
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
|
subtitles_codec_ = static_cast<ExportCodec::Codec>(
|
||||||
|
reader->readElementText().toInt());
|
||||||
} else {
|
} else {
|
||||||
reader->skipCurrentElement();
|
reader->skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
|||||||
+187
-65
@@ -34,19 +34,15 @@
|
|||||||
#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();
|
||||||
|
|
||||||
@@ -58,61 +54,178 @@ public:
|
|||||||
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 EnableAudio(const AudioParams &audio_params,
|
||||||
|
const ExportCodec::Codec &acodec);
|
||||||
void EnableSubtitles(const ExportCodec::Codec &scodec);
|
void EnableSubtitles(const ExportCodec::Codec &scodec);
|
||||||
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, 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);
|
||||||
@@ -120,16 +233,29 @@ public:
|
|||||||
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_; }
|
{
|
||||||
|
return has_custom_range_;
|
||||||
|
}
|
||||||
|
const TimeRange &custom_range() const
|
||||||
|
{
|
||||||
|
return custom_range_;
|
||||||
|
}
|
||||||
void set_custom_range(const TimeRange &custom_range)
|
void set_custom_range(const TimeRange &custom_range)
|
||||||
{
|
{
|
||||||
has_custom_range_ = true;
|
has_custom_range_ = true;
|
||||||
custom_range_ = custom_range;
|
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,
|
||||||
@@ -171,20 +297,14 @@ private:
|
|||||||
|
|
||||||
bool has_custom_range_;
|
bool has_custom_range_;
|
||||||
TimeRange custom_range_;
|
TimeRange custom_range_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Encoder : public QObject
|
class Encoder : public QObject {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Encoder(const EncodingParams ¶ms);
|
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
|
||||||
@@ -197,12 +317,14 @@ public:
|
|||||||
|
|
||||||
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 ¶ms() const;
|
const EncodingParams ¶ms() const;
|
||||||
|
|
||||||
@@ -229,7 +351,8 @@ public:
|
|||||||
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,
|
||||||
|
olive::core::rational time) = 0;
|
||||||
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
|
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
|
||||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
||||||
|
|
||||||
@@ -245,7 +368,6 @@ private:
|
|||||||
EncodingParams params_;
|
EncodingParams params_;
|
||||||
|
|
||||||
QString error_;
|
QString error_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ 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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,10 +27,10 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "render/subtitleparams.h"
|
#include "render/subtitleparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ExportCodec : public QObject
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ExportCodec : public QObject {
|
||||||
Q_OBJECT
|
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
|
||||||
@@ -63,7 +63,6 @@ public:
|
|||||||
static bool IsCodecAStillImage(Codec c);
|
static bool IsCodecAStillImage(Codec c);
|
||||||
|
|
||||||
static bool IsCodecLossless(Codec c);
|
static bool IsCodecLossless(Codec c);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-13
@@ -22,7 +22,8 @@
|
|||||||
|
|
||||||
#include "encoder.h"
|
#include "encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
||||||
{
|
{
|
||||||
@@ -111,9 +112,11 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format 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,
|
||||||
|
ExportCodec::kCodecH265, ExportCodec::kCodecVP9 };
|
||||||
case kFormatMPEG4Video:
|
case kFormatMPEG4Video:
|
||||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265};
|
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||||
|
ExportCodec::kCodecH265 };
|
||||||
case kFormatOpenEXR:
|
case kFormatOpenEXR:
|
||||||
return { ExportCodec::kCodecOpenEXR };
|
return { ExportCodec::kCodecOpenEXR };
|
||||||
case kFormatPNG:
|
case kFormatPNG:
|
||||||
@@ -121,7 +124,9 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
|||||||
case kFormatTIFF:
|
case kFormatTIFF:
|
||||||
return { ExportCodec::kCodecTIFF };
|
return { ExportCodec::kCodecTIFF };
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform};
|
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||||
|
ExportCodec::kCodecH265, ExportCodec::kCodecProRes,
|
||||||
|
ExportCodec::kCodecCineform };
|
||||||
case kFormatWebM:
|
case kFormatWebM:
|
||||||
return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
|
return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
|
||||||
case kFormatOgg:
|
case kFormatOgg:
|
||||||
@@ -145,14 +150,21 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
|||||||
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,
|
||||||
|
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM,
|
||||||
|
ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus,
|
||||||
|
ExportCodec::kCodecFLAC };
|
||||||
case kFormatMPEG4Video:
|
case kFormatMPEG4Video:
|
||||||
case kFormatMPEG4Audio:
|
case kFormatMPEG4Audio:
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3};
|
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||||
|
ExportCodec::kCodecMP3 };
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||||
|
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM };
|
||||||
case kFormatWebM:
|
case kFormatWebM:
|
||||||
return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis};
|
return { ExportCodec::kCodecOpus, ExportCodec::kCodecAAC,
|
||||||
|
ExportCodec::kCodecMP2, ExportCodec::kCodecMP3,
|
||||||
|
ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis };
|
||||||
|
|
||||||
// Audio only formats
|
// Audio only formats
|
||||||
case kFormatWAV:
|
case kFormatWAV:
|
||||||
@@ -164,7 +176,8 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
|||||||
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:
|
||||||
@@ -173,7 +186,6 @@ QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
|||||||
case kFormatSRT:
|
case kFormatSRT:
|
||||||
case kFormatCount:
|
case kFormatCount:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -205,7 +217,8 @@ QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
|
|||||||
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;
|
||||||
@@ -218,7 +231,8 @@ QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, Export
|
|||||||
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());
|
||||||
@@ -228,7 +242,6 @@ std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format,
|
|||||||
delete e;
|
delete e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,10 +27,10 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "exportcodec.h"
|
#include "exportcodec.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ExportFormat : public QObject
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ExportFormat : public QObject {
|
||||||
Q_OBJECT
|
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
|
||||||
@@ -61,8 +61,8 @@ public:
|
|||||||
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 QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||||
static std::vector<SampleFormat> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
|
static std::vector<SampleFormat>
|
||||||
|
GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+251
-175
@@ -45,16 +45,17 @@ extern "C" {
|
|||||||
#include "render/renderer.h"
|
#include "render/renderer.h"
|
||||||
#include "render/subtitleparams.h"
|
#include "render/subtitleparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
QVariant Yuv2RgbShader;
|
QVariant Yuv2RgbShader;
|
||||||
QVariant DeinterlaceShader;
|
QVariant DeinterlaceShader;
|
||||||
|
|
||||||
FFmpegDecoder::FFmpegDecoder() :
|
FFmpegDecoder::FFmpegDecoder()
|
||||||
sws_ctx_(nullptr),
|
: sws_ctx_(nullptr)
|
||||||
working_packet_(nullptr),
|
, working_packet_(nullptr)
|
||||||
cache_at_zero_(false),
|
, cache_at_zero_(false)
|
||||||
cache_at_eof_(false)
|
, cache_at_eof_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,21 +74,22 @@ bool FFmpegDecoder::OpenInternal()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original)
|
TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
|
||||||
|
const RetrieveVideoParams &p,
|
||||||
|
const AVFramePtr original)
|
||||||
{
|
{
|
||||||
// Determine native format
|
// Determine native format
|
||||||
AVPixelFormat ideal_fmt = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(f->format));
|
AVPixelFormat ideal_fmt = FFmpegUtils::GetCompatiblePixelFormat(
|
||||||
|
static_cast<AVPixelFormat>(f->format));
|
||||||
PixelFormat native_fmt = GetNativePixelFormat(ideal_fmt);
|
PixelFormat native_fmt = GetNativePixelFormat(ideal_fmt);
|
||||||
int native_channels = GetNativeChannelCount(ideal_fmt);
|
int native_channels = GetNativeChannelCount(ideal_fmt);
|
||||||
|
|
||||||
// Set up video params
|
// Set up video params
|
||||||
VideoParams vp(original->width,
|
VideoParams vp(original->width, original->height, native_fmt,
|
||||||
original->height,
|
|
||||||
native_fmt,
|
|
||||||
native_channels,
|
native_channels,
|
||||||
av_guess_sample_aspect_ratio(instance_.fmt_ctx(), instance_.avstream(), nullptr),
|
av_guess_sample_aspect_ratio(instance_.fmt_ctx(),
|
||||||
VideoParams::kInterlaceNone,
|
instance_.avstream(), nullptr),
|
||||||
p.divider);
|
VideoParams::kInterlaceNone, p.divider);
|
||||||
|
|
||||||
// Create texture
|
// Create texture
|
||||||
TexturePtr tex = p.renderer->CreateTexture(vp);
|
TexturePtr tex = p.renderer->CreateTexture(vp);
|
||||||
@@ -101,12 +103,13 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
|
|||||||
case AV_PIX_FMT_YUV444P10LE:
|
case AV_PIX_FMT_YUV444P10LE:
|
||||||
case AV_PIX_FMT_YUV420P12LE:
|
case AV_PIX_FMT_YUV420P12LE:
|
||||||
case AV_PIX_FMT_YUV422P12LE:
|
case AV_PIX_FMT_YUV422P12LE:
|
||||||
case AV_PIX_FMT_YUV444P12LE:
|
case AV_PIX_FMT_YUV444P12LE: {
|
||||||
{
|
|
||||||
// Run through YUV to RGB shader
|
// Run through YUV to RGB shader
|
||||||
if (Yuv2RgbShader.isNull()) {
|
if (Yuv2RgbShader.isNull()) {
|
||||||
// Compile shader
|
// Compile shader
|
||||||
Yuv2RgbShader = p.renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag"))));
|
Yuv2RgbShader = p.renderer->CreateNativeShader(
|
||||||
|
ShaderCode(FileFunctions::ReadFileAsString(
|
||||||
|
QStringLiteral(":/shaders/yuv2rgb.frag"))));
|
||||||
if (Yuv2RgbShader.isNull()) {
|
if (Yuv2RgbShader.isNull()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -142,7 +145,8 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
|
|||||||
plane_params.set_channel_count(1);
|
plane_params.set_channel_count(1);
|
||||||
plane_params.set_format(native_fmt);
|
plane_params.set_format(native_fmt);
|
||||||
|
|
||||||
TexturePtr y_plane = p.renderer->CreateTexture(plane_params, hw_in->data[0], hw_in->linesize[0] / px_size);
|
TexturePtr y_plane = p.renderer->CreateTexture(
|
||||||
|
plane_params, hw_in->data[0], hw_in->linesize[0] / px_size);
|
||||||
|
|
||||||
switch (f->format) {
|
switch (f->format) {
|
||||||
case AV_PIX_FMT_YUV420P:
|
case AV_PIX_FMT_YUV420P:
|
||||||
@@ -163,21 +167,37 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePtr u_plane = p.renderer->CreateTexture(plane_params, hw_in->data[1], hw_in->linesize[1] / px_size);
|
TexturePtr u_plane = p.renderer->CreateTexture(
|
||||||
TexturePtr v_plane = p.renderer->CreateTexture(plane_params, hw_in->data[2], hw_in->linesize[2] / px_size);
|
plane_params, hw_in->data[1], hw_in->linesize[1] / px_size);
|
||||||
|
TexturePtr v_plane = p.renderer->CreateTexture(
|
||||||
|
plane_params, hw_in->data[2], hw_in->linesize[2] / px_size);
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane)));
|
job.Insert(QStringLiteral("y_channel"),
|
||||||
job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane)));
|
NodeValue(NodeValue::kTexture,
|
||||||
job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane)));
|
QVariant::fromValue(y_plane)));
|
||||||
job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel));
|
job.Insert(QStringLiteral("u_channel"),
|
||||||
job.Insert(QStringLiteral("full_range"), NodeValue(NodeValue::kBoolean, hw_in->color_range == AVCOL_RANGE_JPEG));
|
NodeValue(NodeValue::kTexture,
|
||||||
|
QVariant::fromValue(u_plane)));
|
||||||
|
job.Insert(QStringLiteral("v_channel"),
|
||||||
|
NodeValue(NodeValue::kTexture,
|
||||||
|
QVariant::fromValue(v_plane)));
|
||||||
|
job.Insert(QStringLiteral("bits_per_pixel"),
|
||||||
|
NodeValue(NodeValue::kInt, bits_per_pixel));
|
||||||
|
job.Insert(QStringLiteral("full_range"),
|
||||||
|
NodeValue(NodeValue::kBoolean,
|
||||||
|
hw_in->color_range == AVCOL_RANGE_JPEG));
|
||||||
|
|
||||||
const int *yuv_coeffs = sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(hw_in->colorspace));
|
const int *yuv_coeffs = sws_getCoefficients(
|
||||||
job.Insert(QStringLiteral("yuv_crv"), NodeValue(NodeValue::kFloat, yuv_coeffs[0]/65536.0));
|
FFmpegUtils::GetSwsColorspaceFromAVColorSpace(hw_in->colorspace));
|
||||||
job.Insert(QStringLiteral("yuv_cgu"), NodeValue(NodeValue::kFloat, yuv_coeffs[2]/65536.0));
|
job.Insert(QStringLiteral("yuv_crv"),
|
||||||
job.Insert(QStringLiteral("yuv_cgv"), NodeValue(NodeValue::kFloat, yuv_coeffs[3]/65536.0));
|
NodeValue(NodeValue::kFloat, yuv_coeffs[0] / 65536.0));
|
||||||
job.Insert(QStringLiteral("yuv_cbu"), NodeValue(NodeValue::kFloat, yuv_coeffs[1]/65536.0));
|
job.Insert(QStringLiteral("yuv_cgu"),
|
||||||
|
NodeValue(NodeValue::kFloat, yuv_coeffs[2] / 65536.0));
|
||||||
|
job.Insert(QStringLiteral("yuv_cgv"),
|
||||||
|
NodeValue(NodeValue::kFloat, yuv_coeffs[3] / 65536.0));
|
||||||
|
job.Insert(QStringLiteral("yuv_cbu"),
|
||||||
|
NodeValue(NodeValue::kFloat, yuv_coeffs[1] / 65536.0));
|
||||||
|
|
||||||
tex = p.renderer->CreateTexture(vp);
|
tex = p.renderer->CreateTexture(vp);
|
||||||
p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
|
p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
|
||||||
@@ -194,13 +214,16 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
|
|||||||
if (p.src_interlacing != VideoParams::kInterlaceNone) {
|
if (p.src_interlacing != VideoParams::kInterlaceNone) {
|
||||||
if (DeinterlaceShader.isNull()) {
|
if (DeinterlaceShader.isNull()) {
|
||||||
// Compile shader
|
// Compile shader
|
||||||
DeinterlaceShader = p.renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace2.frag"))));
|
DeinterlaceShader = p.renderer->CreateNativeShader(
|
||||||
|
ShaderCode(FileFunctions::ReadFileAsString(
|
||||||
|
QStringLiteral(":/shaders/deinterlace2.frag"))));
|
||||||
if (DeinterlaceShader.isNull()) {
|
if (DeinterlaceShader.isNull()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rational frame_rate_tb = av_guess_frame_rate(instance_.fmt_ctx(), instance_.avstream(), original.get());
|
rational frame_rate_tb = av_guess_frame_rate(
|
||||||
|
instance_.fmt_ctx(), instance_.avstream(), original.get());
|
||||||
|
|
||||||
// Double frame rate for interlaced fields
|
// Double frame rate for interlaced fields
|
||||||
frame_rate_tb *= 2;
|
frame_rate_tb *= 2;
|
||||||
@@ -208,22 +231,30 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi
|
|||||||
// Flip frame rate so it can be used as a timebase
|
// Flip frame rate so it can be used as a timebase
|
||||||
frame_rate_tb.flip();
|
frame_rate_tb.flip();
|
||||||
|
|
||||||
int64_t req = Timecode::time_to_timestamp(p.time + rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE), frame_rate_tb);
|
int64_t req = Timecode::time_to_timestamp(
|
||||||
int64_t frm = Timecode::rescale_timestamp(original->pts, instance_.avstream()->time_base, frame_rate_tb);
|
p.time + rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE),
|
||||||
|
frame_rate_tb);
|
||||||
|
int64_t frm = Timecode::rescale_timestamp(
|
||||||
|
original->pts, instance_.avstream()->time_base, frame_rate_tb);
|
||||||
|
|
||||||
bool first = (req == frm);
|
bool first = (req == frm);
|
||||||
bool top_first = (p.src_interlacing == VideoParams::kInterlacedTopFirst);
|
bool top_first =
|
||||||
|
(p.src_interlacing == VideoParams::kInterlacedTopFirst);
|
||||||
|
|
||||||
int interlacing = (first == top_first) ? 1 : 2;
|
int interlacing = (first == top_first) ? 1 : 2;
|
||||||
|
|
||||||
TexturePtr deinterlaced = p.renderer->CreateTexture(tex->params());
|
TexturePtr deinterlaced = p.renderer->CreateTexture(tex->params());
|
||||||
|
|
||||||
ShaderJob job;
|
ShaderJob job;
|
||||||
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, tex));
|
job.Insert(QStringLiteral("ove_maintex"),
|
||||||
job.Insert(QStringLiteral("interlacing"), NodeValue(NodeValue::kInt, interlacing));
|
NodeValue(NodeValue::kTexture, tex));
|
||||||
job.Insert(QStringLiteral("pixel_height"), NodeValue(NodeValue::kInt, original->height));
|
job.Insert(QStringLiteral("interlacing"),
|
||||||
|
NodeValue(NodeValue::kInt, interlacing));
|
||||||
|
job.Insert(QStringLiteral("pixel_height"),
|
||||||
|
NodeValue(NodeValue::kInt, original->height));
|
||||||
|
|
||||||
p.renderer->BlitToTexture(DeinterlaceShader, job, deinterlaced.get(), false);
|
p.renderer->BlitToTexture(DeinterlaceShader, job, deinterlaced.get(),
|
||||||
|
false);
|
||||||
|
|
||||||
tex = deinterlaced;
|
tex = deinterlaced;
|
||||||
}
|
}
|
||||||
@@ -241,10 +272,13 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
|||||||
AVFramePtr original = f;
|
AVFramePtr original = f;
|
||||||
|
|
||||||
// Disregard "JPEG" pixel formats because we allow the user to override that
|
// Disregard "JPEG" pixel formats because we allow the user to override that
|
||||||
f->format = FFmpegUtils::ConvertJPEGSpaceToRegularSpace(static_cast<AVPixelFormat>(f->format));
|
f->format = FFmpegUtils::ConvertJPEGSpaceToRegularSpace(
|
||||||
|
static_cast<AVPixelFormat>(f->format));
|
||||||
|
|
||||||
// Force frame's color range to whatever it's set to in Olive
|
// Force frame's color range to whatever it's set to in Olive
|
||||||
f->color_range = p.force_range == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
|
f->color_range = p.force_range == VideoParams::kColorRangeFull ?
|
||||||
|
AVCOL_RANGE_JPEG :
|
||||||
|
AVCOL_RANGE_MPEG;
|
||||||
|
|
||||||
// Perform any CPU processing required
|
// Perform any CPU processing required
|
||||||
f = PreProcessFrame(f, p);
|
f = PreProcessFrame(f, p);
|
||||||
@@ -277,8 +311,10 @@ rational FFmpegDecoder::GetAudioStartOffset() const
|
|||||||
{
|
{
|
||||||
auto f = instance_.fmt_ctx();
|
auto f = instance_.fmt_ctx();
|
||||||
if (f) {
|
if (f) {
|
||||||
rational fmt_start = rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE);
|
rational fmt_start =
|
||||||
rational str_start = rational(instance_.avstream()->time_base) * instance_.avstream()->start_time;
|
rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE);
|
||||||
|
rational str_start = rational(instance_.avstream()->time_base) *
|
||||||
|
instance_.avstream()->start_time;
|
||||||
return str_start - fmt_start;
|
return str_start - fmt_start;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -290,7 +326,8 @@ QString FFmpegDecoder::id() const
|
|||||||
return QStringLiteral("ffmpeg");
|
return QStringLiteral("ffmpeg");
|
||||||
}
|
}
|
||||||
|
|
||||||
FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *cancelled) const
|
FootageDescription FFmpegDecoder::Probe(const QString &filename,
|
||||||
|
CancelAtom *cancelled) const
|
||||||
{
|
{
|
||||||
// Return value
|
// Return value
|
||||||
FootageDescription desc(id());
|
FootageDescription desc(id());
|
||||||
@@ -307,41 +344,42 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
|
|
||||||
// Handle format context error
|
// Handle format context error
|
||||||
if (error_code == 0) {
|
if (error_code == 0) {
|
||||||
|
|
||||||
// Retrieve metadata about the media
|
// Retrieve metadata about the media
|
||||||
avformat_find_stream_info(fmt_ctx, nullptr);
|
avformat_find_stream_info(fmt_ctx, nullptr);
|
||||||
|
|
||||||
int64_t footage_duration = fmt_ctx->duration;
|
int64_t footage_duration = fmt_ctx->duration;
|
||||||
|
|
||||||
bool duration_guessed_from_bitrate = (fmt_ctx->duration_estimation_method == AVFMT_DURATION_FROM_BITRATE);
|
bool duration_guessed_from_bitrate =
|
||||||
|
(fmt_ctx->duration_estimation_method ==
|
||||||
|
AVFMT_DURATION_FROM_BITRATE);
|
||||||
if (duration_guessed_from_bitrate) {
|
if (duration_guessed_from_bitrate) {
|
||||||
qWarning() << "Unreliable duration detected - we will manually determine it ourselves (this may take some time)";
|
qWarning()
|
||||||
|
<< "Unreliable duration detected - we will manually determine it ourselves (this may take some time)";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump it into the Footage object
|
// Dump it into the Footage object
|
||||||
int video_streams = 0, audio_streams = 0, still_streams = 0;
|
int video_streams = 0, audio_streams = 0, still_streams = 0;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
|
for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
|
||||||
|
|
||||||
// FFmpeg AVStream
|
// FFmpeg AVStream
|
||||||
AVStream *avstream = fmt_ctx->streams[i];
|
AVStream *avstream = fmt_ctx->streams[i];
|
||||||
|
|
||||||
// Find decoder for this stream, if it exists we can proceed
|
// Find decoder for this stream, if it exists we can proceed
|
||||||
const AVCodec* decoder = avcodec_find_decoder(avstream->codecpar->codec_id);
|
const AVCodec *decoder =
|
||||||
|
avcodec_find_decoder(avstream->codecpar->codec_id);
|
||||||
if (decoder
|
|
||||||
&& (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|
|
||||||
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO
|
|
||||||
|| avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
|
|
||||||
|
|
||||||
|
if (decoder &&
|
||||||
|
(avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
|
||||||
|
avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
|
||||||
|
avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
|
||||||
if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
|
|
||||||
AVPixelFormat compatible_pix_fmt = AV_PIX_FMT_NONE;
|
AVPixelFormat compatible_pix_fmt = AV_PIX_FMT_NONE;
|
||||||
|
|
||||||
bool image_is_still = false;
|
bool image_is_still = false;
|
||||||
rational pixel_aspect_ratio;
|
rational pixel_aspect_ratio;
|
||||||
rational frame_rate;
|
rational frame_rate;
|
||||||
VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone;
|
VideoParams::Interlacing interlacing =
|
||||||
|
VideoParams::kInterlaceNone;
|
||||||
|
|
||||||
{
|
{
|
||||||
// Read at least two frames to get more information about this video stream
|
// Read at least two frames to get more information about this video stream
|
||||||
@@ -357,21 +395,27 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
// Check if video is interlaced and what field dominance it has if so
|
// Check if video is interlaced and what field dominance it has if so
|
||||||
if (frame->interlaced_frame) {
|
if (frame->interlaced_frame) {
|
||||||
if (frame->top_field_first) {
|
if (frame->top_field_first) {
|
||||||
interlacing = VideoParams::kInterlacedTopFirst;
|
interlacing =
|
||||||
|
VideoParams::kInterlacedTopFirst;
|
||||||
} else {
|
} else {
|
||||||
interlacing = VideoParams::kInterlacedBottomFirst;
|
interlacing =
|
||||||
|
VideoParams::kInterlacedBottomFirst;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pixel_aspect_ratio = av_guess_sample_aspect_ratio(instance.fmt_ctx(),
|
pixel_aspect_ratio =
|
||||||
instance.avstream(),
|
av_guess_sample_aspect_ratio(
|
||||||
|
instance.fmt_ctx(), instance.avstream(),
|
||||||
frame);
|
frame);
|
||||||
|
|
||||||
frame_rate = av_guess_frame_rate(instance.fmt_ctx(),
|
frame_rate = av_guess_frame_rate(
|
||||||
instance.avstream(),
|
instance.fmt_ctx(), instance.avstream(),
|
||||||
frame);
|
frame);
|
||||||
|
|
||||||
compatible_pix_fmt = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(avstream->codecpar->format));
|
compatible_pix_fmt =
|
||||||
|
FFmpegUtils::GetCompatiblePixelFormat(
|
||||||
|
static_cast<AVPixelFormat>(
|
||||||
|
avstream->codecpar->format));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read second frame
|
// Read second frame
|
||||||
@@ -379,23 +423,30 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
|
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
// Check if we need a manual duration
|
// Check if we need a manual duration
|
||||||
if (avstream->duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) {
|
if (avstream->duration == AV_NOPTS_VALUE ||
|
||||||
if (footage_duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) {
|
duration_guessed_from_bitrate) {
|
||||||
|
if (footage_duration == AV_NOPTS_VALUE ||
|
||||||
|
duration_guessed_from_bitrate) {
|
||||||
// Manually read through file for duration
|
// Manually read through file for duration
|
||||||
int64_t new_dur;
|
int64_t new_dur;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
new_dur = frame->best_effort_timestamp;
|
new_dur =
|
||||||
} while (instance.GetFrame(pkt, frame) >= 0 && (!cancelled || !cancelled->IsCancelled()));
|
frame->best_effort_timestamp;
|
||||||
|
} while (instance.GetFrame(
|
||||||
|
pkt, frame) >= 0 &&
|
||||||
|
(!cancelled ||
|
||||||
|
!cancelled->IsCancelled()));
|
||||||
|
|
||||||
avstream->duration = new_dur;
|
avstream->duration = new_dur;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Fallback to footage duration
|
// Fallback to footage duration
|
||||||
avstream->duration = Timecode::rescale_timestamp_ceil(footage_duration, rational(1, AV_TIME_BASE), avstream->time_base);
|
avstream->duration =
|
||||||
|
Timecode::rescale_timestamp_ceil(
|
||||||
|
footage_duration,
|
||||||
|
rational(1, AV_TIME_BASE),
|
||||||
|
avstream->time_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ret == AVERROR_EOF) {
|
} else if (ret == AVERROR_EOF) {
|
||||||
@@ -414,16 +465,22 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
stream.set_stream_index(i);
|
stream.set_stream_index(i);
|
||||||
stream.set_width(avstream->codecpar->width);
|
stream.set_width(avstream->codecpar->width);
|
||||||
stream.set_height(avstream->codecpar->height);
|
stream.set_height(avstream->codecpar->height);
|
||||||
stream.set_video_type((image_is_still) ? VideoParams::kVideoTypeStill : VideoParams::kVideoTypeVideo);
|
stream.set_video_type((image_is_still) ?
|
||||||
|
VideoParams::kVideoTypeStill :
|
||||||
|
VideoParams::kVideoTypeVideo);
|
||||||
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
|
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
|
||||||
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
|
stream.set_channel_count(
|
||||||
|
GetNativeChannelCount(compatible_pix_fmt));
|
||||||
stream.set_interlacing(interlacing);
|
stream.set_interlacing(interlacing);
|
||||||
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
|
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
|
||||||
stream.set_frame_rate(frame_rate);
|
stream.set_frame_rate(frame_rate);
|
||||||
stream.set_start_time(avstream->start_time);
|
stream.set_start_time(avstream->start_time);
|
||||||
stream.set_time_base(avstream->time_base);
|
stream.set_time_base(avstream->time_base);
|
||||||
stream.set_duration(avstream->duration);
|
stream.set_duration(avstream->duration);
|
||||||
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG ? VideoParams::kColorRangeFull : VideoParams::kColorRangeLimited);
|
stream.set_color_range(avstream->codecpar->color_range ==
|
||||||
|
AVCOL_RANGE_JPEG ?
|
||||||
|
VideoParams::kColorRangeFull :
|
||||||
|
VideoParams::kColorRangeLimited);
|
||||||
|
|
||||||
// Defaults to false, requires user intervention if incorrect
|
// Defaults to false, requires user intervention if incorrect
|
||||||
stream.set_premultiplied_alpha(false);
|
stream.set_premultiplied_alpha(false);
|
||||||
@@ -436,17 +493,22 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
video_streams++;
|
video_streams++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
} else if (avstream->codecpar->codec_type ==
|
||||||
|
AVMEDIA_TYPE_AUDIO) {
|
||||||
// Create an audio stream object
|
// Create an audio stream object
|
||||||
AVChannelLayout &channel_layout = avstream->codecpar->ch_layout;
|
AVChannelLayout &channel_layout =
|
||||||
|
avstream->codecpar->ch_layout;
|
||||||
if (!av_channel_layout_check(&channel_layout)) {
|
if (!av_channel_layout_check(&channel_layout)) {
|
||||||
av_channel_layout_default(&channel_layout,avstream->codecpar->ch_layout.nb_channels);
|
av_channel_layout_default(
|
||||||
|
&channel_layout,
|
||||||
|
avstream->codecpar->ch_layout.nb_channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avstream->duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) {
|
if (avstream->duration == AV_NOPTS_VALUE ||
|
||||||
|
duration_guessed_from_bitrate) {
|
||||||
// Loop through stream until we get the whole duration
|
// Loop through stream until we get the whole duration
|
||||||
if (footage_duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) {
|
if (footage_duration == AV_NOPTS_VALUE ||
|
||||||
|
duration_guessed_from_bitrate) {
|
||||||
Instance instance;
|
Instance instance;
|
||||||
instance.Open(filename_c, avstream->index);
|
instance.Open(filename_c, avstream->index);
|
||||||
|
|
||||||
@@ -457,7 +519,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
new_dur = frame->best_effort_timestamp;
|
new_dur = frame->best_effort_timestamp;
|
||||||
} while (instance.GetFrame(pkt, frame) >= 0 && (!cancelled || !cancelled->IsCancelled()));
|
} while (instance.GetFrame(pkt, frame) >= 0 &&
|
||||||
|
(!cancelled || !cancelled->IsCancelled()));
|
||||||
|
|
||||||
avstream->duration = new_dur;
|
avstream->duration = new_dur;
|
||||||
|
|
||||||
@@ -466,9 +529,10 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
|
|
||||||
instance.Close();
|
instance.Close();
|
||||||
} else {
|
} else {
|
||||||
|
avstream->duration =
|
||||||
avstream->duration = Timecode::rescale_timestamp_ceil(footage_duration, rational(1, AV_TIME_BASE), avstream->time_base);
|
Timecode::rescale_timestamp_ceil(
|
||||||
|
footage_duration, rational(1, AV_TIME_BASE),
|
||||||
|
avstream->time_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,15 +540,17 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
stream.set_stream_index(i);
|
stream.set_stream_index(i);
|
||||||
stream.set_channel_layout(channel_layout);
|
stream.set_channel_layout(channel_layout);
|
||||||
stream.set_sample_rate(avstream->codecpar->sample_rate);
|
stream.set_sample_rate(avstream->codecpar->sample_rate);
|
||||||
stream.set_format(FFmpegUtils::GetNativeSampleFormat(static_cast<AVSampleFormat>(avstream->codecpar->format)));
|
stream.set_format(FFmpegUtils::GetNativeSampleFormat(
|
||||||
|
static_cast<AVSampleFormat>(
|
||||||
|
avstream->codecpar->format)));
|
||||||
stream.set_time_base(avstream->time_base);
|
stream.set_time_base(avstream->time_base);
|
||||||
stream.set_duration(avstream->duration);
|
stream.set_duration(avstream->duration);
|
||||||
desc.AddAudioStream(stream);
|
desc.AddAudioStream(stream);
|
||||||
|
|
||||||
audio_streams++;
|
audio_streams++;
|
||||||
|
|
||||||
} else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
|
} else if (avstream->codecpar->codec_type ==
|
||||||
|
AVMEDIA_TYPE_SUBTITLE) {
|
||||||
// Limit to SRT for now...
|
// Limit to SRT for now...
|
||||||
if (avstream->codecpar->codec_id == AV_CODEC_ID_SUBRIP) {
|
if (avstream->codecpar->codec_id == AV_CODEC_ID_SUBRIP) {
|
||||||
SubtitleParams sub;
|
SubtitleParams sub;
|
||||||
@@ -495,10 +561,15 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
instance.Open(filename_c, avstream->index);
|
instance.Open(filename_c, avstream->index);
|
||||||
|
|
||||||
while (instance.GetPacket(pkt) >= 0) {
|
while (instance.GetPacket(pkt) >= 0) {
|
||||||
TimeRange time(Timecode::timestamp_to_time(pkt->pts, avstream->time_base),
|
TimeRange time(Timecode::timestamp_to_time(
|
||||||
Timecode::timestamp_to_time(pkt->pts + pkt->duration, avstream->time_base));
|
pkt->pts,
|
||||||
|
avstream->time_base),
|
||||||
|
Timecode::timestamp_to_time(
|
||||||
|
pkt->pts + pkt->duration,
|
||||||
|
avstream->time_base));
|
||||||
|
|
||||||
QString text = QString::fromUtf8((const char *) pkt->data, pkt->size);
|
QString text = QString::fromUtf8(
|
||||||
|
(const char *)pkt->data, pkt->size);
|
||||||
|
|
||||||
sub.push_back(Subtitle(time, text));
|
sub.push_back(Subtitle(time, text));
|
||||||
}
|
}
|
||||||
@@ -509,11 +580,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
|
|
||||||
desc.AddSubtitleStream(sub);
|
desc.AddSubtitleStream(sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
desc.SetStreamCount(fmt_ctx->nb_streams);
|
desc.SetStreamCount(fmt_ctx->nb_streams);
|
||||||
@@ -527,7 +595,6 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
|
|||||||
vp.set_enabled(false);
|
vp.set_enabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free all memory
|
// Free all memory
|
||||||
@@ -543,7 +610,9 @@ QString FFmpegDecoder::FFmpegError(int error_code)
|
|||||||
return QStringLiteral("%1 %2").arg(QString::number(error_code), err);
|
return QStringLiteral("%1 %2").arg(QString::number(error_code), err);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams ¶ms, CancelAtom *cancelled)
|
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames,
|
||||||
|
const AudioParams ¶ms,
|
||||||
|
CancelAtom *cancelled)
|
||||||
{
|
{
|
||||||
// Iterate through each audio frame and extract the PCM data
|
// Iterate through each audio frame and extract the PCM data
|
||||||
|
|
||||||
@@ -551,23 +620,22 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
instance_.Seek(0);
|
instance_.Seek(0);
|
||||||
|
|
||||||
// Handle NULL channel layout
|
// Handle NULL channel layout
|
||||||
AVChannelLayout channel_layout = ValidateChannelLayout(instance_.avstream());
|
AVChannelLayout channel_layout =
|
||||||
|
ValidateChannelLayout(instance_.avstream());
|
||||||
if (!av_channel_layout_check(&channel_layout)) {
|
if (!av_channel_layout_check(&channel_layout)) {
|
||||||
qCritical() << "Failed to determine channel layout of audio file, could not conform";
|
qCritical()
|
||||||
|
<< "Failed to determine channel layout of audio file, could not conform";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Create resampling context
|
// Create resampling context
|
||||||
AVChannelLayout layout = params.channel_layout();
|
AVChannelLayout layout = params.channel_layout();
|
||||||
SwrContext *resampler;
|
SwrContext *resampler;
|
||||||
swr_alloc_set_opts2(&resampler,
|
swr_alloc_set_opts2(
|
||||||
&layout,
|
&resampler, &layout,
|
||||||
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
||||||
params.sample_rate(),
|
params.sample_rate(), &channel_layout,
|
||||||
&channel_layout,
|
|
||||||
static_cast<AVSampleFormat>(instance_.avstream()->codecpar->format),
|
static_cast<AVSampleFormat>(instance_.avstream()->codecpar->format),
|
||||||
instance_.avstream()->codecpar->sample_rate,
|
instance_.avstream()->codecpar->sample_rate, 0, nullptr);
|
||||||
0,
|
|
||||||
nullptr);
|
|
||||||
av_channel_layout_uninit(&layout);
|
av_channel_layout_uninit(&layout);
|
||||||
swr_init(resampler);
|
swr_init(resampler);
|
||||||
|
|
||||||
@@ -582,7 +650,9 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
duration = instance_.fmt_ctx()->duration;
|
duration = instance_.fmt_ctx()->duration;
|
||||||
if (!(duration == 0 || duration == AV_NOPTS_VALUE)) {
|
if (!(duration == 0 || duration == AV_NOPTS_VALUE)) {
|
||||||
// Rescale from AVFormatContext timebase to AVStream timebase
|
// Rescale from AVFormatContext timebase to AVStream timebase
|
||||||
duration = av_rescale_q_rnd(duration, {1, AV_TIME_BASE}, instance_.avstream()->time_base, AV_ROUND_UP);
|
duration = av_rescale_q_rnd(duration, { 1, AV_TIME_BASE },
|
||||||
|
instance_.avstream()->time_base,
|
||||||
|
AV_ROUND_UP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,7 +671,6 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
ret = instance_.GetFrame(pkt, frame);
|
ret = instance_.GetFrame(pkt, frame);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
||||||
if (ret == AVERROR_EOF) {
|
if (ret == AVERROR_EOF) {
|
||||||
success = true;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -610,29 +679,33 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
qWarning() << "Failed to conform:" << ret << err_str;
|
qWarning() << "Failed to conform:" << ret << err_str;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate buffers
|
// Allocate buffers
|
||||||
int nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
|
int nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
|
||||||
int nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels;
|
int nb_bytes_per_channel =
|
||||||
|
params.samples_to_bytes(nb_samples) / nb_channels;
|
||||||
data.set_sample_count(nb_bytes_per_channel);
|
data.set_sample_count(nb_bytes_per_channel);
|
||||||
data.allocate();
|
data.allocate();
|
||||||
|
|
||||||
// Resample audio to our destination parameters
|
// Resample audio to our destination parameters
|
||||||
nb_samples = swr_convert(resampler,
|
nb_samples = swr_convert(
|
||||||
|
resampler,
|
||||||
reinterpret_cast<uint8_t **>(data.to_raw_ptrs().data()),
|
reinterpret_cast<uint8_t **>(data.to_raw_ptrs().data()),
|
||||||
nb_samples,
|
nb_samples, const_cast<const uint8_t **>(frame->data),
|
||||||
const_cast<const uint8_t**>(frame->data),
|
|
||||||
frame->nb_samples);
|
frame->nb_samples);
|
||||||
|
|
||||||
// If no error, write to files
|
// If no error, write to files
|
||||||
if (nb_samples > 0) {
|
if (nb_samples > 0) {
|
||||||
// Update byte count for the number of samples we actually received
|
// Update byte count for the number of samples we actually received
|
||||||
nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels;
|
nb_bytes_per_channel =
|
||||||
|
params.samples_to_bytes(nb_samples) / nb_channels;
|
||||||
|
|
||||||
// Write to files
|
// Write to files
|
||||||
wave_out.write(const_cast<const char**>(reinterpret_cast<char**>(data.to_raw_ptrs().data())), nb_bytes_per_channel);
|
wave_out.write(
|
||||||
|
const_cast<const char **>(
|
||||||
|
reinterpret_cast<char **>(data.to_raw_ptrs().data())),
|
||||||
|
nb_bytes_per_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free buffer
|
// Free buffer
|
||||||
@@ -642,7 +715,8 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
if (nb_samples < 0) {
|
if (nb_samples < 0) {
|
||||||
char err_str[512];
|
char err_str[512];
|
||||||
av_strerror(nb_samples, err_str, 512);
|
av_strerror(nb_samples, err_str, 512);
|
||||||
qWarning() << "libswresample failed with error:" << nb_samples << err_str;
|
qWarning() << "libswresample failed with error:" << nb_samples
|
||||||
|
<< err_str;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,7 +774,8 @@ AVChannelLayout FFmpegDecoder::ValidateChannelLayout(AVStream* stream)
|
|||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *FFmpegDecoder::GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing)
|
const char *
|
||||||
|
FFmpegDecoder::GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing)
|
||||||
{
|
{
|
||||||
if (interlacing == VideoParams::kInterlacedTopFirst) {
|
if (interlacing == VideoParams::kInterlacedTopFirst) {
|
||||||
return "tff";
|
return "tff";
|
||||||
@@ -740,13 +815,15 @@ void FFmpegDecoder::ClearFrameCache()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p)
|
AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
|
||||||
|
const RetrieveVideoParams &p)
|
||||||
{
|
{
|
||||||
// In pre-processing, we try to achieve the following:
|
// In pre-processing, we try to achieve the following:
|
||||||
// - If a divider is being used, scale down the image
|
// - If a divider is being used, scale down the image
|
||||||
// - If a pixel format is not compatible with the GLSL shader, convert it to RGBA ourselves
|
// - If a pixel format is not compatible with the GLSL shader, convert it to RGBA ourselves
|
||||||
|
|
||||||
if (p.divider == 1 && IsPixelFormatGLSLCompatible(static_cast<AVPixelFormat>(f->format))) {
|
if (p.divider == 1 &&
|
||||||
|
IsPixelFormatGLSLCompatible(static_cast<AVPixelFormat>(f->format))) {
|
||||||
// No CPU processing required, the user wants this in full resolution and the pixel format can
|
// No CPU processing required, the user wants this in full resolution and the pixel format can
|
||||||
// be converted on the GPU
|
// be converted on the GPU
|
||||||
return f;
|
return f;
|
||||||
@@ -766,8 +843,10 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
|
|||||||
dest->height = VideoParams::GetScaledDimension(dest->height, p.divider);
|
dest->height = VideoParams::GetScaledDimension(dest->height, p.divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsPixelFormatGLSLCompatible(static_cast<AVPixelFormat>(dest->format))) {
|
if (!IsPixelFormatGLSLCompatible(
|
||||||
dest->format = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(dest->format), p.maximum_format);
|
static_cast<AVPixelFormat>(dest->format))) {
|
||||||
|
dest->format = FFmpegUtils::GetCompatiblePixelFormat(
|
||||||
|
static_cast<AVPixelFormat>(dest->format), p.maximum_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
int r = av_frame_get_buffer(dest.get(), 0);
|
int r = av_frame_get_buffer(dest.get(), 0);
|
||||||
@@ -776,15 +855,11 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sws_ctx_
|
if (!sws_ctx_ || sws_src_width_ != f->width ||
|
||||||
|| sws_src_width_ != f->width
|
sws_src_height_ != f->height || sws_src_format_ != f->format ||
|
||||||
|| sws_src_height_ != f->height
|
sws_dst_width_ != dest->width || sws_dst_height_ != dest->height ||
|
||||||
|| sws_src_format_ != f->format
|
sws_dst_format_ != dest->format || sws_colrange_ != dest->color_range ||
|
||||||
|| sws_dst_width_ != dest->width
|
sws_colspace_ != dest->colorspace) {
|
||||||
|| sws_dst_height_ != dest->height
|
|
||||||
|| sws_dst_format_ != dest->format
|
|
||||||
|| sws_colrange_ != dest->color_range
|
|
||||||
|| sws_colspace_ != dest->colorspace) {
|
|
||||||
// SwsContext must be recreated, destroy current if it exists
|
// SwsContext must be recreated, destroy current if it exists
|
||||||
FreeScaler();
|
FreeScaler();
|
||||||
|
|
||||||
@@ -799,27 +874,24 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
|
|||||||
sws_colspace_ = dest->colorspace;
|
sws_colspace_ = dest->colorspace;
|
||||||
|
|
||||||
// Create new scaler
|
// Create new scaler
|
||||||
sws_ctx_ = sws_getContext(sws_src_width_,
|
sws_ctx_ = sws_getContext(sws_src_width_, sws_src_height_,
|
||||||
sws_src_height_,
|
sws_src_format_, sws_dst_width_,
|
||||||
sws_src_format_,
|
sws_dst_height_, sws_dst_format_, SWS_POINT,
|
||||||
sws_dst_width_,
|
nullptr, nullptr, nullptr);
|
||||||
sws_dst_height_,
|
|
||||||
sws_dst_format_,
|
|
||||||
SWS_POINT,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
// Set swscale's colorspace details
|
// Set swscale's colorspace details
|
||||||
sws_setColorspaceDetails(sws_ctx_,
|
sws_setColorspaceDetails(
|
||||||
sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(dest->colorspace)),
|
sws_ctx_,
|
||||||
|
sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(
|
||||||
|
dest->colorspace)),
|
||||||
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0,
|
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0,
|
||||||
sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(dest->colorspace)),
|
sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(
|
||||||
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0,
|
dest->colorspace)),
|
||||||
0, 0x10000, 0x10000);
|
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data, dest->linesize);
|
r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data,
|
||||||
|
dest->linesize);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
FFmpegError(r);
|
FFmpegError(r);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -828,12 +900,16 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, const RetrieveVideoParam
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancelled)
|
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational &time,
|
||||||
|
CancelAtom *cancelled)
|
||||||
{
|
{
|
||||||
int64_t target_ts = Timecode::time_to_timestamp(time, instance_.avstream()->time_base);
|
int64_t target_ts =
|
||||||
|
Timecode::time_to_timestamp(time, instance_.avstream()->time_base);
|
||||||
|
|
||||||
if (instance_.fmt_ctx()->start_time != AV_NOPTS_VALUE) {
|
if (instance_.fmt_ctx()->start_time != AV_NOPTS_VALUE) {
|
||||||
target_ts += av_rescale_q(instance_.fmt_ctx()->start_time, {1, AV_TIME_BASE}, instance_.avstream()->time_base);
|
target_ts += av_rescale_q(instance_.fmt_ctx()->start_time,
|
||||||
|
{ 1, AV_TIME_BASE },
|
||||||
|
instance_.avstream()->time_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int64_t min_seek = 0;
|
const int64_t min_seek = 0;
|
||||||
@@ -842,8 +918,9 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
|
|||||||
|
|
||||||
if (time != kAnyTimecode) {
|
if (time != kAnyTimecode) {
|
||||||
// If the frame wasn't in the frame cache, see if this frame cache is too old to use
|
// If the frame wasn't in the frame cache, see if this frame cache is too old to use
|
||||||
if (cached_frames_.empty()
|
if (cached_frames_.empty() ||
|
||||||
|| (target_ts < cached_frames_.front()->pts || target_ts > cached_frames_.back()->pts + 2*second_ts_)) {
|
(target_ts < cached_frames_.front()->pts ||
|
||||||
|
target_ts > cached_frames_.back()->pts + 2 * second_ts_)) {
|
||||||
ClearFrameCache();
|
ClearFrameCache();
|
||||||
|
|
||||||
instance_.Seek(seek_ts);
|
instance_.Seek(seek_ts);
|
||||||
@@ -891,8 +968,9 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
|
|||||||
if (still_seeking) {
|
if (still_seeking) {
|
||||||
// Handle a failure to seek (occurs on some media)
|
// Handle a failure to seek (occurs on some media)
|
||||||
// We'll only be here if the frame cache was emptied earlier
|
// We'll only be here if the frame cache was emptied earlier
|
||||||
if (!cache_at_zero_ && (ret == AVERROR_EOF || filtered->best_effort_timestamp > target_ts)) {
|
if (!cache_at_zero_ &&
|
||||||
|
(ret == AVERROR_EOF ||
|
||||||
|
filtered->best_effort_timestamp > target_ts)) {
|
||||||
seek_ts = qMax(min_seek, seek_ts - second_ts_);
|
seek_ts = qMax(min_seek, seek_ts - second_ts_);
|
||||||
instance_.Seek(seek_ts);
|
instance_.Seek(seek_ts);
|
||||||
if (seek_ts == min_seek) {
|
if (seek_ts == min_seek) {
|
||||||
@@ -901,19 +979,17 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
still_seeking = false;
|
still_seeking = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == AVERROR_EOF) {
|
if (ret == AVERROR_EOF) {
|
||||||
|
|
||||||
// Handle an "expected" EOF by using the last frame of our cache
|
// Handle an "expected" EOF by using the last frame of our cache
|
||||||
cache_at_eof_ = true;
|
cache_at_eof_ = true;
|
||||||
|
|
||||||
if (cached_frames_.empty()) {
|
if (cached_frames_.empty()) {
|
||||||
qCritical() << "Unexpected codec EOF - unable to retrieve frame";
|
qCritical()
|
||||||
|
<< "Unexpected codec EOF - unable to retrieve frame";
|
||||||
} else {
|
} else {
|
||||||
return_frame = cached_frames_.back();
|
return_frame = cached_frames_.back();
|
||||||
}
|
}
|
||||||
@@ -921,7 +997,6 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Cut down to thread count - 1 before we acquire a new frame
|
// Cut down to thread count - 1 before we acquire a new frame
|
||||||
if (cached_frames_.size() > size_t(MaximumQueueSize())) {
|
if (cached_frames_.size() > size_t(MaximumQueueSize())) {
|
||||||
RemoveFirstFrame();
|
RemoveFirstFrame();
|
||||||
@@ -972,31 +1047,30 @@ void FFmpegDecoder::FreeScaler()
|
|||||||
AVFramePtr FFmpegDecoder::GetFrameFromCache(const int64_t &t) const
|
AVFramePtr FFmpegDecoder::GetFrameFromCache(const int64_t &t) const
|
||||||
{
|
{
|
||||||
if (t < cached_frames_.front()->pts) {
|
if (t < cached_frames_.front()->pts) {
|
||||||
|
|
||||||
if (cache_at_zero_) {
|
if (cache_at_zero_) {
|
||||||
return cached_frames_.front();
|
return cached_frames_.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (t > cached_frames_.back()->pts) {
|
} else if (t > cached_frames_.back()->pts) {
|
||||||
|
|
||||||
if (cache_at_eof_) {
|
if (cache_at_eof_) {
|
||||||
return cached_frames_.back();
|
return cached_frames_.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// We already have this frame in the cache, find it
|
// We already have this frame in the cache, find it
|
||||||
for (auto it=cached_frames_.cbegin(); it!=cached_frames_.cend(); it++) {
|
for (auto it = cached_frames_.cbegin(); it != cached_frames_.cend();
|
||||||
|
it++) {
|
||||||
AVFramePtr this_frame = *it;
|
AVFramePtr this_frame = *it;
|
||||||
|
|
||||||
auto next = it;
|
auto next = it;
|
||||||
next++;
|
next++;
|
||||||
|
|
||||||
if (this_frame->pts == t // Test for an exact match
|
if (this_frame->pts == t // Test for an exact match
|
||||||
|| (next != cached_frames_.cend() && (*next)->pts > t)) { // Or for this frame to be the "closest"
|
||
|
||||||
|
(next != cached_frames_.cend() &&
|
||||||
|
(*next)->pts > t)) { // Or for this frame to be the "closest"
|
||||||
|
|
||||||
return this_frame;
|
return this_frame;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1019,11 +1093,11 @@ int FFmpegDecoder::MaximumQueueSize()
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFmpegDecoder::Instance::Instance() :
|
FFmpegDecoder::Instance::Instance()
|
||||||
fmt_ctx_(nullptr),
|
: fmt_ctx_(nullptr)
|
||||||
codec_ctx_(nullptr),
|
, codec_ctx_(nullptr)
|
||||||
avstream_(nullptr),
|
, avstream_(nullptr)
|
||||||
opts_(nullptr)
|
, opts_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1034,7 +1108,8 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
|
|||||||
|
|
||||||
// Handle format context error
|
// Handle format context error
|
||||||
if (error_code != 0) {
|
if (error_code != 0) {
|
||||||
qCritical() << "Failed to open input:" << filename << FFmpegError(error_code);
|
qCritical()
|
||||||
|
<< "Failed to open input:" << filename << FFmpegError(error_code);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1055,10 +1130,9 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
|
|||||||
|
|
||||||
// Handle failure to find decoder
|
// Handle failure to find decoder
|
||||||
if (codec == nullptr) {
|
if (codec == nullptr) {
|
||||||
qCritical() << "Failed to find appropriate decoder for this codec:"
|
qCritical()
|
||||||
<< filename
|
<< "Failed to find appropriate decoder for this codec:" << filename
|
||||||
<< stream_index
|
<< stream_index << avstream_->codecpar->codec_id;
|
||||||
<< avstream_->codecpar->codec_id;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1074,7 +1148,8 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
|
|||||||
|
|
||||||
// Handle failure to copy parameters
|
// Handle failure to copy parameters
|
||||||
if (error_code < 0) {
|
if (error_code < 0) {
|
||||||
qCritical() << "Failed to copy parameters from AVStream to AVCodecContext";
|
qCritical()
|
||||||
|
<< "Failed to copy parameters from AVStream to AVCodecContext";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1125,8 +1200,9 @@ int FFmpegDecoder::Instance::GetFrame(AVPacket *pkt, AVFrame *frame)
|
|||||||
// Clear any previous frames
|
// Clear any previous frames
|
||||||
av_frame_unref(frame);
|
av_frame_unref(frame);
|
||||||
|
|
||||||
while ((ret = avcodec_receive_frame(codec_ctx_, frame)) == AVERROR(EAGAIN) && !eof) {
|
while ((ret = avcodec_receive_frame(codec_ctx_, frame)) ==
|
||||||
|
AVERROR(EAGAIN) &&
|
||||||
|
!eof) {
|
||||||
// Find next packet in the correct stream index
|
// Find next packet in the correct stream index
|
||||||
ret = GetPacket(pkt);
|
ret = GetPacket(pkt);
|
||||||
|
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ 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
|
||||||
@@ -56,22 +56,31 @@ public:
|
|||||||
|
|
||||||
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 bool ConformAudioInternal(const QVector<QString> &filenames,
|
||||||
|
const AudioParams ¶ms,
|
||||||
|
CancelAtom *cancelled) override;
|
||||||
virtual void CloseInternal() 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();
|
||||||
|
|
||||||
@@ -121,7 +130,6 @@ private:
|
|||||||
AVCodecContext *codec_ctx_;
|
AVCodecContext *codec_ctx_;
|
||||||
AVStream *avstream_;
|
AVStream *avstream_;
|
||||||
AVDictionary *opts_;
|
AVDictionary *opts_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,7 +149,8 @@ private:
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -151,7 +160,9 @@ private:
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -179,7 +190,6 @@ private:
|
|||||||
bool cache_at_eof_;
|
bool cache_at_eof_;
|
||||||
|
|
||||||
Instance instance_;
|
Instance instance_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+192
-112
@@ -30,21 +30,22 @@ extern "C" {
|
|||||||
|
|
||||||
#include "common/ffmpegutils.h"
|
#include "common/ffmpegutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms) :
|
FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms)
|
||||||
Encoder(params),
|
: Encoder(params)
|
||||||
fmt_ctx_(nullptr),
|
, fmt_ctx_(nullptr)
|
||||||
video_stream_(nullptr),
|
, video_stream_(nullptr)
|
||||||
video_codec_ctx_(nullptr),
|
, video_codec_ctx_(nullptr)
|
||||||
video_scale_ctx_(nullptr),
|
, video_scale_ctx_(nullptr)
|
||||||
video_buffersrc_ctx_(nullptr),
|
, video_buffersrc_ctx_(nullptr)
|
||||||
video_buffersink_ctx_(nullptr),
|
, video_buffersink_ctx_(nullptr)
|
||||||
audio_stream_(nullptr),
|
, audio_stream_(nullptr)
|
||||||
audio_codec_ctx_(nullptr),
|
, audio_codec_ctx_(nullptr)
|
||||||
audio_resample_ctx_(nullptr),
|
, audio_resample_ctx_(nullptr)
|
||||||
audio_frame_(nullptr),
|
, audio_frame_(nullptr)
|
||||||
open_(false)
|
, open_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +57,14 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
|||||||
|
|
||||||
if (codec_info) {
|
if (codec_info) {
|
||||||
for (int i = 0; codec_info->pix_fmts[i] != -1; i++) {
|
for (int i = 0; codec_info->pix_fmts[i] != -1; i++) {
|
||||||
if (FFmpegUtils::ConvertJPEGSpaceToRegularSpace(codec_info->pix_fmts[i]) != codec_info->pix_fmts[i]) {
|
if (FFmpegUtils::ConvertJPEGSpaceToRegularSpace(
|
||||||
|
codec_info->pix_fmts[i]) != codec_info->pix_fmts[i]) {
|
||||||
// This is a deprecated "JPEG" space, skip it
|
// This is a deprecated "JPEG" space, skip it
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]);
|
const char *pix_fmt_name =
|
||||||
|
av_get_pix_fmt_name(codec_info->pix_fmts[i]);
|
||||||
pix_fmts.append(pix_fmt_name);
|
pix_fmts.append(pix_fmt_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +72,8 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
|||||||
return pix_fmts;
|
return pix_fmts;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SampleFormat> FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
|
std::vector<SampleFormat>
|
||||||
|
FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
|
||||||
{
|
{
|
||||||
std::vector<SampleFormat> f;
|
std::vector<SampleFormat> f;
|
||||||
|
|
||||||
@@ -77,20 +81,15 @@ std::vector<SampleFormat> FFmpegEncoder::GetSampleFormatsForCodec(ExportCodec::C
|
|||||||
// FFmpeg lists these as separate codecs so we need custom functionality here
|
// FFmpeg lists these as separate codecs so we need custom functionality here
|
||||||
// We list signed 16 first because ExportDialog will always use the first element by default
|
// We list signed 16 first because ExportDialog will always use the first element by default
|
||||||
// (because first element is the "default" in tFFmpeg)
|
// (because first element is the "default" in tFFmpeg)
|
||||||
f = {
|
f = { SampleFormat::S16, SampleFormat::U8, SampleFormat::S32,
|
||||||
SampleFormat::S16,
|
SampleFormat::S64, SampleFormat::F32, SampleFormat::F64 };
|
||||||
SampleFormat::U8,
|
|
||||||
SampleFormat::S32,
|
|
||||||
SampleFormat::S64,
|
|
||||||
SampleFormat::F32,
|
|
||||||
SampleFormat::F64
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
const AVCodec *codec_info = GetEncoder(c, SampleFormat::INVALID);
|
const AVCodec *codec_info = GetEncoder(c, SampleFormat::INVALID);
|
||||||
|
|
||||||
if (codec_info && codec_info->sample_fmts) {
|
if (codec_info && codec_info->sample_fmts) {
|
||||||
for (int i = 0; codec_info->sample_fmts[i] != -1; i++) {
|
for (int i = 0; codec_info->sample_fmts[i] != -1; i++) {
|
||||||
SampleFormat this_format = FFmpegUtils::GetNativeSampleFormat(static_cast<AVSampleFormat>(codec_info->sample_fmts[i]));
|
SampleFormat this_format = FFmpegUtils::GetNativeSampleFormat(
|
||||||
|
static_cast<AVSampleFormat>(codec_info->sample_fmts[i]));
|
||||||
if (this_format != SampleFormat::INVALID) {
|
if (this_format != SampleFormat::INVALID) {
|
||||||
f.push_back(this_format);
|
f.push_back(this_format);
|
||||||
}
|
}
|
||||||
@@ -114,7 +113,8 @@ bool FFmpegEncoder::Open()
|
|||||||
const char *filename_c_str = filename_bytes.constData();
|
const char *filename_c_str = filename_bytes.constData();
|
||||||
|
|
||||||
// Create output format context
|
// Create output format context
|
||||||
error_code = avformat_alloc_output_context2(&fmt_ctx_, nullptr, nullptr, filename_c_str);
|
error_code = avformat_alloc_output_context2(&fmt_ctx_, nullptr, nullptr,
|
||||||
|
filename_c_str);
|
||||||
|
|
||||||
// Check error code
|
// Check error code
|
||||||
if (error_code < 0) {
|
if (error_code < 0) {
|
||||||
@@ -124,7 +124,8 @@ bool FFmpegEncoder::Open()
|
|||||||
|
|
||||||
// Initialize a video stream if it's enabled
|
// Initialize a video stream if it's enabled
|
||||||
if (params().video_enabled()) {
|
if (params().video_enabled()) {
|
||||||
if (!InitializeStream(AVMEDIA_TYPE_VIDEO, &video_stream_, &video_codec_ctx_, params().video_codec())) {
|
if (!InitializeStream(AVMEDIA_TYPE_VIDEO, &video_stream_,
|
||||||
|
&video_codec_ctx_, params().video_codec())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,17 +133,20 @@ bool FFmpegEncoder::Open()
|
|||||||
PixelFormat native_pixel_fmt = params().video_params().format();
|
PixelFormat native_pixel_fmt = params().video_params().format();
|
||||||
|
|
||||||
// This is the format we will need to convert the frame to for swscale to understand it
|
// This is the format we will need to convert the frame to for swscale to understand it
|
||||||
video_conversion_fmt_ = FFmpegUtils::GetCompatiblePixelFormat(native_pixel_fmt);
|
video_conversion_fmt_ =
|
||||||
|
FFmpegUtils::GetCompatiblePixelFormat(native_pixel_fmt);
|
||||||
|
|
||||||
// This is the equivalent pixel format above as an AVPixelFormat that swscale can understand
|
// This is the equivalent pixel format above as an AVPixelFormat that swscale can understand
|
||||||
AVPixelFormat src_alpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_,
|
AVPixelFormat src_alpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(
|
||||||
VideoParams::kRGBAChannelCount);
|
video_conversion_fmt_, VideoParams::kRGBAChannelCount);
|
||||||
|
|
||||||
AVPixelFormat src_noalpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_,
|
AVPixelFormat src_noalpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(
|
||||||
VideoParams::kRGBChannelCount);
|
video_conversion_fmt_, VideoParams::kRGBChannelCount);
|
||||||
|
|
||||||
if (src_alpha_pix_fmt == AV_PIX_FMT_NONE || src_noalpha_pix_fmt == AV_PIX_FMT_NONE) {
|
if (src_alpha_pix_fmt == AV_PIX_FMT_NONE ||
|
||||||
SetError(tr("Failed to find suitable pixel format for this buffer"));
|
src_noalpha_pix_fmt == AV_PIX_FMT_NONE) {
|
||||||
|
SetError(
|
||||||
|
tr("Failed to find suitable pixel format for this buffer"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,17 +161,22 @@ bool FFmpegEncoder::Open()
|
|||||||
static const int FILTER_ARG_SZ = 1024;
|
static const int FILTER_ARG_SZ = 1024;
|
||||||
char filter_args[FILTER_ARG_SZ];
|
char filter_args[FILTER_ARG_SZ];
|
||||||
|
|
||||||
snprintf(filter_args, FILTER_ARG_SZ, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
snprintf(
|
||||||
|
filter_args, FILTER_ARG_SZ,
|
||||||
|
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
||||||
params().video_params().effective_width(),
|
params().video_params().effective_width(),
|
||||||
params().video_params().effective_height(),
|
params().video_params().effective_height(), src_alpha_pix_fmt,
|
||||||
src_alpha_pix_fmt,
|
|
||||||
params().video_params().time_base().numerator(),
|
params().video_params().time_base().numerator(),
|
||||||
params().video_params().time_base().denominator(),
|
params().video_params().time_base().denominator(),
|
||||||
params().video_params().pixel_aspect_ratio().numerator(),
|
params().video_params().pixel_aspect_ratio().numerator(),
|
||||||
params().video_params().pixel_aspect_ratio().denominator());
|
params().video_params().pixel_aspect_ratio().denominator());
|
||||||
|
|
||||||
avfilter_graph_create_filter(&video_buffersrc_ctx_, avfilter_get_by_name("buffer"), "in", filter_args, nullptr, video_scale_ctx_);
|
avfilter_graph_create_filter(&video_buffersrc_ctx_,
|
||||||
avfilter_graph_create_filter(&video_buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, video_scale_ctx_);
|
avfilter_get_by_name("buffer"), "in",
|
||||||
|
filter_args, nullptr, video_scale_ctx_);
|
||||||
|
avfilter_graph_create_filter(&video_buffersink_ctx_,
|
||||||
|
avfilter_get_by_name("buffersink"), "out",
|
||||||
|
nullptr, nullptr, video_scale_ctx_);
|
||||||
|
|
||||||
AVFilterContext *last_filter = video_buffersrc_ctx_;
|
AVFilterContext *last_filter = video_buffersrc_ctx_;
|
||||||
|
|
||||||
@@ -176,9 +185,15 @@ bool FFmpegEncoder::Open()
|
|||||||
AVFilterContext *range_filter;
|
AVFilterContext *range_filter;
|
||||||
|
|
||||||
snprintf(filter_args, FILTER_ARG_SZ, "in_range=full:out_range=%s",
|
snprintf(filter_args, FILTER_ARG_SZ, "in_range=full:out_range=%s",
|
||||||
params().video_params().color_range() == VideoParams::kColorRangeFull ? "full" : "limited");
|
params().video_params().color_range() ==
|
||||||
|
VideoParams::kColorRangeFull ?
|
||||||
|
"full" :
|
||||||
|
"limited");
|
||||||
|
|
||||||
avfilter_graph_create_filter(&range_filter, avfilter_get_by_name("scale"), "range", filter_args, nullptr, video_scale_ctx_);
|
avfilter_graph_create_filter(&range_filter,
|
||||||
|
avfilter_get_by_name("scale"), "range",
|
||||||
|
filter_args, nullptr,
|
||||||
|
video_scale_ctx_);
|
||||||
|
|
||||||
avfilter_link(last_filter, 0, range_filter, 0);
|
avfilter_link(last_filter, 0, range_filter, 0);
|
||||||
last_filter = range_filter;
|
last_filter = range_filter;
|
||||||
@@ -188,9 +203,13 @@ bool FFmpegEncoder::Open()
|
|||||||
// Transform pixel format
|
// Transform pixel format
|
||||||
AVFilterContext *format_filter;
|
AVFilterContext *format_filter;
|
||||||
|
|
||||||
snprintf(filter_args, FILTER_ARG_SZ, "pix_fmts=%u", encoder_pix_fmt);
|
snprintf(filter_args, FILTER_ARG_SZ, "pix_fmts=%u",
|
||||||
|
encoder_pix_fmt);
|
||||||
|
|
||||||
avfilter_graph_create_filter(&format_filter, avfilter_get_by_name("format"), "format", filter_args, nullptr, video_scale_ctx_);
|
avfilter_graph_create_filter(&format_filter,
|
||||||
|
avfilter_get_by_name("format"),
|
||||||
|
"format", filter_args, nullptr,
|
||||||
|
video_scale_ctx_);
|
||||||
|
|
||||||
avfilter_link(last_filter, 0, format_filter, 0);
|
avfilter_link(last_filter, 0, format_filter, 0);
|
||||||
last_filter = format_filter;
|
last_filter = format_filter;
|
||||||
@@ -206,14 +225,17 @@ bool FFmpegEncoder::Open()
|
|||||||
|
|
||||||
// Initialize an audio stream if it's enabled
|
// Initialize an audio stream if it's enabled
|
||||||
if (params().audio_enabled()) {
|
if (params().audio_enabled()) {
|
||||||
if (!InitializeStream(AVMEDIA_TYPE_AUDIO, &audio_stream_, &audio_codec_ctx_, params().audio_codec())) {
|
if (!InitializeStream(AVMEDIA_TYPE_AUDIO, &audio_stream_,
|
||||||
|
&audio_codec_ctx_, params().audio_codec())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a subtitle stream if it's enabled
|
// Initialize a subtitle stream if it's enabled
|
||||||
if (params().subtitles_enabled()) {
|
if (params().subtitles_enabled()) {
|
||||||
if (!InitializeStream(AVMEDIA_TYPE_SUBTITLE, &subtitle_stream_, &subtitle_codec_ctx_, params().subtitles_codec())) {
|
if (!InitializeStream(AVMEDIA_TYPE_SUBTITLE, &subtitle_stream_,
|
||||||
|
&subtitle_codec_ctx_,
|
||||||
|
params().subtitles_codec())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,7 +271,8 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
|
|||||||
AVFramePtr input_frame = CreateAVFramePtr(av_frame_alloc());
|
AVFramePtr input_frame = CreateAVFramePtr(av_frame_alloc());
|
||||||
input_frame->width = frame->width();
|
input_frame->width = frame->width();
|
||||||
input_frame->height = frame->height();
|
input_frame->height = frame->height();
|
||||||
input_frame->format = FFmpegUtils::GetFFmpegPixelFormat(frame->format(), frame->channel_count());
|
input_frame->format = FFmpegUtils::GetFFmpegPixelFormat(
|
||||||
|
frame->format(), frame->channel_count());
|
||||||
input_frame->data[0] = reinterpret_cast<uint8_t *>(frame->data());
|
input_frame->data[0] = reinterpret_cast<uint8_t *>(frame->data());
|
||||||
input_frame->linesize[0] = frame->linesize_bytes();
|
input_frame->linesize[0] = frame->linesize_bytes();
|
||||||
|
|
||||||
@@ -259,7 +282,8 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
|
|||||||
input_frame->color_range = video_codec_ctx_->color_range;
|
input_frame->color_range = video_codec_ctx_->color_range;
|
||||||
|
|
||||||
int r;
|
int r;
|
||||||
r = av_buffersrc_add_frame_flags(video_buffersrc_ctx_, input_frame.get(), AV_BUFFERSRC_FLAG_KEEP_REF);
|
r = av_buffersrc_add_frame_flags(video_buffersrc_ctx_, input_frame.get(),
|
||||||
|
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
FFmpegError(tr("Failed to add frame to filter graph"), r);
|
FFmpegError(tr("Failed to add frame to filter graph"), r);
|
||||||
return false;
|
return false;
|
||||||
@@ -272,7 +296,8 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded_frame->pts = qRound64(time.toDouble() / av_q2d(video_codec_ctx_->time_base));
|
encoded_frame->pts =
|
||||||
|
qRound64(time.toDouble() / av_q2d(video_codec_ctx_->time_base));
|
||||||
|
|
||||||
return WriteAVFrame(encoded_frame.get(), video_codec_ctx_, video_stream_);
|
return WriteAVFrame(encoded_frame.get(), video_codec_ctx_, video_stream_);
|
||||||
}
|
}
|
||||||
@@ -295,8 +320,11 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
|
|||||||
size_t input_sample_count = std::min(end - start, max_frame);
|
size_t input_sample_count = std::min(end - start, max_frame);
|
||||||
int input_linesize;
|
int input_linesize;
|
||||||
|
|
||||||
int r = av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio.audio_params().channel_count(),
|
int r = av_samples_alloc_array_and_samples(
|
||||||
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio.audio_params().format()), 0);
|
&input_data, &input_linesize, audio.audio_params().channel_count(),
|
||||||
|
input_sample_count,
|
||||||
|
FFmpegUtils::GetFFmpegSampleFormat(audio.audio_params().format()),
|
||||||
|
0);
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
FFmpegError(tr("Failed to allocate sample array"), r);
|
FFmpegError(tr("Failed to allocate sample array"), r);
|
||||||
@@ -304,13 +332,18 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
|
|||||||
} else {
|
} else {
|
||||||
int bpsc = audio.audio_params().bytes_per_sample_per_channel();
|
int bpsc = audio.audio_params().bytes_per_sample_per_channel();
|
||||||
for (int i = 0; i < audio.audio_params().channel_count(); i++) {
|
for (int i = 0; i < audio.audio_params().channel_count(); i++) {
|
||||||
memcpy(input_data[i], audio.data(i) + start, input_sample_count * bpsc);
|
memcpy(input_data[i], audio.data(i) + start,
|
||||||
|
input_sample_count * bpsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
start += input_sample_count;
|
start += input_sample_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = WriteAudioData(audio.audio_params().is_valid() ? audio.audio_params() : params().audio_params(), const_cast<const uint8_t**>(input_data), input_sample_count);
|
result = WriteAudioData(audio.audio_params().is_valid() ?
|
||||||
|
audio.audio_params() :
|
||||||
|
params().audio_params(),
|
||||||
|
const_cast<const uint8_t **>(input_data),
|
||||||
|
input_sample_count);
|
||||||
|
|
||||||
if (input_data) {
|
if (input_data) {
|
||||||
av_freep(&input_data[0]);
|
av_freep(&input_data[0]);
|
||||||
@@ -321,7 +354,9 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_t **input_data, int input_sample_count)
|
bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params,
|
||||||
|
const uint8_t **input_data,
|
||||||
|
int input_sample_count)
|
||||||
{
|
{
|
||||||
if (!InitializeResampleContext(audio_params)) {
|
if (!InitializeResampleContext(audio_params)) {
|
||||||
qCritical() << "Failed to initialize resample context";
|
qCritical() << "Failed to initialize resample context";
|
||||||
@@ -331,32 +366,46 @@ bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_
|
|||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
// Create output buffer
|
// Create output buffer
|
||||||
int output_sample_count = input_sample_count ? swr_get_out_samples(audio_resample_ctx_, input_sample_count) : 102400;
|
int output_sample_count =
|
||||||
|
input_sample_count ?
|
||||||
|
swr_get_out_samples(audio_resample_ctx_, input_sample_count) :
|
||||||
|
102400;
|
||||||
uint8_t **output_data = nullptr;
|
uint8_t **output_data = nullptr;
|
||||||
int output_linesize;
|
int output_linesize;
|
||||||
av_samples_alloc_array_and_samples(&output_data, &output_linesize, audio_stream_->codecpar->ch_layout.nb_channels,
|
av_samples_alloc_array_and_samples(
|
||||||
output_sample_count, static_cast<AVSampleFormat>(audio_stream_->codecpar->format), 0);
|
&output_data, &output_linesize,
|
||||||
|
audio_stream_->codecpar->ch_layout.nb_channels, output_sample_count,
|
||||||
|
static_cast<AVSampleFormat>(audio_stream_->codecpar->format), 0);
|
||||||
|
|
||||||
// Perform conversion
|
// Perform conversion
|
||||||
int converted = swr_convert(audio_resample_ctx_, output_data, output_sample_count, const_cast<const uint8_t**>(input_data), input_sample_count);
|
int converted = swr_convert(audio_resample_ctx_, output_data,
|
||||||
|
output_sample_count,
|
||||||
|
const_cast<const uint8_t **>(input_data),
|
||||||
|
input_sample_count);
|
||||||
if (converted > 0) {
|
if (converted > 0) {
|
||||||
// Split sample buffer into frames
|
// Split sample buffer into frames
|
||||||
for (int i = 0; i < converted;) {
|
for (int i = 0; i < converted;) {
|
||||||
int frame_remaining_samples = audio_max_samples_ - audio_frame_offset_;
|
int frame_remaining_samples =
|
||||||
|
audio_max_samples_ - audio_frame_offset_;
|
||||||
int converted_remaining_samples = converted - i;
|
int converted_remaining_samples = converted - i;
|
||||||
|
|
||||||
int copy_length = qMin(frame_remaining_samples, converted_remaining_samples);
|
int copy_length =
|
||||||
|
qMin(frame_remaining_samples, converted_remaining_samples);
|
||||||
|
|
||||||
av_samples_copy(audio_frame_->data, output_data, audio_frame_offset_, i,
|
av_samples_copy(audio_frame_->data, output_data,
|
||||||
copy_length,
|
audio_frame_offset_, i, copy_length,
|
||||||
audio_frame_->ch_layout.nb_channels, static_cast<AVSampleFormat>(audio_frame_->format));
|
audio_frame_->ch_layout.nb_channels,
|
||||||
|
static_cast<AVSampleFormat>(audio_frame_->format));
|
||||||
|
|
||||||
audio_frame_offset_ += copy_length;
|
audio_frame_offset_ += copy_length;
|
||||||
i += copy_length;
|
i += copy_length;
|
||||||
|
|
||||||
if (audio_frame_offset_ == audio_max_samples_ || (i == converted && !input_data)) {
|
if (audio_frame_offset_ == audio_max_samples_ ||
|
||||||
|
(i == converted && !input_data)) {
|
||||||
// Got all the samples we needed, write the frame
|
// Got all the samples we needed, write the frame
|
||||||
audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base);
|
audio_frame_->pts = av_rescale_q(
|
||||||
|
audio_write_count_, { 1, audio_codec_ctx_->sample_rate },
|
||||||
|
audio_codec_ctx_->time_base);
|
||||||
|
|
||||||
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
|
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
|
||||||
audio_write_count_ += audio_frame_offset_;
|
audio_write_count_ += audio_frame_offset_;
|
||||||
@@ -370,7 +419,9 @@ bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_
|
|||||||
|
|
||||||
if (!input_data && audio_frame_offset_ > 0) {
|
if (!input_data && audio_frame_offset_ > 0) {
|
||||||
audio_frame_->nb_samples = audio_frame_offset_;
|
audio_frame_->nb_samples = audio_frame_offset_;
|
||||||
audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base);
|
audio_frame_->pts = av_rescale_q(audio_write_count_,
|
||||||
|
{ 1, audio_codec_ctx_->sample_rate },
|
||||||
|
audio_codec_ctx_->time_base);
|
||||||
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
|
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,12 +443,11 @@ QString GetAssTime(const rational &time)
|
|||||||
int64_t mm = (total_centiseconds / 6000) % 60;
|
int64_t mm = (total_centiseconds / 6000) % 60;
|
||||||
int64_t hh = total_centiseconds / 360000;
|
int64_t hh = total_centiseconds / 360000;
|
||||||
|
|
||||||
return QStringLiteral("%1:%2:%3.%4").arg(
|
return QStringLiteral("%1:%2:%3.%4")
|
||||||
QString::number(hh),
|
.arg(QString::number(hh),
|
||||||
QStringLiteral("%1").arg(mm, 2, 10, QLatin1Char('0')),
|
QStringLiteral("%1").arg(mm, 2, 10, QLatin1Char('0')),
|
||||||
QStringLiteral("%1").arg(ss, 2, 10, QLatin1Char('0')),
|
QStringLiteral("%1").arg(ss, 2, 10, QLatin1Char('0')),
|
||||||
QStringLiteral("%1").arg(cs, 2, 10, QLatin1Char('0'))
|
QStringLiteral("%1").arg(cs, 2, 10, QLatin1Char('0')));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
||||||
@@ -409,10 +459,14 @@ bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
|||||||
pkt->stream_index = subtitle_stream_->index;
|
pkt->stream_index = subtitle_stream_->index;
|
||||||
pkt->data = (uint8_t *)utf8_sub.data();
|
pkt->data = (uint8_t *)utf8_sub.data();
|
||||||
pkt->size = utf8_sub.size();
|
pkt->size = utf8_sub.size();
|
||||||
pkt->pts = Timecode::time_to_timestamp(sub_block->in(), subtitle_codec_ctx_->time_base, Timecode::kFloor);
|
pkt->pts = Timecode::time_to_timestamp(
|
||||||
pkt->duration = av_rescale_q(qRound64(sub_block->length().toDouble() * 1000), {1, 1000}, subtitle_codec_ctx_->time_base);
|
sub_block->in(), subtitle_codec_ctx_->time_base, Timecode::kFloor);
|
||||||
|
pkt->duration =
|
||||||
|
av_rescale_q(qRound64(sub_block->length().toDouble() * 1000),
|
||||||
|
{ 1, 1000 }, subtitle_codec_ctx_->time_base);
|
||||||
pkt->dts = pkt->pts;
|
pkt->dts = pkt->pts;
|
||||||
av_packet_rescale_ts(pkt, subtitle_codec_ctx_->time_base, subtitle_stream_->time_base);
|
av_packet_rescale_ts(pkt, subtitle_codec_ctx_->time_base,
|
||||||
|
subtitle_stream_->time_base);
|
||||||
|
|
||||||
int err = av_interleaved_write_frame(fmt_ctx_, pkt);
|
int err = av_interleaved_write_frame(fmt_ctx_, pkt);
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
@@ -541,12 +595,14 @@ void FFmpegEncoder::FFmpegError(const QString& context, int error_code)
|
|||||||
char err[1024];
|
char err[1024];
|
||||||
av_strerror(error_code, err, 1024);
|
av_strerror(error_code, err, 1024);
|
||||||
|
|
||||||
QString formatted_err = tr("%1: %2 %3").arg(context, err, QString::number(error_code));
|
QString formatted_err =
|
||||||
|
tr("%1: %2 %3").arg(context, err, QString::number(error_code));
|
||||||
qDebug() << formatted_err;
|
qDebug() << formatted_err;
|
||||||
SetError(formatted_err);
|
SetError(formatted_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext* codec_ctx, AVStream* stream)
|
bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
|
||||||
|
AVStream *stream)
|
||||||
{
|
{
|
||||||
// Send raw frame to the encoder
|
// Send raw frame to the encoder
|
||||||
int error_code = avcodec_send_frame(codec_ctx, frame);
|
int error_code = avcodec_send_frame(codec_ctx, frame);
|
||||||
@@ -567,7 +623,8 @@ bool FFmpegEncoder::WriteAVFrame(AVFrame *frame, AVCodecContext* codec_ctx, AVSt
|
|||||||
if (error_code == AVERROR(EAGAIN)) {
|
if (error_code == AVERROR(EAGAIN)) {
|
||||||
break;
|
break;
|
||||||
} else if (error_code < 0) {
|
} else if (error_code < 0) {
|
||||||
FFmpegError(tr("Failed to receive packet from decoder"), error_code);
|
FFmpegError(tr("Failed to receive packet from decoder"),
|
||||||
|
error_code);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,22 +652,29 @@ fail:
|
|||||||
return succeeded;
|
return succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AVCodecContext** codec_ctx_ptr, const ExportCodec::Codec& codec)
|
bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream **stream_ptr,
|
||||||
|
AVCodecContext **codec_ctx_ptr,
|
||||||
|
const ExportCodec::Codec &codec)
|
||||||
{
|
{
|
||||||
if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_SUBTITLE) {
|
if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO &&
|
||||||
SetError(tr("Cannot initialize a stream that is not a video, audio, or subtitle type"));
|
type != AVMEDIA_TYPE_SUBTITLE) {
|
||||||
|
SetError(tr(
|
||||||
|
"Cannot initialize a stream that is not a video, audio, or subtitle type"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find encoder
|
// Find encoder
|
||||||
const AVCodec* encoder = GetEncoder(codec, params().audio_params().format());
|
const AVCodec *encoder =
|
||||||
|
GetEncoder(codec, params().audio_params().format());
|
||||||
if (!encoder) {
|
if (!encoder) {
|
||||||
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
|
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encoder->type != type) {
|
if (encoder->type != type) {
|
||||||
SetError(tr("Retrieved unexpected codec type %1 for codec %2").arg(QString::number(encoder->type), QString::number(codec)));
|
SetError(
|
||||||
|
tr("Retrieved unexpected codec type %1 for codec %2")
|
||||||
|
.arg(QString::number(encoder->type), QString::number(codec)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,35 +689,47 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
if (type == AVMEDIA_TYPE_VIDEO) {
|
if (type == AVMEDIA_TYPE_VIDEO) {
|
||||||
codec_ctx->width = params().video_params().width();
|
codec_ctx->width = params().video_params().width();
|
||||||
codec_ctx->height = params().video_params().height();
|
codec_ctx->height = params().video_params().height();
|
||||||
codec_ctx->sample_aspect_ratio = params().video_params().pixel_aspect_ratio().toAVRational();
|
codec_ctx->sample_aspect_ratio =
|
||||||
codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational();
|
params().video_params().pixel_aspect_ratio().toAVRational();
|
||||||
codec_ctx->framerate = params().video_params().frame_rate().toAVRational();
|
codec_ctx->time_base =
|
||||||
|
params().video_params().frame_rate_as_time_base().toAVRational();
|
||||||
|
codec_ctx->framerate =
|
||||||
|
params().video_params().frame_rate().toAVRational();
|
||||||
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
|
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
|
||||||
codec_ctx->color_range = params().video_params().color_range() == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
|
codec_ctx->color_range = params().video_params().color_range() ==
|
||||||
|
VideoParams::kColorRangeFull ?
|
||||||
|
AVCOL_RANGE_JPEG :
|
||||||
|
AVCOL_RANGE_MPEG;
|
||||||
|
|
||||||
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) {
|
if (params().video_params().interlacing() !=
|
||||||
|
VideoParams::kInterlaceNone) {
|
||||||
// FIXME: I actually don't know what these flags do, the documentation helpfully doesn't
|
// FIXME: I actually don't know what these flags do, the documentation helpfully doesn't
|
||||||
// explain them at all. I hope using both of them is the right thing to do.
|
// explain them at all. I hope using both of them is the right thing to do.
|
||||||
codec_ctx->flags |= AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME;
|
codec_ctx->flags |= AV_CODEC_FLAG_INTERLACED_DCT |
|
||||||
|
AV_CODEC_FLAG_INTERLACED_ME;
|
||||||
|
|
||||||
|
if (params().video_params().interlacing() ==
|
||||||
if (params().video_params().interlacing() == VideoParams::kInterlacedTopFirst) {
|
VideoParams::kInterlacedTopFirst) {
|
||||||
codec_ctx->field_order = AV_FIELD_TT;
|
codec_ctx->field_order = AV_FIELD_TT;
|
||||||
} else {
|
} else {
|
||||||
codec_ctx->field_order = AV_FIELD_BB;
|
codec_ctx->field_order = AV_FIELD_BB;
|
||||||
|
|
||||||
if (codec == ExportCodec::kCodecH264 || codec == ExportCodec::kCodecH264rgb) {
|
if (codec == ExportCodec::kCodecH264 ||
|
||||||
|
codec == ExportCodec::kCodecH264rgb) {
|
||||||
// For some reason, FFmpeg doesn't set libx264's bff flag so we have to do it ourselves
|
// For some reason, FFmpeg doesn't set libx264's bff flag so we have to do it ourselves
|
||||||
av_opt_set(codec_ctx->priv_data, "x264opts", "bff=1", AV_OPT_SEARCH_CHILDREN);
|
av_opt_set(codec_ctx->priv_data, "x264opts", "bff=1",
|
||||||
|
AV_OPT_SEARCH_CHILDREN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set custom options
|
// Set custom options
|
||||||
{
|
{
|
||||||
for (auto i=params().video_opts().begin();i!=params().video_opts().end();i++) {
|
for (auto i = params().video_opts().begin();
|
||||||
|
i != params().video_opts().end(); i++) {
|
||||||
if (!i.key().startsWith(QStringLiteral("ove_"))) {
|
if (!i.key().startsWith(QStringLiteral("ove_"))) {
|
||||||
av_opt_set(codec_ctx->priv_data, i.key().toUtf8(), i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN);
|
av_opt_set(codec_ctx->priv_data, i.key().toUtf8(),
|
||||||
|
i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,12 +746,14 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (params().video_buffer_size() > 0) {
|
if (params().video_buffer_size() > 0) {
|
||||||
codec_ctx->rc_buffer_size = static_cast<int>(params().video_buffer_size());
|
codec_ctx->rc_buffer_size =
|
||||||
|
static_cast<int>(params().video_buffer_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// nclc tags. See https://ffmpeg.org/doxygen/4.0/pixfmt_8h.html#ad384ee5a840bafd73daef08e6d9cafe7
|
// nclc tags. See https://ffmpeg.org/doxygen/4.0/pixfmt_8h.html#ad384ee5a840bafd73daef08e6d9cafe7
|
||||||
// ffprobe -v error -show_format -show_streams "C:\Users\Tom\Documents\srgb correct tags.mov"
|
// ffprobe -v error -show_format -show_streams "C:\Users\Tom\Documents\srgb correct tags.mov"
|
||||||
if (params().color_transform().output().contains(QStringLiteral("sRGB"), Qt::CaseInsensitive)) {
|
if (params().color_transform().output().contains(
|
||||||
|
QStringLiteral("sRGB"), Qt::CaseInsensitive)) {
|
||||||
codec_ctx->color_primaries = AVCOL_PRI_BT709;
|
codec_ctx->color_primaries = AVCOL_PRI_BT709;
|
||||||
codec_ctx->color_trc = AVCOL_TRC_IEC61966_2_1;
|
codec_ctx->color_trc = AVCOL_TRC_IEC61966_2_1;
|
||||||
codec_ctx->colorspace = AVCOL_SPC_BT709;
|
codec_ctx->colorspace = AVCOL_SPC_BT709;
|
||||||
@@ -687,11 +765,11 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (type == AVMEDIA_TYPE_AUDIO) {
|
} else if (type == AVMEDIA_TYPE_AUDIO) {
|
||||||
|
|
||||||
// Assume audio stream
|
// Assume audio stream
|
||||||
codec_ctx->sample_rate = params().audio_params().sample_rate();
|
codec_ctx->sample_rate = params().audio_params().sample_rate();
|
||||||
codec_ctx->ch_layout = params().audio_params().channel_layout();
|
codec_ctx->ch_layout = params().audio_params().channel_layout();
|
||||||
codec_ctx->sample_fmt = FFmpegUtils::GetFFmpegSampleFormat(params().audio_params().format());
|
codec_ctx->sample_fmt = FFmpegUtils::GetFFmpegSampleFormat(
|
||||||
|
params().audio_params().format());
|
||||||
codec_ctx->time_base = { 1, codec_ctx->sample_rate };
|
codec_ctx->time_base = { 1, codec_ctx->sample_rate };
|
||||||
|
|
||||||
if (params().audio_bit_rate() > 0) {
|
if (params().audio_bit_rate() > 0) {
|
||||||
@@ -699,14 +777,13 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (type == AVMEDIA_TYPE_SUBTITLE) {
|
} else if (type == AVMEDIA_TYPE_SUBTITLE) {
|
||||||
|
|
||||||
codec_ctx->time_base = av_get_time_base_q();
|
codec_ctx->time_base = av_get_time_base_q();
|
||||||
|
|
||||||
QByteArray ass_header = SubtitleParams::GenerateASSHeader().toUtf8();
|
QByteArray ass_header = SubtitleParams::GenerateASSHeader().toUtf8();
|
||||||
codec_ctx->subtitle_header = new uint8_t[ass_header.size()];
|
codec_ctx->subtitle_header = new uint8_t[ass_header.size()];
|
||||||
memcpy(codec_ctx->subtitle_header, ass_header.constData(), ass_header.size());
|
memcpy(codec_ctx->subtitle_header, ass_header.constData(),
|
||||||
|
ass_header.size());
|
||||||
codec_ctx->subtitle_header_size = ass_header.size();
|
codec_ctx->subtitle_header_size = ass_header.size();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetupCodecContext(stream, codec_ctx, encoder)) {
|
if (!SetupCodecContext(stream, codec_ctx, encoder)) {
|
||||||
@@ -716,7 +793,9 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx, const AVCodec* codec)
|
bool FFmpegEncoder::InitializeCodecContext(AVStream **stream,
|
||||||
|
AVCodecContext **codec_ctx,
|
||||||
|
const AVCodec *codec)
|
||||||
{
|
{
|
||||||
*stream = avformat_new_stream(fmt_ctx_, nullptr);
|
*stream = avformat_new_stream(fmt_ctx_, nullptr);
|
||||||
if (!(*stream)) {
|
if (!(*stream)) {
|
||||||
@@ -734,7 +813,9 @@ bool FFmpegEncoder::InitializeCodecContext(AVStream **stream, AVCodecContext **c
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ctx, const AVCodec* codec)
|
bool FFmpegEncoder::SetupCodecContext(AVStream *stream,
|
||||||
|
AVCodecContext *codec_ctx,
|
||||||
|
const AVCodec *codec)
|
||||||
{
|
{
|
||||||
int error_code;
|
int error_code;
|
||||||
|
|
||||||
@@ -762,7 +843,8 @@ bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ct
|
|||||||
// Copy context settings to codecpar object
|
// Copy context settings to codecpar object
|
||||||
error_code = avcodec_parameters_from_context(stream->codecpar, codec_ctx);
|
error_code = avcodec_parameters_from_context(stream->codecpar, codec_ctx);
|
||||||
if (error_code < 0) {
|
if (error_code < 0) {
|
||||||
FFmpegError(tr("Failed to copy codec parameters to stream"), error_code);
|
FFmpegError(tr("Failed to copy codec parameters to stream"),
|
||||||
|
error_code);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -828,15 +910,11 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
|
|||||||
}
|
}
|
||||||
AVChannelLayout layout = audio.channel_layout();
|
AVChannelLayout layout = audio.channel_layout();
|
||||||
// Create resample context
|
// Create resample context
|
||||||
swr_alloc_set_opts2(&audio_resample_ctx_,
|
swr_alloc_set_opts2(&audio_resample_ctx_, &audio_codec_ctx_->ch_layout,
|
||||||
&audio_codec_ctx_->ch_layout,
|
|
||||||
audio_codec_ctx_->sample_fmt,
|
audio_codec_ctx_->sample_fmt,
|
||||||
audio_codec_ctx_->sample_rate,
|
audio_codec_ctx_->sample_rate, &layout,
|
||||||
&layout,
|
|
||||||
FFmpegUtils::GetFFmpegSampleFormat(audio.format()),
|
FFmpegUtils::GetFFmpegSampleFormat(audio.format()),
|
||||||
audio.sample_rate(),
|
audio.sample_rate(), 0, nullptr);
|
||||||
0,
|
|
||||||
nullptr);
|
|
||||||
if (!audio_resample_ctx_) {
|
if (!audio_resample_ctx_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -852,7 +930,8 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
|
|||||||
// If not, use another frame size
|
// If not, use another frame size
|
||||||
if (params().video_enabled()) {
|
if (params().video_enabled()) {
|
||||||
// If we're encoding video, use enough samples to cover roughly one frame of video
|
// If we're encoding video, use enough samples to cover roughly one frame of video
|
||||||
audio_max_samples_ = params().audio_params().time_to_samples(params().video_params().frame_rate_as_time_base());
|
audio_max_samples_ = params().audio_params().time_to_samples(
|
||||||
|
params().video_params().frame_rate_as_time_base());
|
||||||
} else {
|
} else {
|
||||||
// If no video, just use an arbitrary number
|
// If no video, just use an arbitrary number
|
||||||
audio_max_samples_ = 256;
|
audio_max_samples_ = 256;
|
||||||
@@ -880,7 +959,8 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c, SampleFormat aformat)
|
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c,
|
||||||
|
SampleFormat aformat)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case ExportCodec::kCodecH264:
|
case ExportCodec::kCodecH264:
|
||||||
|
|||||||
@@ -31,25 +31,29 @@ extern "C" {
|
|||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "codec/encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class FFmpegEncoder : public Encoder
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class FFmpegEncoder : public Encoder {
|
||||||
Q_OBJECT
|
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;
|
||||||
|
|
||||||
@@ -71,18 +75,24 @@ private:
|
|||||||
*/
|
*/
|
||||||
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_;
|
||||||
|
|
||||||
@@ -105,7 +115,6 @@ private:
|
|||||||
AVCodecContext *subtitle_codec_ctx_;
|
AVCodecContext *subtitle_codec_ctx_;
|
||||||
|
|
||||||
bool open_;
|
bool open_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-19
@@ -28,12 +28,13 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,14 +57,16 @@ 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(),
|
||||||
|
params_.channel_count());
|
||||||
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
|
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()
|
||||||
|
<< "Tried to interlace two frames that had incompatible parameters";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,17 +80,18 @@ FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
|
|||||||
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
|
||||||
@@ -96,9 +100,11 @@ Color Frame::get_pixel(int x, int y) const
|
|||||||
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
|
||||||
@@ -112,9 +118,11 @@ void Frame::set_pixel(int x, int y, const Color &c)
|
|||||||
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()
|
||||||
@@ -159,14 +167,14 @@ FramePtr Frame::convert(PixelFormat format) const
|
|||||||
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)) {
|
||||||
|
|||||||
+5
-5
@@ -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,8 +37,7 @@ 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();
|
||||||
|
|
||||||
@@ -52,7 +52,8 @@ public:
|
|||||||
|
|
||||||
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
|
||||||
{
|
{
|
||||||
@@ -164,7 +165,6 @@ private:
|
|||||||
int linesize_;
|
int linesize_;
|
||||||
|
|
||||||
int linesize_pixels_;
|
int linesize_pixels_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,12 +31,13 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +46,8 @@ 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)
|
||||||
|
|
||||||
@@ -85,8 +87,10 @@ FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cance
|
|||||||
// 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);
|
||||||
@@ -124,8 +128,7 @@ 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();
|
||||||
@@ -137,7 +140,9 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
|||||||
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();
|
||||||
@@ -146,15 +151,18 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
|||||||
|
|
||||||
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,
|
||||||
|
static_cast<uint8_t *>(buf.localpixels()) +
|
||||||
|
buf.scanline_stride() * src_y + px_sz * src_x,
|
||||||
px_sz);
|
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()
|
||||||
@@ -170,7 +178,9 @@ bool OIIODecoder::FileTypeIsSupported(const QString& fn)
|
|||||||
|
|
||||||
// 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) {
|
||||||
@@ -180,7 +190,8 @@ bool OIIODecoder::FileTypeIsSupported(const QString& fn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) {
|
if (!supported_formats_.contains(QFileInfo(fn).suffix(),
|
||||||
|
Qt::CaseInsensitive)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,17 +214,20 @@ bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
|
|||||||
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()
|
||||||
|
<< "Failed to convert OIIO::ImageDesc to native pixel format";
|
||||||
return false;
|
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()
|
||||||
|
<< "Failed to determine appropriate OIIO basetype from native format";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,15 +244,18 @@ void OIIODecoder::CloseImageHandle()
|
|||||||
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(
|
||||||
|
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
|
||||||
video_params.set_channel_count(spec.nchannels);
|
video_params.set_channel_count(spec.nchannels);
|
||||||
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
|
video_params.set_pixel_aspect_ratio(
|
||||||
|
OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
|
||||||
video_params.set_video_type(VideoParams::kVideoTypeStill);
|
video_params.set_video_type(VideoParams::kVideoTypeStill);
|
||||||
|
|
||||||
return video_params;
|
return video_params;
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
|
|
||||||
#include "codec/decoder.h"
|
#include "codec/decoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class OIIODecoder : public Decoder
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class OIIODecoder : public Decoder {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
OIIODecoder();
|
OIIODecoder();
|
||||||
@@ -38,13 +38,18 @@ public:
|
|||||||
|
|
||||||
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
|
||||||
|
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
|
||||||
virtual void CloseInternal() override;
|
virtual void CloseInternal() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -65,7 +70,6 @@ private:
|
|||||||
RetrieveVideoParams last_params_;
|
RetrieveVideoParams last_params_;
|
||||||
|
|
||||||
static QStringList supported_formats_;
|
static QStringList supported_formats_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,12 @@
|
|||||||
|
|
||||||
#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()
|
||||||
@@ -45,13 +45,15 @@ bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
frame->linesize_bytes())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "codec/encoder.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class OIIOEncoder : public Encoder
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class OIIOEncoder : public Encoder {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
OIIOEncoder(const EncodingParams ¶ms);
|
OIIOEncoder(const EncodingParams ¶ms);
|
||||||
@@ -34,12 +34,12 @@ public:
|
|||||||
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,
|
||||||
|
olive::core::rational time) override;
|
||||||
virtual bool WriteAudio(const SampleBuffer &audio) override;
|
virtual bool WriteAudio(const SampleBuffer &audio) override;
|
||||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||||
|
|
||||||
virtual void Close() override;
|
virtual void Close() override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,12 @@
|
|||||||
|
|
||||||
#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()
|
||||||
@@ -33,7 +33,8 @@ 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
|
||||||
@@ -54,7 +55,8 @@ bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMo
|
|||||||
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;
|
||||||
|
|
||||||
@@ -68,7 +70,8 @@ qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offs
|
|||||||
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;
|
||||||
|
|
||||||
|
|||||||
@@ -25,12 +25,12 @@
|
|||||||
#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);
|
||||||
@@ -46,7 +46,8 @@ public:
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -56,7 +57,6 @@ public:
|
|||||||
|
|
||||||
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,7 +24,8 @@
|
|||||||
#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:
|
||||||
@@ -49,11 +50,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void CancelEvent(){}
|
virtual void CancelEvent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CancelAtom cancel_;
|
CancelAtom cancel_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,16 +34,23 @@ CommandLineParser::~CommandLineParser()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -103,8 +110,7 @@ found_flag:
|
|||||||
|
|
||||||
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");
|
||||||
@@ -138,7 +144,8 @@ void CommandLineParser::PrintHelp(const char* filename)
|
|||||||
basename = filename;
|
basename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Usage: %s [options] %s\n\n", basename, positional_args.toUtf8().constData());
|
printf("Usage: %s [options] %s\n\n", basename,
|
||||||
|
positional_args.toUtf8().constData());
|
||||||
foreach (const KnownOption &o, options_) {
|
foreach (const KnownOption &o, options_) {
|
||||||
if (o.hidden) {
|
if (o.hidden) {
|
||||||
continue;
|
continue;
|
||||||
@@ -160,7 +167,8 @@ void CommandLineParser::PrintHelp(const char* filename)
|
|||||||
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());
|
||||||
|
|||||||
@@ -36,15 +36,13 @@
|
|||||||
* 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;
|
||||||
|
|
||||||
@@ -60,7 +58,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QString setting_;
|
QString setting_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Option : public PositionalArgument {
|
class Option : public PositionalArgument {
|
||||||
@@ -82,14 +79,18 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_set_;
|
bool is_set_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CommandLineParser() = default;
|
CommandLineParser() = default;
|
||||||
|
|
||||||
const Option* AddOption(const QStringList& strings, const QString& description, bool takes_arg = false, const QString& arg_placeholder = QString(), bool hidden = false);
|
const Option *AddOption(const QStringList &strings,
|
||||||
|
const QString &description, bool takes_arg = false,
|
||||||
|
const QString &arg_placeholder = QString(),
|
||||||
|
bool hidden = false);
|
||||||
|
|
||||||
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false);
|
const PositionalArgument *AddPositionalArgument(const QString &name,
|
||||||
|
const QString &description,
|
||||||
|
bool required = false);
|
||||||
|
|
||||||
void Process(const QVector<QString> &argv);
|
void Process(const QVector<QString> &argv);
|
||||||
|
|
||||||
@@ -115,7 +116,6 @@ private:
|
|||||||
QVector<KnownOption> options_;
|
QVector<KnownOption> options_;
|
||||||
|
|
||||||
QVector<KnownPositionalArgument> positional_args_;
|
QVector<KnownPositionalArgument> positional_args_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COMMANDLINEPARSER_H
|
#endif // COMMANDLINEPARSER_H
|
||||||
|
|||||||
@@ -40,12 +40,16 @@ 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;
|
||||||
|
|
||||||
@@ -54,7 +58,9 @@ bool InitializeCrashpad()
|
|||||||
|
|
||||||
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;
|
||||||
@@ -65,24 +71,29 @@ bool InitializeCrashpad()
|
|||||||
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)
|
||||||
|
return false;
|
||||||
settings->SetUploadsEnabled(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;
|
||||||
|
|||||||
@@ -20,9 +20,11 @@
|
|||||||
|
|
||||||
#include "debug.h"
|
#include "debug.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)
|
||||||
{
|
{
|
||||||
QByteArray localMsg = msg.toLocal8Bit();
|
QByteArray localMsg = msg.toLocal8Bit();
|
||||||
|
|
||||||
|
|||||||
+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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
|
|
||||||
//#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;
|
||||||
@@ -96,7 +96,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr double LOG100 = 4.60517018599;
|
static constexpr double LOG100 = 4.60517018599;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-4
@@ -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,9 +40,12 @@ 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
|
||||||
|
|||||||
+2
-1
@@ -23,7 +23,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
inline int64_t GetDigitCount(int64_t input)
|
inline int64_t GetDigitCount(int64_t input)
|
||||||
{
|
{
|
||||||
|
|||||||
+18
-11
@@ -20,9 +20,12 @@
|
|||||||
|
|
||||||
#include "common/ffmpegutils.h"
|
#include "common/ffmpegutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum)
|
AVPixelFormat
|
||||||
|
FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
|
||||||
|
PixelFormat maximum)
|
||||||
{
|
{
|
||||||
AVPixelFormat possible_pix_fmts[3];
|
AVPixelFormat possible_pix_fmts[3];
|
||||||
|
|
||||||
@@ -35,9 +38,7 @@ AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt
|
|||||||
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
|
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt, 1,
|
||||||
pix_fmt,
|
|
||||||
1,
|
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,11 +137,16 @@ int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
|
|||||||
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:
|
||||||
|
return AV_PIX_FMT_YUV444P;
|
||||||
|
case AV_PIX_FMT_YUVJ440P:
|
||||||
|
return AV_PIX_FMT_YUV440P;
|
||||||
|
case AV_PIX_FMT_YUVJ411P:
|
||||||
|
return AV_PIX_FMT_YUV411P;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -148,7 +154,8 @@ AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
|
|||||||
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) {
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ extern "C" {
|
|||||||
|
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
@@ -40,7 +41,9 @@ 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
|
||||||
@@ -50,7 +53,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @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
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
|
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
|
||||||
{
|
{
|
||||||
@@ -43,7 +44,8 @@ QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
|
|||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -55,7 +57,8 @@ QString FileFunctions::GetConfigurationLocation()
|
|||||||
if (IsPortable()) {
|
if (IsPortable()) {
|
||||||
return GetApplicationPath();
|
return GetApplicationPath();
|
||||||
} else {
|
} else {
|
||||||
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
QString s =
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
QDir(s).mkpath(".");
|
QDir(s).mkpath(".");
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -73,7 +76,9 @@ QString FileFunctions::GetApplicationPath()
|
|||||||
|
|
||||||
QString FileFunctions::GetTempFilePath()
|
QString FileFunctions::GetTempFilePath()
|
||||||
{
|
{
|
||||||
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
QString temp_path =
|
||||||
|
QDir(
|
||||||
|
QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
||||||
.filePath(QCoreApplication::organizationName()))
|
.filePath(QCoreApplication::organizationName()))
|
||||||
.filePath(QCoreApplication::applicationName());
|
.filePath(QCoreApplication::applicationName());
|
||||||
|
|
||||||
@@ -83,20 +88,23 @@ QString FileFunctions::GetTempFilePath()
|
|||||||
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(".") ||
|
||||||
|
info.fileName() == QStringLiteral("..")) {
|
||||||
continue;
|
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(),
|
||||||
|
dest_equivalent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (QFileInfo::exists(dest_equivalent)) {
|
} else if (QFileInfo::exists(dest_equivalent)) {
|
||||||
@@ -107,12 +115,14 @@ bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, co
|
|||||||
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()
|
||||||
|
<< "Failed to copy directory, source" << source << "didn't exist";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +137,8 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
|
|||||||
|
|
||||||
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(".") ||
|
||||||
|
info.fileName() == QStringLiteral("..")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,8 +151,10 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
|
|||||||
// 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 |
|
||||||
|
QFileDevice::WriteUser | QFileDevice::WriteGroup |
|
||||||
|
QFileDevice::WriteOther);
|
||||||
file.remove();
|
file.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,13 +163,15 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
@@ -201,16 +216,16 @@ QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
|
|||||||
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";
|
||||||
@@ -228,7 +243,9 @@ bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString
|
|||||||
|
|
||||||
QString FileFunctions::GetAutoRecoveryRoot()
|
QString FileFunctions::GetAutoRecoveryRoot()
|
||||||
{
|
{
|
||||||
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery"));
|
return QDir(QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::AppLocalDataLocation))
|
||||||
|
.filePath(QStringLiteral("autorecovery"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
#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
|
||||||
@@ -49,11 +50,14 @@ public:
|
|||||||
|
|
||||||
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
|
||||||
@@ -64,7 +68,8 @@ 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);
|
||||||
|
|
||||||
@@ -83,7 +88,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @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)
|
||||||
{
|
{
|
||||||
@@ -95,11 +101,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static QString GetAutoRecoveryRoot();
|
static QString GetAutoRecoveryRoot();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FILEFUNCTIONS_H
|
#endif // FILEFUNCTIONS_H
|
||||||
|
|||||||
+58
-32
@@ -5,12 +5,11 @@
|
|||||||
|
|
||||||
#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)
|
||||||
{
|
{
|
||||||
@@ -71,14 +70,14 @@ void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
|||||||
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()) });
|
fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) });
|
||||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||||
|
|
||||||
if (kBlockTags.contains(tag)) {
|
if (kBlockTags.contains(tag)) {
|
||||||
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes());
|
QTextBlockFormat block_fmt =
|
||||||
|
ReadBlockFormat(reader.attributes());
|
||||||
if (inside_block) {
|
if (inside_block) {
|
||||||
c.setBlockFormat(block_fmt);
|
c.setBlockFormat(block_fmt);
|
||||||
c.setBlockCharFormat(current_fmt);
|
c.setBlockCharFormat(current_fmt);
|
||||||
@@ -89,12 +88,10 @@ void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||||
|
|
||||||
QString characters = reader.text().toString();
|
QString characters = reader.text().toString();
|
||||||
c.insertText(characters, current_fmt);
|
c.insertText(characters, current_fmt);
|
||||||
|
|
||||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||||
|
|
||||||
QString tag = reader.name().toString().toLower();
|
QString tag = reader.name().toString().toLower();
|
||||||
|
|
||||||
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
|
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
|
||||||
@@ -108,7 +105,6 @@ void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,11 +122,14 @@ void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
|||||||
// 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"),
|
||||||
|
QStringLiteral("right"));
|
||||||
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center"));
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
|
QStringLiteral("center"));
|
||||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify"));
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
|
QStringLiteral("justify"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +142,8 @@ void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
|||||||
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());
|
||||||
@@ -163,7 +163,8 @@ void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
|||||||
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();
|
||||||
|
|
||||||
@@ -192,7 +193,8 @@ void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment
|
|||||||
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) {
|
||||||
@@ -210,23 +212,30 @@ 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;
|
||||||
@@ -254,7 +263,11 @@ void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
|
|||||||
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);
|
||||||
@@ -262,17 +275,22 @@ void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
|
|||||||
|
|
||||||
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"),
|
||||||
|
QStringLiteral("small-caps"));
|
||||||
// TODO: Add others
|
// 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())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,25 +308,29 @@ QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
|
|||||||
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"),
|
||||||
|
Qt::CaseInsensitive)) {
|
||||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||||
}
|
}
|
||||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||||
fmt.setFontWeight(first_val.toInt() / 8);
|
fmt.setFontWeight(first_val.toInt() / 8);
|
||||||
} else if (it.key() == QStringLiteral("font-style")) {
|
} else if (it.key() == QStringLiteral("font-style")) {
|
||||||
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic")));
|
fmt.setFontItalic(
|
||||||
|
StrEquals(first_val, QStringLiteral("italic")));
|
||||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||||
foreach (const QString &v, it.value()) {
|
foreach (const QString &v, it.value()) {
|
||||||
if (StrEquals(v, QStringLiteral("underline"))) {
|
if (StrEquals(v, QStringLiteral("underline"))) {
|
||||||
fmt.setFontUnderline(true);
|
fmt.setFontUnderline(true);
|
||||||
} else if (StrEquals(v, QStringLiteral("line-through"))) {
|
} else if (StrEquals(v,
|
||||||
|
QStringLiteral("line-through"))) {
|
||||||
fmt.setFontStrikeOut(true);
|
fmt.setFontStrikeOut(true);
|
||||||
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
||||||
fmt.setFontOverline(true);
|
fmt.setFontOverline(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (it.key() == QStringLiteral("color")) {
|
} else if (it.key() == QStringLiteral("color")) {
|
||||||
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) {
|
if (first_val.startsWith(QStringLiteral("rgba"),
|
||||||
|
Qt::CaseInsensitive)) {
|
||||||
QString vals_only = first_val;
|
QString vals_only = first_val;
|
||||||
vals_only.remove(QStringLiteral("rgba"));
|
vals_only.remove(QStringLiteral("rgba"));
|
||||||
vals_only.remove(QStringLiteral("("));
|
vals_only.remove(QStringLiteral("("));
|
||||||
@@ -331,7 +353,8 @@ QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
|
|||||||
}
|
}
|
||||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||||
if (first_val.contains(QChar('%'))) {
|
if (first_val.contains(QChar('%'))) {
|
||||||
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble());
|
fmt.setFontLetterSpacing(
|
||||||
|
first_val.chopped(1).toDouble());
|
||||||
}
|
}
|
||||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||||
if (first_val.contains(QChar('%'))) {
|
if (first_val.contains(QChar('%'))) {
|
||||||
@@ -370,7 +393,9 @@ QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,7 +450,8 @@ QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
|
|||||||
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('\'') ||
|
||||||
|
current_char == QChar('"'))) {
|
||||||
in_quote = current_char;
|
in_quote = current_char;
|
||||||
} else {
|
} else {
|
||||||
current_str.append(current_char);
|
current_str.append(current_char);
|
||||||
|
|||||||
+12
-7
@@ -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
|
||||||
@@ -33,26 +34,30 @@ public:
|
|||||||
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,
|
||||||
|
const QString &value)
|
||||||
{
|
{
|
||||||
WriteCSSProperty(style, key, QStringList({ 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,7 +2,8 @@
|
|||||||
|
|
||||||
#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;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class JobTime
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class JobTime {
|
||||||
public:
|
public:
|
||||||
JobTime();
|
JobTime();
|
||||||
|
|
||||||
@@ -50,7 +50,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t value_;
|
uint64_t value_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -28,12 +28,13 @@ template<typename 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-13
@@ -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,8 +47,7 @@ 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:
|
||||||
/**
|
/**
|
||||||
@@ -63,7 +63,8 @@ public:
|
|||||||
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,
|
||||||
|
&MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
|
||||||
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +200,6 @@ public:
|
|||||||
int64_t timestamp_;
|
int64_t timestamp_;
|
||||||
|
|
||||||
int64_t accessed_;
|
int64_t accessed_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using ElementPtr = std::shared_ptr<Element>;
|
using ElementPtr = std::shared_ptr<Element>;
|
||||||
@@ -245,7 +245,8 @@ public:
|
|||||||
// 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>(
|
||||||
|
this,
|
||||||
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
|
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
|
||||||
lent_elements_.push_back(e.get());
|
lent_elements_.push_back(e.get());
|
||||||
|
|
||||||
@@ -262,7 +263,8 @@ public:
|
|||||||
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_;
|
||||||
|
|
||||||
@@ -335,7 +337,6 @@ public:
|
|||||||
std::list<Element *> lent_elements_;
|
std::list<Element *> lent_elements_;
|
||||||
|
|
||||||
qint64 empty_time_;
|
qint64 empty_time_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -369,13 +370,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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:"
|
||||||
|
<< element_count_;
|
||||||
return nullptr;
|
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()
|
||||||
|
<< "Failed to create arena, allocation failed. Out of memory?";
|
||||||
delete a;
|
delete a;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -411,12 +414,14 @@ private slots:
|
|||||||
{
|
{
|
||||||
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) {
|
if (arena->GetUsageCount() == 0 &&
|
||||||
|
arena->GetTimeArenaWasMadeEmpty() <= min_time) {
|
||||||
qDebug() << "Removing an empty arena";
|
qDebug() << "Removing an empty arena";
|
||||||
delete arena;
|
delete arena;
|
||||||
it = arenas_.erase(it);
|
it = arenas_.erase(it);
|
||||||
@@ -425,7 +430,6 @@ private slots:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
#include "ocioutils.h"
|
#include "ocioutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
|
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ 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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,29 +22,25 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
|
void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
|
||||||
{
|
{
|
||||||
buf->set_pixels(OIIO::ROI(),
|
buf->set_pixels(OIIO::ROI(), buf->spec().format, frame->const_data(),
|
||||||
buf->spec().format,
|
OIIO::AutoStride, frame->linesize_bytes());
|
||||||
frame->const_data(),
|
|
||||||
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)
|
||||||
|
|||||||
@@ -27,11 +27,13 @@
|
|||||||
#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) {
|
switch (format) {
|
||||||
case PixelFormat::U8:
|
case PixelFormat::U8:
|
||||||
@@ -57,7 +59,6 @@ public:
|
|||||||
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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -25,7 +25,8 @@
|
|||||||
|
|
||||||
#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)
|
||||||
{
|
{
|
||||||
|
|||||||
+25
-14
@@ -22,9 +22,11 @@
|
|||||||
|
|
||||||
#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
|
||||||
@@ -47,7 +49,9 @@ QFrame *QtUtils::CreateVerticalLine()
|
|||||||
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);
|
||||||
@@ -85,7 +89,8 @@ 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;
|
||||||
|
|
||||||
@@ -95,16 +100,17 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
|
|||||||
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 &&
|
||||||
|
QFontMetricsWidth(fm, this_line) >= bounding_width) {
|
||||||
int old_size = this_line.size();
|
int old_size = this_line.size();
|
||||||
int hard_break = -1;
|
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++;
|
||||||
}
|
}
|
||||||
@@ -113,13 +119,16 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
|
|||||||
|
|
||||||
list.append(chopped);
|
list.append(chopped);
|
||||||
|
|
||||||
while (j < this_line.size() && this_line.at(j).isSpace()) {
|
while (j < this_line.size() &&
|
||||||
|
this_line.at(j).isSpace()) {
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
this_line.remove(0, j);
|
this_line.remove(0, j);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (hard_break == -1 && QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
|
} 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
|
// In case we can't find a better place to split, split at the earliest time the line
|
||||||
// goes under the width limit
|
// goes under the width limit
|
||||||
hard_break = j;
|
hard_break = j;
|
||||||
@@ -131,7 +140,8 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
|
|||||||
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()
|
||||||
|
<< "Failed to find anywhere to wrap. Returning full line.";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +155,8 @@ QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, in
|
|||||||
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;
|
||||||
@@ -195,7 +206,8 @@ QColor QtUtils::toQColor(const core::Color &i)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace core {
|
namespace core
|
||||||
|
{
|
||||||
|
|
||||||
uint qHash(const core::rational &r, uint seed)
|
uint qHash(const core::rational &r, uint seed)
|
||||||
{
|
{
|
||||||
@@ -207,7 +219,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-10
@@ -29,7 +29,8 @@
|
|||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
class QtUtils {
|
class QtUtils {
|
||||||
public:
|
public:
|
||||||
@@ -46,21 +47,24 @@ public:
|
|||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -87,15 +91,14 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @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);
|
||||||
|
|||||||
+1
-2
@@ -21,8 +21,7 @@
|
|||||||
#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);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-19
@@ -24,23 +24,23 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
double GetFloatRatioFromUser(QWidget* parent,
|
double GetFloatRatioFromUser(QWidget *parent, const QString &title, bool *ok_in)
|
||||||
const QString& title,
|
|
||||||
bool* ok_in)
|
|
||||||
{
|
{
|
||||||
QString s;
|
QString s;
|
||||||
|
|
||||||
forever {
|
forever
|
||||||
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
|
|
||||||
s = QInputDialog::getText(parent,
|
s = QInputDialog::getText(
|
||||||
title,
|
parent, title,
|
||||||
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
QCoreApplication::translate(
|
||||||
QLineEdit::Normal,
|
"RatioDialog",
|
||||||
s,
|
"Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||||
&ok);
|
QLineEdit::Normal, s, &ok);
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
// User cancelled dialog, do nothing
|
// User cancelled dialog, do nothing
|
||||||
@@ -50,7 +50,8 @@ double GetFloatRatioFromUser(QWidget* parent,
|
|||||||
return qSNaN();
|
return qSNaN();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ratio_components = s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
QStringList ratio_components =
|
||||||
|
s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
||||||
|
|
||||||
if (ratio_components.size() == 1) {
|
if (ratio_components.size() == 1) {
|
||||||
bool float_ok;
|
bool float_ok;
|
||||||
@@ -69,10 +70,7 @@ double GetFloatRatioFromUser(QWidget* parent,
|
|||||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||||
|
|
||||||
if (numer_ok
|
if (numer_ok && denom_ok && num > 0 && den > 0) {
|
||||||
&& denom_ok
|
|
||||||
&& num > 0
|
|
||||||
&& den > 0) {
|
|
||||||
// Exit loop and set this ratio
|
// Exit loop and set this ratio
|
||||||
if (ok_in) {
|
if (ok_in) {
|
||||||
*ok_in = true;
|
*ok_in = true;
|
||||||
@@ -81,10 +79,14 @@ double GetFloatRatioFromUser(QWidget* parent,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox::warning(parent,
|
QMessageBox::warning(
|
||||||
|
parent,
|
||||||
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
||||||
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a "
|
QCoreApplication::translate(
|
||||||
"rational fraction with a ':' or a '/' separator.").arg(s),
|
"RatioDialog",
|
||||||
|
"Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||||
|
"rational fraction with a ':' or a '/' separator.")
|
||||||
|
.arg(s),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,7 @@
|
|||||||
#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;
|
||||||
|
|
||||||
@@ -21,7 +19,6 @@ private:
|
|||||||
QMutex mutex_;
|
QMutex mutex_;
|
||||||
|
|
||||||
QMap<K, V> map_;
|
QMap<K, V> map_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // THREADSAFEMAP_H
|
#endif // THREADSAFEMAP_H
|
||||||
|
|||||||
+4
-2
@@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
#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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -21,8 +21,7 @@
|
|||||||
#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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,16 @@
|
|||||||
#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()) {
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+207
-99
@@ -37,7 +37,8 @@
|
|||||||
#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_;
|
||||||
|
|
||||||
@@ -46,14 +47,16 @@ 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()
|
||||||
@@ -64,106 +67,190 @@ Config &Config::Current()
|
|||||||
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,
|
||||||
|
Timecode::kTimecodeDropFrame);
|
||||||
|
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational,
|
||||||
|
QVariant::fromValue(rational(2)));
|
||||||
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1);
|
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt, 20);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000);
|
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt,
|
||||||
|
1);
|
||||||
|
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt,
|
||||||
|
20);
|
||||||
|
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt,
|
||||||
|
10000);
|
||||||
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
|
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
|
||||||
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"),
|
||||||
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false);
|
NodeValue::kBoolean, false);
|
||||||
|
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"),
|
||||||
|
NodeValue::kBoolean, true);
|
||||||
|
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"),
|
||||||
|
NodeValue::kBoolean, true);
|
||||||
|
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean,
|
||||||
|
false);
|
||||||
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
|
||||||
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false);
|
false);
|
||||||
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage);
|
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"),
|
||||||
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true);
|
NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"),
|
||||||
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true);
|
NodeValue::kBoolean, true);
|
||||||
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk);
|
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("Loop"), NodeValue::kBoolean, false);
|
||||||
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean,
|
||||||
|
true);
|
||||||
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
|
||||||
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true);
|
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean,
|
||||||
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false);
|
true);
|
||||||
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
|
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"),
|
||||||
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
|
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("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,
|
||||||
|
QString());
|
||||||
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, 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()
|
||||||
@@ -175,7 +262,8 @@ void Config::Load()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!config_file.open(QFile::ReadOnly)) {
|
if (!config_file.open(QFile::ReadOnly)) {
|
||||||
qWarning() << "Failed to load application settings. This session will use defaults.";
|
qWarning()
|
||||||
|
<< "Failed to load application settings. This session will use defaults.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,22 +284,26 @@ void Config::Load()
|
|||||||
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('.')) {
|
} 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
|
// 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
|
// supported rational
|
||||||
qDebug() << " CONFIG: Finding closest match to" << value;
|
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);
|
||||||
@@ -219,11 +311,13 @@ void Config::Load()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,10 +328,14 @@ void Config::Load()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (reader.hasError()) {
|
if (reader.hasError()) {
|
||||||
QMessageBox::critical(Core::instance()->main_window(),
|
QMessageBox::critical(
|
||||||
|
Core::instance()->main_window(),
|
||||||
QCoreApplication::translate("Config", "Error loading settings"),
|
QCoreApplication::translate("Config", "Error loading settings"),
|
||||||
QCoreApplication::translate("Config", "Failed to load application settings. This session will "
|
QCoreApplication::translate(
|
||||||
"use defaults.\n\n%1").arg(reader.errorString()),
|
"Config",
|
||||||
|
"Failed to load application settings. This session will "
|
||||||
|
"use defaults.\n\n%1")
|
||||||
|
.arg(reader.errorString()),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
current_config_.SetDefaults();
|
current_config_.SetDefaults();
|
||||||
}
|
}
|
||||||
@@ -248,14 +346,18 @@ void Config::Load()
|
|||||||
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(
|
||||||
|
Core::instance()->main_window(),
|
||||||
QCoreApplication::translate("Config", "Error saving settings"),
|
QCoreApplication::translate("Config", "Error saving settings"),
|
||||||
QCoreApplication::translate("Config", "Failed to save application settings. The application "
|
QCoreApplication::translate(
|
||||||
|
"Config",
|
||||||
|
"Failed to save application settings. The application "
|
||||||
"may lack write permissions for this location."),
|
"may lack write permissions for this location."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
return;
|
return;
|
||||||
@@ -269,16 +371,19 @@ void Config::Save()
|
|||||||
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()
|
||||||
|
<< "had null type and was discarded";
|
||||||
} else {
|
} else {
|
||||||
writer.writeTextElement(iterator.key(), value);
|
writer.writeTextElement(iterator.key(), value);
|
||||||
}
|
}
|
||||||
@@ -290,8 +395,11 @@ void Config::Save()
|
|||||||
|
|
||||||
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)) {
|
||||||
|
qWarning()
|
||||||
|
<< QStringLiteral(
|
||||||
|
"Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
|
||||||
.arg(real_filename, temp_filename);
|
.arg(real_filename, temp_filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -27,7 +27,8 @@
|
|||||||
|
|
||||||
#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]
|
||||||
@@ -56,7 +57,8 @@ private:
|
|||||||
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_;
|
||||||
|
|
||||||
|
|||||||
+231
-145
@@ -78,20 +78,21 @@
|
|||||||
#include "window/mainwindow/mainstatusbar.h"
|
#include "window/mainwindow/mainstatusbar.h"
|
||||||
#include "window/mainwindow/mainwindow.h"
|
#include "window/mainwindow/mainwindow.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
Core *Core::instance_ = nullptr;
|
Core *Core::instance_ = nullptr;
|
||||||
|
|
||||||
Core::Core(const CoreParams& params) :
|
Core::Core(const CoreParams ¶ms)
|
||||||
main_window_(nullptr),
|
: main_window_(nullptr)
|
||||||
open_project_(nullptr),
|
, open_project_(nullptr)
|
||||||
tool_(Tool::kPointer),
|
, tool_(Tool::kPointer)
|
||||||
addable_object_(Tool::kAddableEmpty),
|
, addable_object_(Tool::kAddableEmpty)
|
||||||
snapping_(true),
|
, snapping_(true)
|
||||||
core_params_(params),
|
, core_params_(params)
|
||||||
magic_(false),
|
, magic_(false)
|
||||||
pixel_sampling_users_(0),
|
, pixel_sampling_users_(0)
|
||||||
shown_cache_full_warning_(false)
|
, shown_cache_full_warning_(false)
|
||||||
{
|
{
|
||||||
// Store reference to this object, making the assumption that Core will only ever be made in
|
// Store reference to this object, making the assumption that Core will only ever be made in
|
||||||
// main(). This will obviously break if not.
|
// main(). This will obviously break if not.
|
||||||
@@ -169,7 +170,8 @@ void Core::Start()
|
|||||||
StartGUI(core_params_.fullscreen());
|
StartGUI(core_params_.fullscreen());
|
||||||
|
|
||||||
// If we have a startup
|
// If we have a startup
|
||||||
QMetaObject::invokeMethod(this, "OpenStartupProject", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, "OpenStartupProject",
|
||||||
|
Qt::QueuedConnection);
|
||||||
break;
|
break;
|
||||||
case CoreParams::kHeadlessExport:
|
case CoreParams::kHeadlessExport:
|
||||||
qInfo() << "Headless export is not fully implemented yet";
|
qInfo() << "Headless export is not fully implemented yet";
|
||||||
@@ -182,12 +184,11 @@ void Core::Start()
|
|||||||
// Manual crash triggering
|
// Manual crash triggering
|
||||||
if (core_params_.crash_on_startup()) {
|
if (core_params_.crash_on_startup()) {
|
||||||
const int interval = 5000;
|
const int interval = 5000;
|
||||||
qInfo() << "Manual crash was triggered. Application will crash in" << interval << "ms";
|
qInfo() << "Manual crash was triggered. Application will crash in"
|
||||||
|
<< interval << "ms";
|
||||||
QTimer *crash_timer = new QTimer(this);
|
QTimer *crash_timer = new QTimer(this);
|
||||||
crash_timer->setInterval(interval);
|
crash_timer->setInterval(interval);
|
||||||
connect(crash_timer, &QTimer::timeout, this, []{
|
connect(crash_timer, &QTimer::timeout, this, [] { abort(); });
|
||||||
abort();
|
|
||||||
});
|
|
||||||
crash_timer->start();
|
crash_timer->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +239,8 @@ UndoStack *Core::undo_stack()
|
|||||||
void Core::ImportFiles(const QStringList &urls, Folder *parent)
|
void Core::ImportFiles(const QStringList &urls, Folder *parent)
|
||||||
{
|
{
|
||||||
if (urls.isEmpty()) {
|
if (urls.isEmpty()) {
|
||||||
QMessageBox::critical(main_window_, tr("Import error"), tr("Nothing to import"));
|
QMessageBox::critical(main_window_, tr("Import error"),
|
||||||
|
tr("Nothing to import"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,9 +252,11 @@ void Core::ImportFiles(const QStringList &urls, Folder* parent)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskDialog* task_dialog = new TaskDialog(pim, tr("Importing..."), main_window());
|
TaskDialog *task_dialog =
|
||||||
|
new TaskDialog(pim, tr("Importing..."), main_window());
|
||||||
|
|
||||||
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::ImportTaskComplete);
|
connect(task_dialog, &TaskDialog::TaskSucceeded, this,
|
||||||
|
&Core::ImportTaskComplete);
|
||||||
|
|
||||||
task_dialog->open();
|
task_dialog->open();
|
||||||
}
|
}
|
||||||
@@ -333,19 +337,22 @@ void Core::DialogAboutShow()
|
|||||||
void Core::DialogImportShow()
|
void Core::DialogImportShow()
|
||||||
{
|
{
|
||||||
// Open dialog for user to select files
|
// Open dialog for user to select files
|
||||||
QStringList files = QFileDialog::getOpenFileNames(main_window_,
|
QStringList files =
|
||||||
tr("Import footage..."));
|
QFileDialog::getOpenFileNames(main_window_, tr("Import footage..."));
|
||||||
|
|
||||||
// Check if the user actually selected files to import
|
// Check if the user actually selected files to import
|
||||||
if (!files.isEmpty()) {
|
if (!files.isEmpty()) {
|
||||||
|
|
||||||
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
|
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
|
||||||
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
ProjectPanel *active_project_panel =
|
||||||
|
PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
||||||
Project *active_project;
|
Project *active_project;
|
||||||
|
|
||||||
if (active_project_panel == nullptr // Check that we found a Project panel
|
if (active_project_panel ==
|
||||||
|| (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project
|
nullptr // Check that we found a Project panel
|
||||||
QMessageBox::critical(main_window_, tr("Failed to import footage"), tr("Failed to find active Project panel"));
|
|| (active_project = active_project_panel->project()) ==
|
||||||
|
nullptr) { // and that we could find an active Project
|
||||||
|
QMessageBox::critical(main_window_, tr("Failed to import footage"),
|
||||||
|
tr("Failed to find active Project panel"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,8 +377,8 @@ void Core::DialogProjectPropertiesShow()
|
|||||||
ProjectPropertiesDialog ppd(proj, main_window_);
|
ProjectPropertiesDialog ppd(proj, main_window_);
|
||||||
ppd.exec();
|
ppd.exec();
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(main_window_,
|
QMessageBox::critical(
|
||||||
tr("No Active Project"),
|
main_window_, tr("No Active Project"),
|
||||||
tr("No project is currently open to set the properties for"),
|
tr("No project is currently open to set the properties for"),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
}
|
}
|
||||||
@@ -385,7 +392,8 @@ void Core::DialogExportShow()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_OTIO
|
#ifdef USE_OTIO
|
||||||
bool Core::DialogImportOTIOShow(const QList<Sequence*>& sequences) {
|
bool Core::DialogImportOTIOShow(const QList<Sequence *> &sequences)
|
||||||
|
{
|
||||||
Project *active_project = GetActiveProject();
|
Project *active_project = GetActiveProject();
|
||||||
OTIOPropertiesDialog opd(sequences, active_project);
|
OTIOPropertiesDialog opd(sequences, active_project);
|
||||||
return opd.exec() == QDialog::Accepted;
|
return opd.exec() == QDialog::Accepted;
|
||||||
@@ -395,12 +403,15 @@ bool Core::DialogImportOTIOShow(const QList<Sequence*>& sequences) {
|
|||||||
void Core::CreateNewFolder()
|
void Core::CreateNewFolder()
|
||||||
{
|
{
|
||||||
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
|
// Locate the most recently focused Project panel (assume that's the panel the user wants to import into)
|
||||||
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
ProjectPanel *active_project_panel =
|
||||||
|
PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
||||||
Project *active_project;
|
Project *active_project;
|
||||||
|
|
||||||
if (active_project_panel == nullptr // Check that we found a Project panel
|
if (active_project_panel == nullptr // Check that we found a Project panel
|
||||||
|| (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project
|
|| (active_project = active_project_panel->project()) ==
|
||||||
QMessageBox::critical(main_window_, tr("Failed to create new folder"), tr("Failed to find active project"));
|
nullptr) { // and that we could find an active Project
|
||||||
|
QMessageBox::critical(main_window_, tr("Failed to create new folder"),
|
||||||
|
tr("Failed to find active project"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +441,8 @@ void Core::CreateNewSequence()
|
|||||||
Project *active_project = GetActiveProject();
|
Project *active_project = GetActiveProject();
|
||||||
|
|
||||||
if (!active_project) {
|
if (!active_project) {
|
||||||
QMessageBox::critical(main_window_, tr("Failed to create new sequence"), tr("Failed to find active project"));
|
QMessageBox::critical(main_window_, tr("Failed to create new sequence"),
|
||||||
|
tr("Failed to find active project"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,25 +456,25 @@ void Core::CreateNewSequence()
|
|||||||
sd.SetUndoable(false);
|
sd.SetUndoable(false);
|
||||||
|
|
||||||
if (sd.exec() == QDialog::Accepted) {
|
if (sd.exec() == QDialog::Accepted) {
|
||||||
|
|
||||||
// Create an undoable command
|
// Create an undoable command
|
||||||
MultiUndoCommand *command = new MultiUndoCommand();
|
MultiUndoCommand *command = new MultiUndoCommand();
|
||||||
|
|
||||||
command->add_child(new NodeAddCommand(active_project, new_sequence));
|
command->add_child(new NodeAddCommand(active_project, new_sequence));
|
||||||
command->add_child(new FolderAddChild(GetSelectedFolderInActiveProject(), new_sequence));
|
command->add_child(new FolderAddChild(
|
||||||
command->add_child(new NodeSetPositionCommand(new_sequence, new_sequence, Node::Position()));
|
GetSelectedFolderInActiveProject(), new_sequence));
|
||||||
|
command->add_child(new NodeSetPositionCommand(
|
||||||
|
new_sequence, new_sequence, Node::Position()));
|
||||||
command->add_child(new OpenSequenceCommand(new_sequence));
|
command->add_child(new OpenSequenceCommand(new_sequence));
|
||||||
|
|
||||||
// Create and connect default nodes to new sequence
|
// Create and connect default nodes to new sequence
|
||||||
new_sequence->add_default_nodes(command);
|
new_sequence->add_default_nodes(command);
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command, tr("Created New Sequence"));
|
Core::instance()->undo_stack()->push(command,
|
||||||
|
tr("Created New Sequence"));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// If the dialog was accepted, ownership goes to the AddItemCommand. But if we get here, just delete
|
// If the dialog was accepted, ownership goes to the AddItemCommand. But if we get here, just delete
|
||||||
delete new_sequence;
|
delete new_sequence;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +521,8 @@ bool Core::AddOpenProjectFromTask(Task *task, bool add_to_recents)
|
|||||||
void Core::SetActiveProject(Project *p)
|
void Core::SetActiveProject(Project *p)
|
||||||
{
|
{
|
||||||
if (open_project_) {
|
if (open_project_) {
|
||||||
disconnect(open_project_, &Project::ModifiedChanged, this, &Core::ProjectWasModified);
|
disconnect(open_project_, &Project::ModifiedChanged, this,
|
||||||
|
&Core::ProjectWasModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
open_project_ = p;
|
open_project_ = p;
|
||||||
@@ -517,7 +530,8 @@ void Core::SetActiveProject(Project *p)
|
|||||||
main_window_->SetProject(p);
|
main_window_->SetProject(p);
|
||||||
|
|
||||||
if (open_project_) {
|
if (open_project_) {
|
||||||
connect(open_project_, &Project::ModifiedChanged, this, &Core::ProjectWasModified);
|
connect(open_project_, &Project::ModifiedChanged, this,
|
||||||
|
&Core::ProjectWasModified);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,7 +548,8 @@ void Core::ImportTaskComplete(Task* task)
|
|||||||
|
|
||||||
for (int i = 0; i < f->GetVideoStreamCount(); i++) {
|
for (int i = 0; i < f->GetVideoStreamCount(); i++) {
|
||||||
const VideoParams &vs = f->GetVideoParams(i);
|
const VideoParams &vs = f->GetVideoParams(i);
|
||||||
if (!(vs.video_type() == VideoParams::kVideoTypeStill && vs.enabled() == (i == 0))) {
|
if (!(vs.video_type() == VideoParams::kVideoTypeStill &&
|
||||||
|
vs.enabled() == (i == 0))) {
|
||||||
all_stills = false;
|
all_stills = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,11 +559,15 @@ void Core::ImportTaskComplete(Task* task)
|
|||||||
|
|
||||||
d.setIcon(QMessageBox::Question);
|
d.setIcon(QMessageBox::Question);
|
||||||
d.setWindowTitle(tr("Multi-Layer Image"));
|
d.setWindowTitle(tr("Multi-Layer Image"));
|
||||||
d.setText(tr("The file '%1' has multiple layers. Would you like these layers to be "
|
d.setText(
|
||||||
"separated across multiple tracks or merged into a single image?").arg(f->filename()));
|
tr("The file '%1' has multiple layers. Would you like these layers to be "
|
||||||
|
"separated across multiple tracks or merged into a single image?")
|
||||||
|
.arg(f->filename()));
|
||||||
|
|
||||||
auto multi_btn = d.addButton(tr("Multiple Layers"), QMessageBox::YesRole);
|
auto multi_btn =
|
||||||
auto single_btn = d.addButton(tr("Single Layer"), QMessageBox::NoRole);
|
d.addButton(tr("Multiple Layers"), QMessageBox::YesRole);
|
||||||
|
auto single_btn =
|
||||||
|
d.addButton(tr("Single Layer"), QMessageBox::NoRole);
|
||||||
auto cancel_btn = d.addButton(QMessageBox::Cancel);
|
auto cancel_btn = d.addButton(QMessageBox::Cancel);
|
||||||
|
|
||||||
d.exec();
|
d.exec();
|
||||||
@@ -571,11 +590,14 @@ void Core::ImportTaskComplete(Task* task)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (import_task->HasInvalidFiles()) {
|
if (import_task->HasInvalidFiles()) {
|
||||||
ProjectImportErrorDialog d(import_task->GetInvalidFiles(), main_window_);
|
ProjectImportErrorDialog d(import_task->GetInvalidFiles(),
|
||||||
|
main_window_);
|
||||||
d.exec();
|
d.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
undo_stack_.push(command, tr("Imported %1 File(s)").arg(import_task->GetImportedFootage().size()));
|
undo_stack_.push(
|
||||||
|
command,
|
||||||
|
tr("Imported %1 File(s)").arg(import_task->GetImportedFootage().size()));
|
||||||
|
|
||||||
main_window_->SelectFootage(import_task->GetImportedFootage());
|
main_window_->SelectFootage(import_task->GetImportedFootage());
|
||||||
}
|
}
|
||||||
@@ -587,7 +609,8 @@ bool Core::ConfirmImageSequence(const QString& filename)
|
|||||||
mb.setIcon(QMessageBox::Question);
|
mb.setIcon(QMessageBox::Question);
|
||||||
mb.setWindowTitle(tr("Possible image sequence detected"));
|
mb.setWindowTitle(tr("Possible image sequence detected"));
|
||||||
mb.setText(tr("The file '%1' looks like it might be part of an image "
|
mb.setText(tr("The file '%1' looks like it might be part of an image "
|
||||||
"sequence. Would you like to import it as such?").arg(filename));
|
"sequence. Would you like to import it as such?")
|
||||||
|
.arg(filename));
|
||||||
|
|
||||||
mb.addButton(QMessageBox::Yes);
|
mb.addButton(QMessageBox::Yes);
|
||||||
mb.addButton(QMessageBox::No);
|
mb.addButton(QMessageBox::No);
|
||||||
@@ -605,7 +628,8 @@ bool Core::StartHeadlessExport()
|
|||||||
const QString &startup_project = core_params_.startup_project();
|
const QString &startup_project = core_params_.startup_project();
|
||||||
|
|
||||||
if (startup_project.isEmpty()) {
|
if (startup_project.isEmpty()) {
|
||||||
qCritical().noquote() << tr("You must specify a project file to export");
|
qCritical().noquote()
|
||||||
|
<< tr("You must specify a project file to export");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,14 +713,15 @@ bool Core::StartHeadlessExport()
|
|||||||
void Core::OpenStartupProject()
|
void Core::OpenStartupProject()
|
||||||
{
|
{
|
||||||
const QString &startup_project = core_params_.startup_project();
|
const QString &startup_project = core_params_.startup_project();
|
||||||
bool startup_project_exists = !startup_project.isEmpty() && QFileInfo::exists(startup_project);
|
bool startup_project_exists = !startup_project.isEmpty() &&
|
||||||
|
QFileInfo::exists(startup_project);
|
||||||
|
|
||||||
// Load startup project
|
// Load startup project
|
||||||
if (!startup_project_exists && !startup_project.isEmpty()) {
|
if (!startup_project_exists && !startup_project.isEmpty()) {
|
||||||
QMessageBox::warning(main_window_,
|
QMessageBox::warning(main_window_, tr("Failed to open startup file"),
|
||||||
tr("Failed to open startup file"),
|
|
||||||
tr("The project \"%1\" doesn't exist. "
|
tr("The project \"%1\" doesn't exist. "
|
||||||
"A new project will be started instead.").arg(startup_project),
|
"A new project will be started instead.")
|
||||||
|
.arg(startup_project),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +737,8 @@ void Core::OpenStartupProject()
|
|||||||
void Core::AddRecoveryProjectFromTask(Task *task)
|
void Core::AddRecoveryProjectFromTask(Task *task)
|
||||||
{
|
{
|
||||||
if (AddOpenProjectFromTask(task, false)) {
|
if (AddOpenProjectFromTask(task, false)) {
|
||||||
ProjectLoadBaseTask* load_task = static_cast<ProjectLoadBaseTask*>(task);
|
ProjectLoadBaseTask *load_task =
|
||||||
|
static_cast<ProjectLoadBaseTask *>(task);
|
||||||
|
|
||||||
Project *project = load_task->GetLoadedProject();
|
Project *project = load_task->GetLoadedProject();
|
||||||
|
|
||||||
@@ -747,9 +773,7 @@ void Core::StartGUI(bool full_screen)
|
|||||||
DiskManager::CreateInstance();
|
DiskManager::CreateInstance();
|
||||||
|
|
||||||
// Connect the PanelFocusManager to the application's focus change signal
|
// Connect the PanelFocusManager to the application's focus change signal
|
||||||
connect(qApp,
|
connect(qApp, &QApplication::focusChanged, PanelManager::instance(),
|
||||||
&QApplication::focusChanged,
|
|
||||||
PanelManager::instance(),
|
|
||||||
&PanelManager::FocusChanged);
|
&PanelManager::FocusChanged);
|
||||||
|
|
||||||
// Set KDDockWidgets flags
|
// Set KDDockWidgets flags
|
||||||
@@ -774,12 +798,14 @@ void Core::StartGUI(bool full_screen)
|
|||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
// Workaround for Qt bug where menus don't appear in full screen mode
|
// Workaround for Qt bug where menus don't appear in full screen mode
|
||||||
// See: https://doc.qt.io/qt-5/windows-issues.html
|
// See: https://doc.qt.io/qt-5/windows-issues.html
|
||||||
QWindowsWindowFunctions::setHasBorderInFullScreen(main_window_->windowHandle(), true);
|
QWindowsWindowFunctions::setHasBorderInFullScreen(
|
||||||
|
main_window_->windowHandle(), true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Start autorecovery timer using the config value as its interval
|
// Start autorecovery timer using the config value as its interval
|
||||||
SetAutorecoveryInterval(OLIVE_CONFIG("AutorecoveryInterval").toInt());
|
SetAutorecoveryInterval(OLIVE_CONFIG("AutorecoveryInterval").toInt());
|
||||||
connect(&autorecovery_timer_, &QTimer::timeout, this, &Core::SaveAutorecovery);
|
connect(&autorecovery_timer_, &QTimer::timeout, this,
|
||||||
|
&Core::SaveAutorecovery);
|
||||||
autorecovery_timer_.start();
|
autorecovery_timer_.start();
|
||||||
|
|
||||||
// Load recently opened projects list
|
// Load recently opened projects list
|
||||||
@@ -802,24 +828,28 @@ void Core::SaveProjectInternal(const QString& override_filename)
|
|||||||
// Create save manager
|
// Create save manager
|
||||||
Task *psm;
|
Task *psm;
|
||||||
|
|
||||||
if (open_project_->filename().endsWith(QStringLiteral(".otio"), Qt::CaseInsensitive)) {
|
if (open_project_->filename().endsWith(QStringLiteral(".otio"),
|
||||||
|
Qt::CaseInsensitive)) {
|
||||||
#ifdef USE_OTIO
|
#ifdef USE_OTIO
|
||||||
psm = new SaveOTIOTask(open_project_);
|
psm = new SaveOTIOTask(open_project_);
|
||||||
#else
|
#else
|
||||||
QMessageBox::critical(main_window_,
|
QMessageBox::critical(
|
||||||
tr("Missing OpenTimelineIO Libraries"),
|
main_window_, tr("Missing OpenTimelineIO Libraries"),
|
||||||
tr("This build was compiled without OpenTimelineIO and therefore "
|
tr("This build was compiled without OpenTimelineIO and therefore "
|
||||||
"cannot open OpenTimelineIO files."));
|
"cannot open OpenTimelineIO files."));
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
bool use_compression = !open_project_->filename().endsWith(QStringLiteral(".ovexml"), Qt::CaseInsensitive);
|
bool use_compression = !open_project_->filename().endsWith(
|
||||||
|
QStringLiteral(".ovexml"), Qt::CaseInsensitive);
|
||||||
psm = new ProjectSaveTask(open_project_, use_compression);
|
psm = new ProjectSaveTask(open_project_, use_compression);
|
||||||
static_cast<ProjectSaveTask*>(psm)->SetLayout(main_window_->SaveLayout());
|
static_cast<ProjectSaveTask *>(psm)->SetLayout(
|
||||||
|
main_window_->SaveLayout());
|
||||||
|
|
||||||
if (!override_filename.isEmpty()) {
|
if (!override_filename.isEmpty()) {
|
||||||
// Set override filename if provided
|
// Set override filename if provided
|
||||||
static_cast<ProjectSaveTask*>(psm)->SetOverrideFilename(override_filename);
|
static_cast<ProjectSaveTask *>(psm)->SetOverrideFilename(
|
||||||
|
override_filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,26 +874,28 @@ void Core::SaveProjectInternal(const QString& override_filename)
|
|||||||
ViewerOutput *Core::GetSequenceToExport()
|
ViewerOutput *Core::GetSequenceToExport()
|
||||||
{
|
{
|
||||||
// First try the most recently focused time based window
|
// First try the most recently focused time based window
|
||||||
TimeBasedPanel* time_panel = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
|
TimeBasedPanel *time_panel =
|
||||||
|
PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
|
||||||
|
|
||||||
// If that fails try defaulting to the first timeline (i.e. if a project has just been loaded).
|
// If that fails try defaulting to the first timeline (i.e. if a project has just been loaded).
|
||||||
if (!time_panel->GetConnectedViewer()) {
|
if (!time_panel->GetConnectedViewer()) {
|
||||||
// Safe to assume there will always be one timeline.
|
// Safe to assume there will always be one timeline.
|
||||||
time_panel = PanelManager::instance()->GetPanelsOfType<TimelinePanel>().first();
|
time_panel =
|
||||||
|
PanelManager::instance()->GetPanelsOfType<TimelinePanel>().first();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time_panel && time_panel->GetConnectedViewer()) {
|
if (time_panel && time_panel->GetConnectedViewer()) {
|
||||||
if (time_panel->GetConnectedViewer()->GetLength() == 0) {
|
if (time_panel->GetConnectedViewer()->GetLength() == 0) {
|
||||||
QMessageBox::critical(main_window_,
|
QMessageBox::critical(
|
||||||
tr("Error"),
|
main_window_, tr("Error"),
|
||||||
tr("This Sequence is empty. There is nothing to export."),
|
tr("This Sequence is empty. There is nothing to export."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
} else {
|
} else {
|
||||||
return time_panel->GetConnectedViewer();
|
return time_panel->GetConnectedViewer();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(main_window_,
|
QMessageBox::critical(
|
||||||
tr("Error"),
|
main_window_, tr("Error"),
|
||||||
tr("No valid sequence detected.\n\nMake sure a sequence is loaded and it has a connected Viewer node."),
|
tr("No valid sequence detected.\n\nMake sure a sequence is loaded and it has a connected Viewer node."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
}
|
}
|
||||||
@@ -873,7 +905,9 @@ ViewerOutput *Core::GetSequenceToExport()
|
|||||||
|
|
||||||
QString Core::GetAutoRecoveryIndexFilename()
|
QString Core::GetAutoRecoveryIndexFilename()
|
||||||
{
|
{
|
||||||
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("unrecovered"));
|
return QDir(QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::AppLocalDataLocation))
|
||||||
|
.filePath(QStringLiteral("unrecovered"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::SaveUnrecoveredList()
|
void Core::SaveUnrecoveredList()
|
||||||
@@ -908,20 +942,27 @@ void Core::SaveUnrecoveredList()
|
|||||||
bool Core::RevertProjectInternal(bool by_opening_existing)
|
bool Core::RevertProjectInternal(bool by_opening_existing)
|
||||||
{
|
{
|
||||||
if (open_project_->filename().isEmpty()) {
|
if (open_project_->filename().isEmpty()) {
|
||||||
QMessageBox::critical(main_window_, tr("Revert"),
|
QMessageBox::critical(
|
||||||
|
main_window_, tr("Revert"),
|
||||||
tr("This project has not yet been saved, therefore there is no last saved state to revert to."));
|
tr("This project has not yet been saved, therefore there is no last saved state to revert to."));
|
||||||
} else {
|
} else {
|
||||||
QString msg;
|
QString msg;
|
||||||
|
|
||||||
if (by_opening_existing) {
|
if (by_opening_existing) {
|
||||||
msg = tr("The project \"%1\" is already open. By re-opening it, the project will revert to "
|
msg =
|
||||||
"its last saved state. Any unsaved changes will be lost. Do you wish to continue?").arg(open_project_->filename());
|
tr("The project \"%1\" is already open. By re-opening it, the project will revert to "
|
||||||
|
"its last saved state. Any unsaved changes will be lost. Do you wish to continue?")
|
||||||
|
.arg(open_project_->filename());
|
||||||
} else {
|
} else {
|
||||||
msg = tr("This will revert the project \"%1\" back to its last saved state. "
|
msg =
|
||||||
"All unsaved changes will be lost. Do you wish to continue?").arg(open_project_->name());
|
tr("This will revert the project \"%1\" back to its last saved state. "
|
||||||
|
"All unsaved changes will be lost. Do you wish to continue?")
|
||||||
|
.arg(open_project_->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QMessageBox::question(main_window_, tr("Revert"), msg, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
|
if (QMessageBox::question(main_window_, tr("Revert"), msg,
|
||||||
|
QMessageBox::Ok | QMessageBox::Cancel) ==
|
||||||
|
QMessageBox::Ok) {
|
||||||
// Copy filename because CloseProject is going to delete `p`
|
// Copy filename because CloseProject is going to delete `p`
|
||||||
QString filename = open_project_->filename();
|
QString filename = open_project_->filename();
|
||||||
|
|
||||||
@@ -954,16 +995,22 @@ void Core::SaveAutorecovery()
|
|||||||
{
|
{
|
||||||
if (OLIVE_CONFIG("AutorecoveryEnabled").toBool()) {
|
if (OLIVE_CONFIG("AutorecoveryEnabled").toBool()) {
|
||||||
if (open_project_ && !open_project_->has_autorecovery_been_saved()) {
|
if (open_project_ && !open_project_->has_autorecovery_been_saved()) {
|
||||||
QDir project_autorecovery_dir(QDir(FileFunctions::GetAutoRecoveryRoot()).filePath(open_project_->GetUuid().toString()));
|
QDir project_autorecovery_dir(
|
||||||
|
QDir(FileFunctions::GetAutoRecoveryRoot())
|
||||||
|
.filePath(open_project_->GetUuid().toString()));
|
||||||
if (FileFunctions::DirectoryIsValid(project_autorecovery_dir)) {
|
if (FileFunctions::DirectoryIsValid(project_autorecovery_dir)) {
|
||||||
QString this_autorecovery_path = project_autorecovery_dir.filePath(QStringLiteral("%1.ove").arg(QString::number(QDateTime::currentSecsSinceEpoch())));
|
QString this_autorecovery_path =
|
||||||
|
project_autorecovery_dir.filePath(
|
||||||
|
QStringLiteral("%1.ove").arg(QString::number(
|
||||||
|
QDateTime::currentSecsSinceEpoch())));
|
||||||
|
|
||||||
SaveProjectInternal(this_autorecovery_path);
|
SaveProjectInternal(this_autorecovery_path);
|
||||||
|
|
||||||
open_project_->set_autorecovery_saved(true);
|
open_project_->set_autorecovery_saved(true);
|
||||||
|
|
||||||
// Keep track of projects that where the "newest" save is the recovery project
|
// Keep track of projects that where the "newest" save is the recovery project
|
||||||
if (!autorecovered_projects_.contains(open_project_->GetUuid())) {
|
if (!autorecovered_projects_.contains(
|
||||||
|
open_project_->GetUuid())) {
|
||||||
autorecovered_projects_.append(open_project_->GetUuid());
|
autorecovered_projects_.append(open_project_->GetUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -971,27 +1018,34 @@ void Core::SaveAutorecovery()
|
|||||||
|
|
||||||
// Write human-readable real name so it's not just a UUID
|
// Write human-readable real name so it's not just a UUID
|
||||||
{
|
{
|
||||||
QFile realname_file(project_autorecovery_dir.filePath(QStringLiteral("realname.txt")));
|
QFile realname_file(project_autorecovery_dir.filePath(
|
||||||
|
QStringLiteral("realname.txt")));
|
||||||
realname_file.open(QFile::WriteOnly);
|
realname_file.open(QFile::WriteOnly);
|
||||||
realname_file.write(open_project_->pretty_filename().toUtf8());
|
realname_file.write(
|
||||||
|
open_project_->pretty_filename().toUtf8());
|
||||||
realname_file.close();
|
realname_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t max_recoveries_per_file = OLIVE_CONFIG("AutorecoveryMaximum").toLongLong();
|
int64_t max_recoveries_per_file =
|
||||||
|
OLIVE_CONFIG("AutorecoveryMaximum").toLongLong();
|
||||||
|
|
||||||
// Since we write an extra file, increment total allowed files by 1
|
// Since we write an extra file, increment total allowed files by 1
|
||||||
max_recoveries_per_file++;
|
max_recoveries_per_file++;
|
||||||
|
|
||||||
// Delete old entries
|
// Delete old entries
|
||||||
QStringList recovery_files = project_autorecovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
|
QStringList recovery_files = project_autorecovery_dir.entryList(
|
||||||
|
QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
|
||||||
while (recovery_files.size() > max_recoveries_per_file) {
|
while (recovery_files.size() > max_recoveries_per_file) {
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
for (int i = 0; i < recovery_files.size(); i++) {
|
for (int i = 0; i < recovery_files.size(); i++) {
|
||||||
const QString &f = recovery_files.at(i);
|
const QString &f = recovery_files.at(i);
|
||||||
|
|
||||||
if (f.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) {
|
if (f.endsWith(QStringLiteral(".ove"),
|
||||||
QString delete_full_path = project_autorecovery_dir.filePath(f);
|
Qt::CaseInsensitive)) {
|
||||||
qDebug() << "Deleted old recovery:" << delete_full_path;
|
QString delete_full_path =
|
||||||
|
project_autorecovery_dir.filePath(f);
|
||||||
|
qDebug()
|
||||||
|
<< "Deleted old recovery:" << delete_full_path;
|
||||||
QFile::remove(delete_full_path);
|
QFile::remove(delete_full_path);
|
||||||
recovery_files.removeAt(i);
|
recovery_files.removeAt(i);
|
||||||
deleted = true;
|
deleted = true;
|
||||||
@@ -1006,7 +1060,8 @@ void Core::SaveAutorecovery()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(main_window_, tr("Auto-Recovery Error"),
|
QMessageBox::critical(
|
||||||
|
main_window_, tr("Auto-Recovery Error"),
|
||||||
tr("Failed to save auto-recovery to \"%1\". "
|
tr("Failed to save auto-recovery to \"%1\". "
|
||||||
"Olive may not have permission to this directory.")
|
"Olive may not have permission to this directory.")
|
||||||
.arg(project_autorecovery_dir.absolutePath()));
|
.arg(project_autorecovery_dir.absolutePath()));
|
||||||
@@ -1039,7 +1094,8 @@ Project* Core::GetActiveProject() const
|
|||||||
|
|
||||||
Folder *Core::GetSelectedFolderInActiveProject() const
|
Folder *Core::GetSelectedFolderInActiveProject() const
|
||||||
{
|
{
|
||||||
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
ProjectPanel *active_project_panel =
|
||||||
|
PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
|
||||||
|
|
||||||
if (active_project_panel) {
|
if (active_project_panel) {
|
||||||
return active_project_panel->GetSelectedFolder();
|
return active_project_panel->GetSelectedFolder();
|
||||||
@@ -1050,7 +1106,8 @@ Folder *Core::GetSelectedFolderInActiveProject() const
|
|||||||
|
|
||||||
Timecode::Display Core::GetTimecodeDisplay() const
|
Timecode::Display Core::GetTimecodeDisplay() const
|
||||||
{
|
{
|
||||||
return static_cast<Timecode::Display>(OLIVE_CONFIG("TimecodeDisplay").toInt());
|
return static_cast<Timecode::Display>(
|
||||||
|
OLIVE_CONFIG("TimecodeDisplay").toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::SetTimecodeDisplay(Timecode::Display d)
|
void Core::SetTimecodeDisplay(Timecode::Display d)
|
||||||
@@ -1099,7 +1156,8 @@ QString Core::GetProjectFilter(bool include_any_filter)
|
|||||||
for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) {
|
for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) {
|
||||||
combined.append(QStringLiteral("*.%1").arg(it->second));
|
combined.append(QStringLiteral("*.%1").arg(it->second));
|
||||||
}
|
}
|
||||||
filters.append(QStringLiteral("%1 (%2)").arg(tr("All Supported Projects"), combined.join(' ')));
|
filters.append(QStringLiteral("%1 (%2)").arg(
|
||||||
|
tr("All Supported Projects"), combined.join(' ')));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) {
|
for (auto it = FILTERS.cbegin(); it != FILTERS.cend(); it++) {
|
||||||
@@ -1111,17 +1169,20 @@ QString Core::GetProjectFilter(bool include_any_filter)
|
|||||||
|
|
||||||
QString Core::GetRecentProjectsFilePath()
|
QString Core::GetRecentProjectsFilePath()
|
||||||
{
|
{
|
||||||
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("recent"));
|
return QDir(FileFunctions::GetConfigurationLocation())
|
||||||
|
.filePath(QStringLiteral("recent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::SetStartupLocale()
|
void Core::SetStartupLocale()
|
||||||
{
|
{
|
||||||
// Set language
|
// Set language
|
||||||
if (!core_params_.startup_language().isEmpty()) {
|
if (!core_params_.startup_language().isEmpty()) {
|
||||||
if (translator_->load(core_params_.startup_language()) && QApplication::installTranslator(translator_)) {
|
if (translator_->load(core_params_.startup_language()) &&
|
||||||
|
QApplication::installTranslator(translator_)) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Failed to load translation file. Falling back to defaults.";
|
qWarning()
|
||||||
|
<< "Failed to load translation file. Falling back to defaults.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1133,7 +1194,8 @@ void Core::SetStartupLocale()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!SetLanguage(use_locale)) {
|
if (!SetLanguage(use_locale)) {
|
||||||
qWarning() << "Trying to use locale" << use_locale << "but couldn't find a translation for it";
|
qWarning() << "Trying to use locale" << use_locale
|
||||||
|
<< "but couldn't find a translation for it";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1168,12 +1230,15 @@ void Core::OpenNodeInViewer(ViewerOutput *viewer)
|
|||||||
main_window_->OpenNodeInViewer(viewer);
|
main_window_->OpenNodeInViewer(viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image)
|
void Core::OpenExportDialogForViewer(ViewerOutput *viewer,
|
||||||
|
bool start_still_image)
|
||||||
{
|
{
|
||||||
ExportDialog* ed = new ExportDialog(viewer, start_still_image, main_window_);
|
ExportDialog *ed =
|
||||||
|
new ExportDialog(viewer, start_still_image, main_window_);
|
||||||
connect(ed, &ExportDialog::finished, ed, &ExportDialog::deleteLater);
|
connect(ed, &ExportDialog::finished, ed, &ExportDialog::deleteLater);
|
||||||
ed->open();
|
ed->open();
|
||||||
connect(ed, &ExportDialog::RequestImportFile, this, &Core::ImportSingleFile);
|
connect(ed, &ExportDialog::RequestImportFile, this,
|
||||||
|
&Core::ImportSingleFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::CheckForAutoRecoveries()
|
void Core::CheckForAutoRecoveries()
|
||||||
@@ -1182,9 +1247,11 @@ void Core::CheckForAutoRecoveries()
|
|||||||
if (autorecovery_index.exists()) {
|
if (autorecovery_index.exists()) {
|
||||||
// Uh-oh, we have auto-recoveries to prompt
|
// Uh-oh, we have auto-recoveries to prompt
|
||||||
if (autorecovery_index.open(QFile::ReadOnly)) {
|
if (autorecovery_index.open(QFile::ReadOnly)) {
|
||||||
QStringList recovery_filenames = QString::fromUtf8(autorecovery_index.readAll()).split('\n');
|
QStringList recovery_filenames =
|
||||||
|
QString::fromUtf8(autorecovery_index.readAll()).split('\n');
|
||||||
|
|
||||||
AutoRecoveryDialog ard(tr("The following projects had unsaved changes when Olive "
|
AutoRecoveryDialog ard(
|
||||||
|
tr("The following projects had unsaved changes when Olive "
|
||||||
"forcefully quit. Would you like to load them?"),
|
"forcefully quit. Would you like to load them?"),
|
||||||
recovery_filenames, true, main_window_);
|
recovery_filenames, true, main_window_);
|
||||||
ard.exec();
|
ard.exec();
|
||||||
@@ -1194,10 +1261,12 @@ void Core::CheckForAutoRecoveries()
|
|||||||
// Delete recovery index since we don't need it anymore
|
// Delete recovery index since we don't need it anymore
|
||||||
QFile::remove(GetAutoRecoveryIndexFilename());
|
QFile::remove(GetAutoRecoveryIndexFilename());
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(main_window_, tr("Auto-Recovery Error"),
|
QMessageBox::critical(
|
||||||
|
main_window_, tr("Auto-Recovery Error"),
|
||||||
tr("Found auto-recoveries but failed to load the auto-recovery index. "
|
tr("Found auto-recoveries but failed to load the auto-recovery index. "
|
||||||
"Auto-recover projects will have to be opened manually.\n\n"
|
"Auto-recover projects will have to be opened manually.\n\n"
|
||||||
"Your recoverable projects are still available at: %1").arg(FileFunctions::GetAutoRecoveryRoot()));
|
"Your recoverable projects are still available at: %1")
|
||||||
|
.arg(FileFunctions::GetAutoRecoveryRoot()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1205,8 +1274,10 @@ void Core::CheckForAutoRecoveries()
|
|||||||
void Core::BrowseAutoRecoveries()
|
void Core::BrowseAutoRecoveries()
|
||||||
{
|
{
|
||||||
// List all auto-recovery entries
|
// List all auto-recovery entries
|
||||||
AutoRecoveryDialog ard(tr("The following project versions have been auto-saved:"),
|
AutoRecoveryDialog ard(
|
||||||
QDir(FileFunctions::GetAutoRecoveryRoot()).entryList(QDir::Dirs | QDir::NoDotAndDotDot),
|
tr("The following project versions have been auto-saved:"),
|
||||||
|
QDir(FileFunctions::GetAutoRecoveryRoot())
|
||||||
|
.entryList(QDir::Dirs | QDir::NoDotAndDotDot),
|
||||||
false, main_window_);
|
false, main_window_);
|
||||||
ard.exec();
|
ard.exec();
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1306,8 @@ void Core::WarnCacheFull()
|
|||||||
if (!shown_cache_full_warning_ && main_window_) {
|
if (!shown_cache_full_warning_ && main_window_) {
|
||||||
shown_cache_full_warning_ = true;
|
shown_cache_full_warning_ = true;
|
||||||
|
|
||||||
QMessageBox::warning(main_window_, tr("Disk Cache Full"),
|
QMessageBox::warning(
|
||||||
|
main_window_, tr("Disk Cache Full"),
|
||||||
tr("The disk cache is currently full and Olive is having to delete old "
|
tr("The disk cache is currently full and Olive is having to delete old "
|
||||||
"frames to keep it within the limits set in the Disk preferences. This "
|
"frames to keep it within the limits set in the Disk preferences. This "
|
||||||
"will result in SIGNIFICANTLY reduced cache performance.\n\n"
|
"will result in SIGNIFICANTLY reduced cache performance.\n\n"
|
||||||
@@ -1259,7 +1331,8 @@ bool Core::SaveProjectAs()
|
|||||||
// Somewhat hacky method of extracting the extension from the name filter
|
// Somewhat hacky method of extracting the extension from the name filter
|
||||||
const QString &name_filter = fd.selectedNameFilter();
|
const QString &name_filter = fd.selectedNameFilter();
|
||||||
int ext_index = name_filter.indexOf(QStringLiteral("(*.")) + 3;
|
int ext_index = name_filter.indexOf(QStringLiteral("(*.")) + 3;
|
||||||
QString extension = name_filter.mid(ext_index, name_filter.size() - ext_index - 1);
|
QString extension =
|
||||||
|
name_filter.mid(ext_index, name_filter.size() - ext_index - 1);
|
||||||
|
|
||||||
fn = FileFunctions::EnsureFilenameExtension(fn, extension);
|
fn = FileFunctions::EnsureFilenameExtension(fn, extension);
|
||||||
|
|
||||||
@@ -1328,8 +1401,8 @@ void Core::OpenProjectInternal(const QString &filename, bool recovery_project)
|
|||||||
#ifdef USE_OTIO
|
#ifdef USE_OTIO
|
||||||
load_task = new LoadOTIOTask(filename);
|
load_task = new LoadOTIOTask(filename);
|
||||||
#else
|
#else
|
||||||
QMessageBox::critical(main_window_,
|
QMessageBox::critical(
|
||||||
tr("Missing OpenTimelineIO Libraries"),
|
main_window_, tr("Missing OpenTimelineIO Libraries"),
|
||||||
tr("This build was compiled without OpenTimelineIO and therefore "
|
tr("This build was compiled without OpenTimelineIO and therefore "
|
||||||
"cannot open OpenTimelineIO files."));
|
"cannot open OpenTimelineIO files."));
|
||||||
return;
|
return;
|
||||||
@@ -1339,12 +1412,15 @@ void Core::OpenProjectInternal(const QString &filename, bool recovery_project)
|
|||||||
load_task = new ProjectLoadTask(filename);
|
load_task = new ProjectLoadTask(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskDialog* task_dialog = new TaskDialog(load_task, tr("Load Project"), main_window());
|
TaskDialog *task_dialog =
|
||||||
|
new TaskDialog(load_task, tr("Load Project"), main_window());
|
||||||
|
|
||||||
if (recovery_project) {
|
if (recovery_project) {
|
||||||
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddRecoveryProjectFromTask);
|
connect(task_dialog, &TaskDialog::TaskSucceeded, this,
|
||||||
|
&Core::AddRecoveryProjectFromTask);
|
||||||
} else {
|
} else {
|
||||||
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddOpenProjectFromTaskAndAddToRecents);
|
connect(task_dialog, &TaskDialog::TaskSucceeded, this,
|
||||||
|
&Core::AddOpenProjectFromTaskAndAddToRecents);
|
||||||
}
|
}
|
||||||
|
|
||||||
task_dialog->open();
|
task_dialog->open();
|
||||||
@@ -1366,7 +1442,8 @@ int Core::CountFilesInFileList(const QFileInfoList &filenames)
|
|||||||
if (f.fileName() == "." || f.fileName() == "..") {
|
if (f.fileName() == "." || f.fileName() == "..") {
|
||||||
continue;
|
continue;
|
||||||
} else if (f.isDir()) {
|
} else if (f.isDir()) {
|
||||||
QFileInfoList info_list = QDir(f.absoluteFilePath()).entryInfoList();
|
QFileInfoList info_list =
|
||||||
|
QDir(f.absoluteFilePath()).entryInfoList();
|
||||||
|
|
||||||
file_count += CountFilesInFileList(info_list);
|
file_count += CountFilesInFileList(info_list);
|
||||||
} else {
|
} else {
|
||||||
@@ -1395,12 +1472,9 @@ bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString s = QInputDialog::getText(main_window_,
|
QString s = QInputDialog::getText(main_window_, tr("Label Node"),
|
||||||
tr("Label Node"),
|
tr("Set node label"), QLineEdit::Normal,
|
||||||
tr("Set node label"),
|
start_label, &ok);
|
||||||
QLineEdit::Normal,
|
|
||||||
start_label,
|
|
||||||
&ok);
|
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
NodeRenameCommand *rename_command = new NodeRenameCommand();
|
NodeRenameCommand *rename_command = new NodeRenameCommand();
|
||||||
@@ -1412,7 +1486,8 @@ bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
|
|||||||
if (parent) {
|
if (parent) {
|
||||||
parent->add_child(rename_command);
|
parent->add_child(rename_command);
|
||||||
} else {
|
} else {
|
||||||
undo_stack_.push(rename_command, tr("Renamed %1 Node(s)").arg(nodes.size()));
|
undo_stack_.push(rename_command,
|
||||||
|
tr("Renamed %1 Node(s)").arg(nodes.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1421,7 +1496,8 @@ bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequence *Core::CreateNewSequenceForProject(const QString &format, Project* project)
|
Sequence *Core::CreateNewSequenceForProject(const QString &format,
|
||||||
|
Project *project)
|
||||||
{
|
{
|
||||||
Sequence *new_sequence = new Sequence();
|
Sequence *new_sequence = new Sequence();
|
||||||
|
|
||||||
@@ -1443,9 +1519,11 @@ void Core::OpenProjectFromRecentList(int index)
|
|||||||
|
|
||||||
if (QFileInfo::exists(open_fn)) {
|
if (QFileInfo::exists(open_fn)) {
|
||||||
OpenProjectInternal(open_fn);
|
OpenProjectInternal(open_fn);
|
||||||
} else if (QMessageBox::information(main_window(),
|
} else if (
|
||||||
tr("Cannot open recent project"),
|
QMessageBox::information(
|
||||||
tr("The project \"%1\" doesn't exist. Would you like to remove this file from the recent list?").arg(open_fn),
|
main_window(), tr("Cannot open recent project"),
|
||||||
|
tr("The project \"%1\" doesn't exist. Would you like to remove this file from the recent list?")
|
||||||
|
.arg(open_fn),
|
||||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||||
recent_projects_.removeAt(index);
|
recent_projects_.removeAt(index);
|
||||||
|
|
||||||
@@ -1464,10 +1542,12 @@ bool Core::CloseProject(bool auto_open_new, bool ignore_modified)
|
|||||||
mb.setWindowModality(Qt::WindowModal);
|
mb.setWindowModality(Qt::WindowModal);
|
||||||
mb.setIcon(QMessageBox::Question);
|
mb.setIcon(QMessageBox::Question);
|
||||||
mb.setWindowTitle(tr("Unsaved Changes"));
|
mb.setWindowTitle(tr("Unsaved Changes"));
|
||||||
mb.setText(tr("The project '%1' has unsaved changes. Would you like to save them?")
|
mb.setText(
|
||||||
|
tr("The project '%1' has unsaved changes. Would you like to save them?")
|
||||||
.arg(open_project_->name()));
|
.arg(open_project_->name()));
|
||||||
|
|
||||||
QPushButton* yes_btn = mb.addButton(tr("Save"), QMessageBox::YesRole);
|
QPushButton *yes_btn =
|
||||||
|
mb.addButton(tr("Save"), QMessageBox::YesRole);
|
||||||
|
|
||||||
mb.addButton(tr("Don't Save"), QMessageBox::NoRole);
|
mb.addButton(tr("Don't Save"), QMessageBox::NoRole);
|
||||||
|
|
||||||
@@ -1504,13 +1584,15 @@ bool Core::CloseProject(bool auto_open_new, bool ignore_modified)
|
|||||||
|
|
||||||
void Core::CacheActiveSequence(bool in_out_only)
|
void Core::CacheActiveSequence(bool in_out_only)
|
||||||
{
|
{
|
||||||
TimeBasedPanel* p = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
|
TimeBasedPanel *p =
|
||||||
|
PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
|
||||||
|
|
||||||
if (p && p->GetConnectedViewer()) {
|
if (p && p->GetConnectedViewer()) {
|
||||||
// Hacky but works for now
|
// Hacky but works for now
|
||||||
|
|
||||||
// Find Viewer attached to this TimeBasedPanel
|
// Find Viewer attached to this TimeBasedPanel
|
||||||
QList<ViewerPanel*> all_viewers = PanelManager::instance()->GetPanelsOfType<ViewerPanel>();
|
QList<ViewerPanel *> all_viewers =
|
||||||
|
PanelManager::instance()->GetPanelsOfType<ViewerPanel>();
|
||||||
|
|
||||||
ViewerPanel *found_panel = nullptr;
|
ViewerPanel *found_panel = nullptr;
|
||||||
|
|
||||||
@@ -1528,8 +1610,8 @@ void Core::CacheActiveSequence(bool in_out_only)
|
|||||||
found_panel->CacheEntireSequence();
|
found_panel->CacheEntireSequence();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(main_window_,
|
QMessageBox::critical(
|
||||||
tr("Failed to cache sequence"),
|
main_window_, tr("Failed to cache sequence"),
|
||||||
tr("No active viewer found with this sequence."),
|
tr("No active viewer found with this sequence."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
}
|
}
|
||||||
@@ -1558,7 +1640,8 @@ QString StripWindowsDriveLetter(QString s)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::ValidateFootageInLoadedProject(Project* project, const QString& project_saved_url)
|
bool Core::ValidateFootageInLoadedProject(Project *project,
|
||||||
|
const QString &project_saved_url)
|
||||||
{
|
{
|
||||||
QVector<Footage *> footage_we_couldnt_validate;
|
QVector<Footage *> footage_we_couldnt_validate;
|
||||||
|
|
||||||
@@ -1567,7 +1650,8 @@ bool Core::ValidateFootageInLoadedProject(Project* project, const QString& proje
|
|||||||
QString footage_fn = StripWindowsDriveLetter(footage->filename());
|
QString footage_fn = StripWindowsDriveLetter(footage->filename());
|
||||||
QString project_fn = StripWindowsDriveLetter(project_saved_url);
|
QString project_fn = StripWindowsDriveLetter(project_saved_url);
|
||||||
|
|
||||||
if (!QFileInfo::exists(footage_fn) && !project_saved_url.isEmpty()) {
|
if (!QFileInfo::exists(footage_fn) &&
|
||||||
|
!project_saved_url.isEmpty()) {
|
||||||
// If the footage doesn't exist, it might have moved with the project
|
// If the footage doesn't exist, it might have moved with the project
|
||||||
const QString &project_current_url = project->filename();
|
const QString &project_current_url = project->filename();
|
||||||
|
|
||||||
@@ -1576,12 +1660,15 @@ bool Core::ValidateFootageInLoadedProject(Project* project, const QString& proje
|
|||||||
QDir saved_dir(QFileInfo(project_fn).dir());
|
QDir saved_dir(QFileInfo(project_fn).dir());
|
||||||
QDir true_dir(QFileInfo(project_current_url).dir());
|
QDir true_dir(QFileInfo(project_current_url).dir());
|
||||||
|
|
||||||
QString relative_filename = saved_dir.relativeFilePath(footage_fn);
|
QString relative_filename =
|
||||||
QString transformed_abs_filename = true_dir.filePath(relative_filename);
|
saved_dir.relativeFilePath(footage_fn);
|
||||||
|
QString transformed_abs_filename =
|
||||||
|
true_dir.filePath(relative_filename);
|
||||||
|
|
||||||
if (QFileInfo::exists(transformed_abs_filename)) {
|
if (QFileInfo::exists(transformed_abs_filename)) {
|
||||||
// Use this file instead
|
// Use this file instead
|
||||||
qInfo() << "Resolved" << footage_fn << "relatively to" << transformed_abs_filename;
|
qInfo() << "Resolved" << footage_fn << "relatively to"
|
||||||
|
<< transformed_abs_filename;
|
||||||
footage->set_filename(transformed_abs_filename);
|
footage->set_filename(transformed_abs_filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1611,7 +1698,8 @@ bool Core::SetLanguage(const QString &locale)
|
|||||||
QApplication::removeTranslator(translator_);
|
QApplication::removeTranslator(translator_);
|
||||||
|
|
||||||
QString resource_path = QStringLiteral(":/ts/%1").arg(locale);
|
QString resource_path = QStringLiteral(":/ts/%1").arg(locale);
|
||||||
if (translator_->load(resource_path) && QApplication::installTranslator(translator_)) {
|
if (translator_->load(resource_path) &&
|
||||||
|
QApplication::installTranslator(translator_)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1620,20 +1708,18 @@ bool Core::SetLanguage(const QString &locale)
|
|||||||
|
|
||||||
void Core::OpenProject()
|
void Core::OpenProject()
|
||||||
{
|
{
|
||||||
QString file = QFileDialog::getOpenFileName(main_window_,
|
QString file = QFileDialog::getOpenFileName(
|
||||||
tr("Open Project"),
|
main_window_, tr("Open Project"), QString(), GetProjectFilter(true));
|
||||||
QString(),
|
|
||||||
GetProjectFilter(true));
|
|
||||||
|
|
||||||
if (!file.isEmpty()) {
|
if (!file.isEmpty()) {
|
||||||
OpenProjectInternal(file);
|
OpenProjectInternal(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::CoreParams::CoreParams() :
|
Core::CoreParams::CoreParams()
|
||||||
mode_(kRunNormal),
|
: mode_(kRunNormal)
|
||||||
run_fullscreen_(false),
|
, run_fullscreen_(false)
|
||||||
crash_(false)
|
, crash_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-18
@@ -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,20 +49,14 @@ 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
|
||||||
{
|
{
|
||||||
@@ -123,7 +118,6 @@ public:
|
|||||||
bool run_fullscreen_;
|
bool run_fullscreen_;
|
||||||
|
|
||||||
bool crash_;
|
bool crash_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,12 +242,14 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @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,
|
||||||
|
Project *project);
|
||||||
static Sequence *CreateNewSequenceForProject(Project *project)
|
static Sequence *CreateNewSequenceForProject(Project *project)
|
||||||
{
|
{
|
||||||
return CreateNewSequenceForProject(tr("Sequence %1"), project);
|
return CreateNewSequenceForProject(tr("Sequence %1"), project);
|
||||||
@@ -277,7 +273,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @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
|
||||||
@@ -297,9 +294,13 @@ public:
|
|||||||
|
|
||||||
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:
|
||||||
/**
|
/**
|
||||||
@@ -610,10 +611,10 @@ private slots:
|
|||||||
/**
|
/**
|
||||||
* @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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@
|
|||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
|
CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
|
||||||
{
|
{
|
||||||
@@ -52,7 +53,8 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
|
|||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by "
|
layout->addWidget(new QLabel(
|
||||||
|
tr("We're sorry, Olive has crashed. Please help us fix it by "
|
||||||
"sending an error report.")));
|
"sending an error report.")));
|
||||||
|
|
||||||
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
||||||
@@ -60,7 +62,8 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
|
|||||||
layout->addWidget(splitter);
|
layout->addWidget(splitter);
|
||||||
|
|
||||||
summary_edit_ = new QTextEdit();
|
summary_edit_ = new QTextEdit();
|
||||||
summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as "
|
summary_edit_->setPlaceholderText(
|
||||||
|
tr("Describe what you were doing in as much detail as "
|
||||||
"possible. If you can, provide steps to reproduce this crash."));
|
"possible. If you can, provide steps to reproduce this crash."));
|
||||||
|
|
||||||
splitter->addWidget(summary_edit_);
|
splitter->addWidget(summary_edit_);
|
||||||
@@ -83,11 +86,13 @@ CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
|
|||||||
btn_layout->addStretch();
|
btn_layout->addStretch();
|
||||||
|
|
||||||
send_report_btn_ = new QPushButton(tr("Send Error Report"));
|
send_report_btn_ = new QPushButton(tr("Send Error Report"));
|
||||||
connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport);
|
connect(send_report_btn_, &QPushButton::clicked, this,
|
||||||
|
&CrashHandlerDialog::SendErrorReport);
|
||||||
btn_layout->addWidget(send_report_btn_);
|
btn_layout->addWidget(send_report_btn_);
|
||||||
|
|
||||||
dont_send_btn_ = new QPushButton(tr("Don't Send"));
|
dont_send_btn_ = new QPushButton(tr("Don't Send"));
|
||||||
connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject);
|
connect(dont_send_btn_, &QPushButton::clicked, this,
|
||||||
|
&CrashHandlerDialog::reject);
|
||||||
btn_layout->addWidget(dont_send_btn_);
|
btn_layout->addWidget(dont_send_btn_);
|
||||||
|
|
||||||
layout->addLayout(btn_layout);
|
layout->addLayout(btn_layout);
|
||||||
@@ -117,7 +122,8 @@ QString CrashHandlerDialog::GetSymbolPath()
|
|||||||
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"));
|
||||||
@@ -132,13 +138,18 @@ void CrashHandlerDialog::GenerateReport()
|
|||||||
|
|
||||||
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 =
|
||||||
|
QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
|
||||||
p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
|
p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
|
||||||
crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
|
crash_report_->setText(
|
||||||
|
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
|
void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
|
||||||
@@ -153,7 +164,9 @@ void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
|
|||||||
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(
|
||||||
|
tr("Failed to send error report (%1). Please try again later.")
|
||||||
|
.arg(QString::number(reply->error())));
|
||||||
b.addButton(QMessageBox::Ok);
|
b.addButton(QMessageBox::Ok);
|
||||||
b.exec();
|
b.exec();
|
||||||
|
|
||||||
@@ -161,7 +174,8 @@ void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -172,7 +186,8 @@ void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply, const QList<QSslE
|
|||||||
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")
|
||||||
|
.arg(errors.join('\n')));
|
||||||
b.addButton(QMessageBox::Ok);
|
b.addButton(QMessageBox::Ok);
|
||||||
b.exec();
|
b.exec();
|
||||||
}
|
}
|
||||||
@@ -190,7 +205,8 @@ void CrashHandlerDialog::AttemptToFindReport()
|
|||||||
|
|
||||||
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()
|
||||||
@@ -206,48 +222,62 @@ void CrashHandlerDialog::SendErrorReport()
|
|||||||
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(
|
||||||
|
tr("You must write a description to submit this crash report."));
|
||||||
b.addButton(QMessageBox::Ok);
|
b.addButton(QMessageBox::Ok);
|
||||||
b.exec();
|
b.exec();
|
||||||
return;
|
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.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||||
|
QStringLiteral("form-data; name=\"description\""));
|
||||||
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
|
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
|
||||||
multipart->append(desc_part);
|
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.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||||
|
QStringLiteral("form-data; name=\"report\""));
|
||||||
report_part.setBody(report_data_);
|
report_part.setBody(report_data_);
|
||||||
multipart->append(report_part);
|
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.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||||
|
QStringLiteral("form-data; name=\"commit\""));
|
||||||
commit_part.setBody(kAppVersionLong.toUtf8());
|
commit_part.setBody(kAppVersionLong.toUtf8());
|
||||||
multipart->append(commit_part);
|
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"));
|
||||||
|
dump_part.setHeader(
|
||||||
|
QNetworkRequest::ContentDispositionHeader,
|
||||||
|
QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
|
||||||
.arg(QFileInfo(report_filename_).fileName()));
|
.arg(QFileInfo(report_filename_).fileName()));
|
||||||
QFile *dump_file = new QFile(report_filename_);
|
QFile *dump_file = new QFile(report_filename_);
|
||||||
dump_file->open(QFile::ReadOnly);
|
dump_file->open(QFile::ReadOnly);
|
||||||
@@ -268,7 +298,8 @@ void CrashHandlerDialog::SendErrorReport()
|
|||||||
#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()));
|
||||||
@@ -294,8 +325,11 @@ void CrashHandlerDialog::SendErrorReport()
|
|||||||
#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"));
|
||||||
|
sym_part.setHeader(
|
||||||
|
QNetworkRequest::ContentDispositionHeader,
|
||||||
|
QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
|
||||||
.arg(symbol_filename));
|
.arg(symbol_filename));
|
||||||
QFile sym_file(symbol_full_path);
|
QFile sym_file(symbol_full_path);
|
||||||
|
|
||||||
@@ -329,7 +363,8 @@ void CrashHandlerDialog::closeEvent(QCloseEvent* e)
|
|||||||
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(
|
||||||
|
tr("Crash report is still uploading. Closing now may result in no "
|
||||||
"report being sent. Are you sure you wish to close?"));
|
"report being sent. Are you sure you wish to close?"));
|
||||||
b.addButton(QMessageBox::Ok);
|
b.addButton(QMessageBox::Ok);
|
||||||
b.addButton(QMessageBox::Cancel);
|
b.addButton(QMessageBox::Cancel);
|
||||||
|
|||||||
@@ -30,10 +30,10 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CrashHandlerDialog : public QDialog
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CrashHandlerDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CrashHandlerDialog(const QString &report_path);
|
CrashHandlerDialog(const QString &report_path);
|
||||||
@@ -74,7 +74,6 @@ private slots:
|
|||||||
void ReadProcessFinished();
|
void ReadProcessFinished();
|
||||||
|
|
||||||
void SendErrorReport();
|
void SendErrorReport();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-17
@@ -30,13 +30,15 @@
|
|||||||
#include "patreon.h"
|
#include "patreon.h"
|
||||||
#include "scrollinglabel.h"
|
#include "scrollinglabel.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
|
||||||
QDialog(parent)
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
if (welcome_dialog) {
|
if (welcome_dialog) {
|
||||||
setWindowTitle(tr("Welcome to %1").arg(QApplication::applicationName()));
|
setWindowTitle(
|
||||||
|
tr("Welcome to %1").arg(QApplication::applicationName()));
|
||||||
} else {
|
} else {
|
||||||
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
|
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
|
||||||
}
|
}
|
||||||
@@ -44,25 +46,29 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
|||||||
QFontMetrics fm = fontMetrics();
|
QFontMetrics fm = fontMetrics();
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
|
layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||||
|
fm.height());
|
||||||
|
|
||||||
QHBoxLayout *horiz_layout = new QHBoxLayout();
|
QHBoxLayout *horiz_layout = new QHBoxLayout();
|
||||||
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
|
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||||
|
fm.height());
|
||||||
horiz_layout->setSpacing(fm.height() * 2);
|
horiz_layout->setSpacing(fm.height() * 2);
|
||||||
|
|
||||||
QLabel* icon = new QLabel(QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
|
QLabel *icon = new QLabel(
|
||||||
|
QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
|
||||||
icon->setAlignment(Qt::AlignCenter);
|
icon->setAlignment(Qt::AlignCenter);
|
||||||
horiz_layout->addWidget(icon);
|
horiz_layout->addWidget(icon);
|
||||||
|
|
||||||
// Construct About text
|
// Construct About text
|
||||||
QLabel* label =
|
QLabel *label = new QLabel(
|
||||||
new QLabel(QStringLiteral("<html><head/><body>"
|
QStringLiteral("<html><head/><body>"
|
||||||
"<p><b>%1</b> %2</p>" // AppName (version identifier)
|
"<p><b>%1</b> %2</p>" // AppName (version identifier)
|
||||||
"<p><a href=\"https://www.olivevideoeditor.org/\">"
|
"<p><a href=\"https://www.olivevideoeditor.org/\">"
|
||||||
"https://www.olivevideoeditor.org/"
|
"https://www.olivevideoeditor.org/"
|
||||||
"</a></p>"
|
"</a></p>"
|
||||||
"<p>%3</p>" // First statement
|
"<p>%3</p>" // First statement
|
||||||
"</body></html>").arg(QApplication::applicationName(),
|
"</body></html>")
|
||||||
|
.arg(QApplication::applicationName(),
|
||||||
QApplication::applicationVersion(),
|
QApplication::applicationVersion(),
|
||||||
tr("Olive is a free open source non-linear video editor. "
|
tr("Olive is a free open source non-linear video editor. "
|
||||||
"This software is licensed under the GNU GPL Version 3.")));
|
"This software is licensed under the GNU GPL Version 3.")));
|
||||||
@@ -72,7 +78,8 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
|||||||
label->setWordWrap(true);
|
label->setWordWrap(true);
|
||||||
label->setOpenExternalLinks(true);
|
label->setOpenExternalLinks(true);
|
||||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
label->setTextInteractionFlags(Qt::TextSelectableByMouse |
|
||||||
|
Qt::LinksAccessibleByMouse);
|
||||||
label->setCursor(Qt::IBeamCursor);
|
label->setCursor(Qt::IBeamCursor);
|
||||||
horiz_layout->addWidget(label);
|
horiz_layout->addWidget(label);
|
||||||
|
|
||||||
@@ -84,16 +91,20 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
|||||||
QString opening_statement;
|
QString opening_statement;
|
||||||
|
|
||||||
if (welcome_dialog || patrons.isEmpty()) {
|
if (welcome_dialog || patrons.isEmpty()) {
|
||||||
opening_statement = tr("<b>Olive relies on support from the community to continue its development.</b>");
|
opening_statement = tr(
|
||||||
|
"<b>Olive relies on support from the community to continue its development.</b>");
|
||||||
} else {
|
} else {
|
||||||
opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people.");
|
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 "
|
QLabel *support_lbl = new QLabel(
|
||||||
|
tr("<html>%1 "
|
||||||
"If you like this project, please consider making a "
|
"If you like this project, please consider making a "
|
||||||
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
|
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
|
||||||
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
|
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
|
||||||
"support its development.</html>").arg(opening_statement));
|
"support its development.</html>")
|
||||||
|
.arg(opening_statement));
|
||||||
support_lbl->setWordWrap(true);
|
support_lbl->setWordWrap(true);
|
||||||
support_lbl->setAlignment(Qt::AlignCenter);
|
support_lbl->setAlignment(Qt::AlignCenter);
|
||||||
support_lbl->setOpenExternalLinks(true);
|
support_lbl->setOpenExternalLinks(true);
|
||||||
@@ -112,13 +123,15 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
|||||||
btn_layout->setSpacing(0);
|
btn_layout->setSpacing(0);
|
||||||
|
|
||||||
if (welcome_dialog) {
|
if (welcome_dialog) {
|
||||||
dont_show_again_checkbox_ = new QCheckBox(tr("Don't show this message again"));
|
dont_show_again_checkbox_ =
|
||||||
|
new QCheckBox(tr("Don't show this message again"));
|
||||||
btn_layout->addWidget(dont_show_again_checkbox_);
|
btn_layout->addWidget(dont_show_again_checkbox_);
|
||||||
} else {
|
} else {
|
||||||
dont_show_again_checkbox_ = nullptr;
|
dont_show_again_checkbox_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
QDialogButtonBox *buttons =
|
||||||
|
new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
||||||
if (!welcome_dialog) {
|
if (!welcome_dialog) {
|
||||||
buttons->setCenterButtons(true);
|
buttons->setCenterButtons(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,8 +35,7 @@ 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:
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +54,6 @@ public slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QCheckBox *dont_show_again_checkbox_;
|
QCheckBox *dont_show_again_checkbox_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,20 +24,21 @@
|
|||||||
|
|
||||||
#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);
|
||||||
}
|
}
|
||||||
@@ -88,7 +89,8 @@ void ScrollingLabel::paintEvent(QPaintEvent *e)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +98,8 @@ void ScrollingLabel::paintEvent(QPaintEvent *e)
|
|||||||
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];
|
||||||
|
|||||||
@@ -24,10 +24,10 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ScrollingLabel : public QWidget
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ScrollingLabel : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ScrollingLabel(QWidget *parent = nullptr);
|
ScrollingLabel(QWidget *parent = nullptr);
|
||||||
@@ -49,7 +49,8 @@ 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;
|
||||||
|
|
||||||
@@ -63,7 +64,6 @@ private:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void AnimationUpdate();
|
void AnimationUpdate();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,12 @@
|
|||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
ActionSearch::ActionSearch(QWidget *parent) :
|
ActionSearch::ActionSearch(QWidget *parent)
|
||||||
QDialog(parent),
|
: QDialog(parent)
|
||||||
menu_bar_(nullptr)
|
, menu_bar_(nullptr)
|
||||||
{
|
{
|
||||||
// ActionSearch requires a parent widget
|
// ActionSearch requires a parent widget
|
||||||
Q_ASSERT(parent != nullptr);
|
Q_ASSERT(parent != nullptr);
|
||||||
@@ -58,13 +59,16 @@ ActionSearch::ActionSearch(QWidget *parent) :
|
|||||||
entry_field->setPlaceholderText(tr("Search for action..."));
|
entry_field->setPlaceholderText(tr("Search for action..."));
|
||||||
|
|
||||||
// Connect signals/slots
|
// Connect signals/slots
|
||||||
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
|
connect(entry_field, SIGNAL(textChanged(const QString &)), this,
|
||||||
|
SLOT(search_update(const QString &)));
|
||||||
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_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.
|
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
|
||||||
// We override it here to select the upper or lower item in the list.
|
// 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(moveSelectionUp()), this,
|
||||||
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
|
SLOT(move_selection_up()));
|
||||||
|
connect(entry_field, SIGNAL(moveSelectionDown()), this,
|
||||||
|
SLOT(move_selection_down()));
|
||||||
layout->addWidget(entry_field);
|
layout->addWidget(entry_field);
|
||||||
|
|
||||||
// Construct list of actions
|
// Construct list of actions
|
||||||
@@ -89,7 +93,8 @@ 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) {
|
||||||
@@ -102,7 +107,6 @@ void ActionSearch::search_update(const QString &s, const QString &p, QMenu *pare
|
|||||||
// 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
|
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
|
||||||
// (and their submenus).
|
// (and their submenus).
|
||||||
|
|
||||||
@@ -125,31 +129,28 @@ void ActionSearch::search_update(const QString &s, const QString &p, QMenu *pare
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
|
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
|
||||||
|
|
||||||
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
|
// 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`).
|
// adding the current menu's text to the existing hierarchy (passed in `p`).
|
||||||
QString menu_text;
|
QString menu_text;
|
||||||
if (!p.isEmpty()) menu_text += p + " > ";
|
if (!p.isEmpty())
|
||||||
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names
|
menu_text += p + " > ";
|
||||||
|
menu_text += parent->title().replace(
|
||||||
|
"&", ""); // Strip out any &s used in menu action names
|
||||||
|
|
||||||
// Loop over the menu's actions
|
// Loop over the menu's actions
|
||||||
QList<QAction *> actions = parent->actions();
|
QList<QAction *> actions = parent->actions();
|
||||||
for (int i = 0; i < actions.size(); i++) {
|
for (int i = 0; i < actions.size(); i++) {
|
||||||
|
|
||||||
QAction *a = actions.at(i);
|
QAction *a = actions.at(i);
|
||||||
|
|
||||||
// Ignore separator actions
|
// Ignore separator actions
|
||||||
if (!a->isSeparator()) {
|
if (!a->isSeparator()) {
|
||||||
|
|
||||||
if (a->menu() != nullptr) {
|
if (a->menu() != nullptr) {
|
||||||
|
|
||||||
// If the action is a menu, run this function recursively on it
|
// If the action is a menu, run this function recursively on it
|
||||||
search_update(s, menu_text, a->menu());
|
search_update(s, menu_text, a->menu());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// This is a valid non-separator non-menu action, so check it against the currently entered string.
|
// 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
|
// Strip out all &s from the action's name
|
||||||
@@ -157,45 +158,43 @@ void ActionSearch::search_update(const QString &s, const QString &p, QMenu *pare
|
|||||||
|
|
||||||
// See if the action's name contains any of the currently entered string
|
// See if the action's name contains any of the currently entered string
|
||||||
if (comp.contains(s, Qt::CaseInsensitive)) {
|
if (comp.contains(s, Qt::CaseInsensitive)) {
|
||||||
|
|
||||||
// If so, we add it to the list widget.
|
// If so, we add it to the list widget.
|
||||||
QListWidgetItem* item = new QListWidgetItem(QStringLiteral("%1\n(%2)").arg(comp, menu_text), 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
|
// Add a pointer to the original QAction in the item's data
|
||||||
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
|
item->setData(Qt::UserRole + 1,
|
||||||
|
reinterpret_cast<quintptr>(a));
|
||||||
|
|
||||||
list_widget->addItem(item);
|
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.
|
// 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();
|
QList<QListWidgetItem *> selected_items = list_widget->selectedItems();
|
||||||
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
||||||
|
|
||||||
QListWidgetItem *item = selected_items.at(0);
|
QListWidgetItem *item = selected_items.at(0);
|
||||||
|
|
||||||
// Get QAction pointer from item's data
|
// Get QAction pointer from item's data
|
||||||
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
|
QAction *a = reinterpret_cast<QAction *>(
|
||||||
|
item->data(Qt::UserRole + 1).value<quintptr>());
|
||||||
|
|
||||||
a->trigger();
|
a->trigger();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close this popup
|
// Close this popup
|
||||||
accept();
|
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
|
// 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
|
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
|
||||||
// bottom item).
|
// bottom item).
|
||||||
@@ -210,8 +209,8 @@ void ActionSearch::move_selection_up() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// 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
|
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
|
||||||
// bottom item).
|
// bottom item).
|
||||||
@@ -226,7 +225,10 @@ void ActionSearch::move_selection_down() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {}
|
ActionSearchEntry::ActionSearchEntry(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool ActionSearchEntry::event(QEvent *e)
|
bool ActionSearchEntry::event(QEvent *e)
|
||||||
{
|
{
|
||||||
@@ -259,13 +261,15 @@ bool ActionSearchEntry::event(QEvent *e)
|
|||||||
return QLineEdit::event(e);
|
return QLineEdit::event(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {}
|
ActionSearchList::ActionSearchList(QWidget *parent)
|
||||||
|
: QListWidget(parent)
|
||||||
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) {
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *)
|
||||||
|
{
|
||||||
// Indiscriminately emit a signal on any double click
|
// Indiscriminately emit a signal on any double click
|
||||||
emit dbl_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,8 +40,7 @@ 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:
|
||||||
/**
|
/**
|
||||||
@@ -82,7 +82,8 @@ 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
|
||||||
@@ -108,6 +109,7 @@ private slots:
|
|||||||
* 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
|
||||||
@@ -136,6 +138,7 @@ public:
|
|||||||
* 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.
|
||||||
@@ -164,6 +167,7 @@ public:
|
|||||||
* 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.
|
||||||
|
|||||||
@@ -29,12 +29,15 @@
|
|||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
@@ -65,14 +68,18 @@ void AutoRecoveryDialog::Init(const QString& header_text)
|
|||||||
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 =
|
||||||
|
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
|
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
|
||||||
connect(buttons, &QDialogButtonBox::accepted, this, &AutoRecoveryDialog::accept);
|
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||||
connect(buttons, &QDialogButtonBox::rejected, this, &AutoRecoveryDialog::reject);
|
&AutoRecoveryDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||||
|
&AutoRecoveryDialog::reject);
|
||||||
layout->addWidget(buttons);
|
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());
|
||||||
@@ -83,7 +90,8 @@ void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autoch
|
|||||||
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(
|
||||||
|
recovery_dir.filePath(QStringLiteral("realname.txt")));
|
||||||
if (pretty_name_file.open(QFile::ReadOnly)) {
|
if (pretty_name_file.open(QFile::ReadOnly)) {
|
||||||
// Read pretty name that we should have written in the autorecovery process
|
// Read pretty name that we should have written in the autorecovery process
|
||||||
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
|
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
|
||||||
@@ -101,30 +109,41 @@ void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autoch
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Populate with recoveries
|
// Populate with recoveries
|
||||||
QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
|
QStringList entries =
|
||||||
|
recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
|
||||||
|
QDir::Name | QDir::Reversed);
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
const QString &entry = entries.at(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 =
|
||||||
|
entry.left(entry.indexOf('.')).toLongLong(&ok);
|
||||||
QString entry_name;
|
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 =
|
||||||
|
QDateTime::fromSecsSinceEpoch(recovery_time)
|
||||||
|
.toString();
|
||||||
} else {
|
} else {
|
||||||
// Fallback if we couldn't discern a date from this
|
// Fallback if we couldn't discern a date from this
|
||||||
entry_name = entry;
|
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,13 +26,14 @@
|
|||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class AutoRecoveryDialog : public QDialog
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class AutoRecoveryDialog : public QDialog {
|
||||||
Q_OBJECT
|
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;
|
||||||
@@ -46,10 +47,7 @@ private:
|
|||||||
|
|
||||||
QVector<QTreeWidgetItem *> checkable_items_;
|
QVector<QTreeWidgetItem *> checkable_items_;
|
||||||
|
|
||||||
enum DataRole {
|
enum DataRole { kFilenameRole = Qt::UserRole };
|
||||||
kFilenameRole = Qt::UserRole
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,13 @@
|
|||||||
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start, QWidget *parent) :
|
ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
|
||||||
QDialog(parent),
|
QWidget *parent)
|
||||||
color_manager_(color_manager)
|
: QDialog(parent)
|
||||||
|
, color_manager_(color_manager)
|
||||||
{
|
{
|
||||||
setWindowTitle(tr("Select Color"));
|
setWindowTitle(tr("Select Color"));
|
||||||
|
|
||||||
@@ -52,7 +54,8 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
|||||||
wheel_layout->addWidget(color_wheel_);
|
wheel_layout->addWidget(color_wheel_);
|
||||||
|
|
||||||
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
|
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
|
||||||
hsv_value_gradient_->setFixedWidth(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
|
hsv_value_gradient_->setFixedWidth(
|
||||||
|
QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
|
||||||
wheel_layout->addWidget(hsv_value_gradient_);
|
wheel_layout->addWidget(hsv_value_gradient_);
|
||||||
|
|
||||||
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
||||||
@@ -82,29 +85,44 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
|||||||
// Split window 50/50
|
// Split window 50/50
|
||||||
splitter->setSizes({ INT_MAX, INT_MAX });
|
splitter->setSizes({ INT_MAX, INT_MAX });
|
||||||
|
|
||||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
|
||||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
color_values_widget_, &ColorValuesWidget::SetColor);
|
||||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
|
||||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_,
|
||||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
&ColorSwatchChooser::SetCurrentColor);
|
||||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
color_values_widget_, &ColorValuesWidget::SetColor);
|
||||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_, &ColorValuesWidget::SetColor);
|
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::DiameterChanged,
|
||||||
|
hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
QDialogButtonBox *buttons =
|
||||||
|
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
layout->addWidget(buttons);
|
layout->addWidget(buttons);
|
||||||
|
|
||||||
SetColor(start);
|
SetColor(start);
|
||||||
|
|
||||||
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged);
|
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this,
|
||||||
|
&ColorDialog::ColorSpaceChanged);
|
||||||
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
||||||
|
|
||||||
// Set default size ratio to 2:1
|
// Set default size ratio to 2:1
|
||||||
@@ -119,18 +137,15 @@ void ColorDialog::SetColor(const ManagedColor &start)
|
|||||||
Color managed_start;
|
Color managed_start;
|
||||||
|
|
||||||
if (start.color_input().isEmpty()) {
|
if (start.color_input().isEmpty()) {
|
||||||
|
|
||||||
managed_start = start;
|
managed_start = start;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Convert reference color to the input space
|
// Convert reference color to the input space
|
||||||
ColorProcessorPtr linear_to_input = ColorProcessor::Create(color_manager_,
|
ColorProcessorPtr linear_to_input = ColorProcessor::Create(
|
||||||
color_manager_->GetReferenceColorSpace(),
|
color_manager_, color_manager_->GetReferenceColorSpace(),
|
||||||
start.color_input());
|
start.color_input());
|
||||||
|
|
||||||
managed_start = linear_to_input->ConvertColor(start);
|
managed_start = linear_to_input->ConvertColor(start);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
color_wheel_->SetSelectedColor(managed_start);
|
color_wheel_->SetSelectedColor(managed_start);
|
||||||
@@ -164,15 +179,17 @@ 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)
|
||||||
@@ -185,8 +202,10 @@ void ColorDialog::ColorSpaceChanged(const QString &input, const ColorTransform &
|
|||||||
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,10 +31,10 @@
|
|||||||
#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
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ColorDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -56,7 +56,9 @@ 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
|
||||||
@@ -89,7 +91,6 @@ private:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ColorSpaceChanged(const QString &input, const ColorTransform &output);
|
void ColorSpaceChanged(const QString &input, const ColorTransform &output);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,11 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
|
ConfigDialogBase::ConfigDialogBase(QWidget *parent)
|
||||||
QDialog(parent)
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
@@ -46,17 +47,18 @@ ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
|
|||||||
|
|
||||||
QDialogButtonBox *button_box = new QDialogButtonBox(this);
|
QDialogButtonBox *button_box = new QDialogButtonBox(this);
|
||||||
button_box->setOrientation(Qt::Horizontal);
|
button_box->setOrientation(Qt::Horizontal);
|
||||||
button_box->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
button_box->setStandardButtons(QDialogButtonBox::Cancel |
|
||||||
|
QDialogButtonBox::Ok);
|
||||||
|
|
||||||
layout->addWidget(button_box);
|
layout->addWidget(button_box);
|
||||||
|
|
||||||
connect(button_box, &QDialogButtonBox::accepted, this, &ConfigDialogBase::accept);
|
connect(button_box, &QDialogButtonBox::accepted, this,
|
||||||
connect(button_box, &QDialogButtonBox::rejected, this, &ConfigDialogBase::reject);
|
&ConfigDialogBase::accept);
|
||||||
|
connect(button_box, &QDialogButtonBox::rejected, this,
|
||||||
|
&ConfigDialogBase::reject);
|
||||||
|
|
||||||
connect(list_widget_,
|
connect(list_widget_, &QListWidget::currentRowChanged,
|
||||||
&QListWidget::currentRowChanged,
|
preference_pane_stack_, &QStackedWidget::setCurrentIndex);
|
||||||
preference_pane_stack_,
|
|
||||||
&QStackedWidget::setCurrentIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigDialogBase::accept()
|
void ConfigDialogBase::accept()
|
||||||
|
|||||||
@@ -27,10 +27,10 @@
|
|||||||
|
|
||||||
#include "configdialogbasetab.h"
|
#include "configdialogbasetab.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class ConfigDialogBase : public QDialog
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ConfigDialogBase : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ConfigDialogBase(QWidget *parent = nullptr);
|
ConfigDialogBase(QWidget *parent = nullptr);
|
||||||
@@ -44,7 +44,9 @@ private slots:
|
|||||||
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_;
|
||||||
@@ -52,7 +54,6 @@ private:
|
|||||||
QStackedWidget *preference_pane_stack_;
|
QStackedWidget *preference_pane_stack_;
|
||||||
|
|
||||||
QList<ConfigDialogBaseTab *> tabs_;
|
QList<ConfigDialogBaseTab *> tabs_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
#include "configdialogbasetab.h"
|
#include "configdialogbasetab.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
bool ConfigDialogBaseTab::Validate()
|
bool ConfigDialogBaseTab::Validate()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
#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;
|
||||||
|
|
||||||
|
|||||||
@@ -28,17 +28,19 @@
|
|||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) :
|
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
||||||
QDialog(parent),
|
: QDialog(parent)
|
||||||
folder_(folder)
|
, folder_(folder)
|
||||||
{
|
{
|
||||||
QGridLayout *layout = new QGridLayout(this);
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())), row, 0, 1, 2);
|
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())),
|
||||||
|
row, 0, 1, 2);
|
||||||
setWindowTitle(tr("Disk Cache Settings"));
|
setWindowTitle(tr("Disk Cache Settings"));
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
@@ -48,32 +50,40 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) :
|
|||||||
maximum_cache_slider_ = new FloatSlider();
|
maximum_cache_slider_ = new FloatSlider();
|
||||||
maximum_cache_slider_->SetFormat(tr("%1 GB"));
|
maximum_cache_slider_->SetFormat(tr("%1 GB"));
|
||||||
maximum_cache_slider_->SetMinimum(1.0);
|
maximum_cache_slider_->SetMinimum(1.0);
|
||||||
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) / static_cast<double>(kBytesInGigabyte));
|
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) /
|
||||||
|
static_cast<double>(kBytesInGigabyte));
|
||||||
layout->addWidget(maximum_cache_slider_, row, 1);
|
layout->addWidget(maximum_cache_slider_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
|
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
|
||||||
connect(clear_cache_btn_, &QPushButton::clicked, this, static_cast<void(DiskCacheDialog::*)()>(&DiskCacheDialog::ClearDiskCache));
|
connect(clear_cache_btn_, &QPushButton::clicked, this,
|
||||||
|
static_cast<void (DiskCacheDialog::*)()>(
|
||||||
|
&DiskCacheDialog::ClearDiskCache));
|
||||||
layout->addWidget(clear_cache_btn_, row, 1);
|
layout->addWidget(clear_cache_btn_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
clear_disk_cache_ = new QCheckBox(tr("Automatically clear disk cache on close"));
|
clear_disk_cache_ =
|
||||||
|
new QCheckBox(tr("Automatically clear disk cache on close"));
|
||||||
clear_disk_cache_->setChecked(folder->GetClearOnClose());
|
clear_disk_cache_->setChecked(folder->GetClearOnClose());
|
||||||
layout->addWidget(clear_disk_cache_, row, 1);
|
layout->addWidget(clear_disk_cache_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
QDialogButtonBox *buttons =
|
||||||
connect(buttons, &QDialogButtonBox::accepted, this, &DiskCacheDialog::accept);
|
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
connect(buttons, &QDialogButtonBox::rejected, this, &DiskCacheDialog::reject);
|
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||||
|
&DiskCacheDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||||
|
&DiskCacheDialog::reject);
|
||||||
layout->addWidget(buttons, row, 0, 1, 2);
|
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 =
|
||||||
|
qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
|
||||||
if (new_disk_cache_limit != folder_->GetLimit()) {
|
if (new_disk_cache_limit != folder_->GetLimit()) {
|
||||||
folder_->SetLimit(new_disk_cache_limit);
|
folder_->SetLimit(new_disk_cache_limit);
|
||||||
}
|
}
|
||||||
@@ -90,22 +100,27 @@ 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'?")
|
||||||
|
.arg(path),
|
||||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||||
if (clear_btn) clear_btn->setEnabled(false);
|
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)
|
||||||
|
clear_btn->setText(tr("Disk Cache Cleared"));
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::information(parent,
|
QMessageBox::information(
|
||||||
tr("Clear Disk Cache"),
|
parent, tr("Clear Disk Cache"),
|
||||||
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
|
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
if (clear_btn) clear_btn->setText(tr("Disk Cache Partially Cleared"));
|
if (clear_btn)
|
||||||
|
clear_btn->setText(tr("Disk Cache Partially Cleared"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,15 +28,16 @@
|
|||||||
#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
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class DiskCacheDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
|
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
|
||||||
|
|
||||||
static void ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn = 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;
|
||||||
@@ -52,7 +53,6 @@ private:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ClearDiskCache();
|
void ClearDiskCache();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,15 +28,16 @@
|
|||||||
#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);
|
||||||
@@ -45,7 +46,8 @@ AV1Section::AV1Section(int default_crf, QWidget *parent) :
|
|||||||
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(
|
||||||
|
"This parameter governs the efficiency/encode-time trade-off.\n"
|
||||||
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
|
"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."));
|
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
|
||||||
|
|
||||||
@@ -61,7 +63,8 @@ AV1Section::AV1Section(int default_crf, QWidget *parent) :
|
|||||||
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(
|
||||||
|
"This parameter governs the quality/size trade-off.\n"
|
||||||
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
|
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
|
||||||
"Lower CRF values retain more detail at the cost of larger file sizes.\n"
|
"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."));
|
"The possible range of CRF in SVT-AV1 is 1-63."));
|
||||||
@@ -79,29 +82,29 @@ AV1Section::AV1Section(int default_crf, QWidget *parent) :
|
|||||||
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(
|
||||||
|
compression_box,
|
||||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||||
compression_method_stack_,
|
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
||||||
&QStackedWidget::setCurrentIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AV1Section::AddOpts(EncodingParams *params)
|
void AV1Section::AddOpts(EncodingParams *params)
|
||||||
{
|
{
|
||||||
|
CompressionMethod method = static_cast<CompressionMethod>(
|
||||||
CompressionMethod method = static_cast<CompressionMethod>(compression_method_stack_->currentIndex());
|
compression_method_stack_->currentIndex());
|
||||||
|
|
||||||
if (method == kConstantRateFactor) {
|
if (method == kConstantRateFactor) {
|
||||||
|
|
||||||
// Set Quantizer value
|
// Set Quantizer value
|
||||||
params->set_video_option(QStringLiteral("qp"), QString::number(crf_section_->GetValue()));
|
params->set_video_option(QStringLiteral("qp"),
|
||||||
|
QString::number(crf_section_->GetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex()));
|
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);
|
||||||
@@ -113,15 +116,18 @@ AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) :
|
|||||||
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->fontMetrics(), QStringLiteral("HHHH")));
|
||||||
crf_input->SetMinimum(kMinimumCRF);
|
crf_input->SetMinimum(kMinimumCRF);
|
||||||
crf_input->SetMaximum(kMaximumCRF);
|
crf_input->SetMaximum(kMaximumCRF);
|
||||||
crf_input->SetValue(default_crf);
|
crf_input->SetValue(default_crf);
|
||||||
crf_input->SetDefaultValue(default_crf);
|
crf_input->SetDefaultValue(default_crf);
|
||||||
layout->addWidget(crf_input);
|
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
|
||||||
|
|||||||
@@ -28,10 +28,10 @@
|
|||||||
#include "codecsection.h"
|
#include "codecsection.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class AV1CRFSection : public QWidget
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class AV1CRFSection : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AV1CRFSection(int default_crf, QWidget *parent = nullptr);
|
AV1CRFSection(int default_crf, QWidget *parent = nullptr);
|
||||||
@@ -45,11 +45,9 @@ private:
|
|||||||
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 {
|
||||||
|
|||||||
@@ -23,10 +23,11 @@
|
|||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
CineformSection::CineformSection(QWidget *parent) :
|
CineformSection::CineformSection(QWidget *parent)
|
||||||
CodecSection(parent)
|
: CodecSection(parent)
|
||||||
{
|
{
|
||||||
QGridLayout *layout = new QGridLayout(this);
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
|
|
||||||
@@ -79,12 +80,15 @@ CineformSection::CineformSection(QWidget *parent) :
|
|||||||
|
|
||||||
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,10 +25,10 @@
|
|||||||
|
|
||||||
#include "codecsection.h"
|
#include "codecsection.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
|
||||||
class CineformSection : public CodecSection
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CineformSection : public CodecSection {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CineformSection(QWidget *parent = nullptr);
|
CineformSection(QWidget *parent = nullptr);
|
||||||
@@ -39,7 +39,6 @@ public:
|
|||||||
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CodecSection : public QWidget {
|
||||||
Q_OBJECT
|
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)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
#include "codecstack.h"
|
#include "codecstack.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
#define super QStackedWidget
|
#define super QStackedWidget
|
||||||
|
|
||||||
@@ -41,9 +42,11 @@ void CodecStack::OnChange(int index)
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < count(); i++) {
|
for (int i = 0; i < count(); i++) {
|
||||||
if (i == index) {
|
if (i == index) {
|
||||||
widget(i)->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
widget(i)->setSizePolicy(QSizePolicy::Expanding,
|
||||||
|
QSizePolicy::Expanding);
|
||||||
} else {
|
} else {
|
||||||
widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
widget(i)->setSizePolicy(QSizePolicy::Ignored,
|
||||||
|
QSizePolicy::Ignored);
|
||||||
}
|
}
|
||||||
widget(i)->adjustSize();
|
widget(i)->adjustSize();
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user