change: change code style to Linux style except indent.
This commit is contained in:
+69
-101
@@ -1,148 +1,116 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# clang-format configuration file. Intended for clang-format >= 11.
|
||||
#
|
||||
# For more information, see:
|
||||
#
|
||||
# Documentation/dev-tools/clang-format.rst
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
FixNamespaceComments: false
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentGotoLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- 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
|
||||
|
||||
# Taken from git's rules
|
||||
PenaltyBreakAssignment: 10
|
||||
PenaltyBreakBeforeFirstCallParameter: 30
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakString: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
TabWidth: 4
|
||||
UseTab: Always
|
||||
...
|
||||
|
||||
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
# Generated from CLion Inspection settings
|
||||
---
|
||||
Checks: '-*,
|
||||
bugprone-argument-comment,
|
||||
bugprone-assert-side-effect,
|
||||
bugprone-bad-signal-to-kill-thread,
|
||||
bugprone-branch-clone,
|
||||
bugprone-copy-constructor-init,
|
||||
bugprone-dangling-handle,
|
||||
bugprone-dynamic-static-initializers,
|
||||
bugprone-fold-init-type,
|
||||
bugprone-forward-declaration-namespace,
|
||||
bugprone-forwarding-reference-overload,
|
||||
bugprone-inaccurate-erase,
|
||||
bugprone-incorrect-roundings,
|
||||
bugprone-integer-division,
|
||||
bugprone-lambda-function-name,
|
||||
bugprone-macro-parentheses,
|
||||
bugprone-macro-repeated-side-effects,
|
||||
bugprone-misplaced-operator-in-strlen-in-alloc,
|
||||
bugprone-misplaced-pointer-arithmetic-in-alloc,
|
||||
bugprone-misplaced-widening-cast,
|
||||
bugprone-move-forwarding-reference,
|
||||
bugprone-multiple-statement-macro,
|
||||
bugprone-no-escape,
|
||||
bugprone-parent-virtual-call,
|
||||
bugprone-posix-return,
|
||||
bugprone-reserved-identifier,
|
||||
bugprone-sizeof-container,
|
||||
bugprone-sizeof-expression,
|
||||
bugprone-spuriously-wake-up-functions,
|
||||
bugprone-string-constructor,
|
||||
bugprone-string-integer-assignment,
|
||||
bugprone-string-literal-with-embedded-nul,
|
||||
bugprone-suspicious-enum-usage,
|
||||
bugprone-suspicious-include,
|
||||
bugprone-suspicious-memset-usage,
|
||||
bugprone-suspicious-missing-comma,
|
||||
bugprone-suspicious-semicolon,
|
||||
bugprone-suspicious-string-compare,
|
||||
bugprone-suspicious-memory-comparison,
|
||||
bugprone-suspicious-realloc-usage,
|
||||
bugprone-swapped-arguments,
|
||||
bugprone-terminating-continue,
|
||||
bugprone-throw-keyword-missing,
|
||||
bugprone-too-small-loop-variable,
|
||||
bugprone-undefined-memory-manipulation,
|
||||
bugprone-undelegated-constructor,
|
||||
bugprone-unhandled-self-assignment,
|
||||
bugprone-unused-raii,
|
||||
bugprone-unused-return-value,
|
||||
bugprone-use-after-move,
|
||||
bugprone-virtual-near-miss,
|
||||
cert-dcl21-cpp,
|
||||
cert-dcl58-cpp,
|
||||
cert-err34-c,
|
||||
cert-err52-cpp,
|
||||
cert-err60-cpp,
|
||||
cert-flp30-c,
|
||||
cert-msc50-cpp,
|
||||
cert-msc51-cpp,
|
||||
cert-str34-c,
|
||||
cppcoreguidelines-interfaces-global-init,
|
||||
cppcoreguidelines-narrowing-conversions,
|
||||
cppcoreguidelines-pro-type-member-init,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
cppcoreguidelines-slicing,
|
||||
google-default-arguments,
|
||||
google-explicit-constructor,
|
||||
google-runtime-operator,
|
||||
hicpp-exception-baseclass,
|
||||
hicpp-multiway-paths-covered,
|
||||
misc-misplaced-const,
|
||||
misc-new-delete-overloads,
|
||||
misc-no-recursion,
|
||||
misc-non-copyable-objects,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-unconventional-assign-operator,
|
||||
misc-uniqueptr-reset-release,
|
||||
modernize-avoid-bind,
|
||||
modernize-concat-nested-namespaces,
|
||||
modernize-deprecated-headers,
|
||||
modernize-deprecated-ios-base-aliases,
|
||||
modernize-loop-convert,
|
||||
modernize-make-shared,
|
||||
modernize-make-unique,
|
||||
modernize-pass-by-value,
|
||||
modernize-raw-string-literal,
|
||||
modernize-redundant-void-arg,
|
||||
modernize-replace-auto-ptr,
|
||||
modernize-replace-disallow-copy-and-assign-macro,
|
||||
modernize-replace-random-shuffle,
|
||||
modernize-return-braced-init-list,
|
||||
modernize-shrink-to-fit,
|
||||
modernize-unary-static-assert,
|
||||
modernize-use-auto,
|
||||
modernize-use-bool-literals,
|
||||
modernize-use-emplace,
|
||||
modernize-use-equals-default,
|
||||
modernize-use-equals-delete,
|
||||
modernize-use-nodiscard,
|
||||
modernize-use-noexcept,
|
||||
modernize-use-nullptr,
|
||||
modernize-use-override,
|
||||
modernize-use-transparent-functors,
|
||||
modernize-use-uncaught-exceptions,
|
||||
mpi-buffer-deref,
|
||||
mpi-type-mismatch,
|
||||
openmp-use-default-none,
|
||||
performance-faster-string-find,
|
||||
performance-for-range-copy,
|
||||
performance-implicit-conversion-in-loop,
|
||||
performance-inefficient-algorithm,
|
||||
performance-inefficient-string-concatenation,
|
||||
performance-inefficient-vector-operation,
|
||||
performance-move-const-arg,
|
||||
performance-move-constructor-init,
|
||||
performance-no-automatic-move,
|
||||
performance-noexcept-move-constructor,
|
||||
performance-trivially-destructible,
|
||||
performance-type-promotion-in-math-fn,
|
||||
performance-unnecessary-copy-initialization,
|
||||
performance-unnecessary-value-param,
|
||||
portability-simd-intrinsics,
|
||||
readability-avoid-const-params-in-decls,
|
||||
readability-const-return-type,
|
||||
readability-container-size-empty,
|
||||
readability-convert-member-functions-to-static,
|
||||
readability-delete-null-pointer,
|
||||
readability-deleted-default,
|
||||
readability-inconsistent-declaration-parameter-name,
|
||||
readability-make-member-function-const,
|
||||
readability-misleading-indentation,
|
||||
readability-misplaced-array-index,
|
||||
readability-non-const-parameter,
|
||||
readability-redundant-control-flow,
|
||||
readability-redundant-declaration,
|
||||
readability-redundant-function-ptr-dereference,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-redundant-string-init,
|
||||
readability-simplify-subscript-expr,
|
||||
readability-static-accessed-through-instance,
|
||||
readability-static-definition-in-anonymous-namespace,
|
||||
readability-string-compare,
|
||||
readability-uniqueptr-delete-release,
|
||||
readability-use-anyofallof'
|
||||
+200
-171
@@ -28,290 +28,319 @@
|
||||
|
||||
#include "config/config.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
AudioManager* AudioManager::instance_ = nullptr;
|
||||
AudioManager *AudioManager::instance_ = nullptr;
|
||||
|
||||
void AudioManager::CreateInstance()
|
||||
{
|
||||
if (instance_ == nullptr) {
|
||||
instance_ = new AudioManager();
|
||||
}
|
||||
if (instance_ == nullptr) {
|
||||
instance_ = new AudioManager();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
AudioManager *AudioManager::instance()
|
||||
{
|
||||
return instance_;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
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 read_count = device->read(reinterpret_cast<char*>(output), max_read);
|
||||
if (read_count < max_read) {
|
||||
memset(reinterpret_cast<uint8_t*>(output) + read_count, 0, max_read - read_count);
|
||||
}
|
||||
qint64 max_read = frameCount * device->bytes_per_frame();
|
||||
qint64 read_count =
|
||||
device->read(reinterpret_cast<char *>(output), max_read);
|
||||
if (read_count < max_read) {
|
||||
memset(reinterpret_cast<uint8_t *>(output) + read_count, 0,
|
||||
max_read - read_count);
|
||||
}
|
||||
|
||||
return paContinue;
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
int InputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
|
||||
int InputCallback(const void *input, void *output, unsigned long frameCount,
|
||||
const PaStreamCallbackTimeInfo *timeInfo,
|
||||
PaStreamCallbackFlags statusFlags, void *userData)
|
||||
{
|
||||
FFmpegEncoder *f = static_cast<FFmpegEncoder*>(userData);
|
||||
FFmpegEncoder *f = static_cast<FFmpegEncoder *>(userData);
|
||||
|
||||
AudioParams our_params = f->params().audio_params();
|
||||
our_params.set_format(f->params().audio_params().format().to_packed_equivalent());
|
||||
AudioParams our_params = f->params().audio_params();
|
||||
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 (error) *error = tr("No output device is set");
|
||||
return false;
|
||||
}
|
||||
if (output_device_ == paNoDevice) {
|
||||
if (error)
|
||||
*error = tr("No output device is set");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (output_params_ != params || output_stream_ == nullptr) {
|
||||
output_params_ = params;
|
||||
if (output_params_ != params || output_stream_ == nullptr) {
|
||||
output_params_ = params;
|
||||
|
||||
CloseOutputStream();
|
||||
CloseOutputStream();
|
||||
|
||||
PaStreamParameters p = GetPortAudioParams(params, output_device_);
|
||||
PaStreamParameters p = GetPortAudioParams(params, output_device_);
|
||||
|
||||
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_);
|
||||
if (r != paNoError) {
|
||||
// Unhandled error
|
||||
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
|
||||
if (error) *error = Pa_GetErrorText(r);
|
||||
return false;
|
||||
}
|
||||
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p,
|
||||
output_params_.sample_rate(),
|
||||
paFramesPerBufferUnspecified, paNoFlag,
|
||||
OutputCallback, output_buffer_);
|
||||
if (r != paNoError) {
|
||||
// Unhandled error
|
||||
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
|
||||
if (error)
|
||||
*error = Pa_GetErrorText(r);
|
||||
return false;
|
||||
}
|
||||
|
||||
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
|
||||
}
|
||||
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
|
||||
}
|
||||
|
||||
output_buffer_->write(samples);
|
||||
output_buffer_->write(samples);
|
||||
|
||||
if (!Pa_IsStreamActive(output_stream_)) {
|
||||
Pa_StartStream(output_stream_);
|
||||
}
|
||||
if (!Pa_IsStreamActive(output_stream_)) {
|
||||
Pa_StartStream(output_stream_);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioManager::ClearBufferedOutput()
|
||||
{
|
||||
output_buffer_->clear();
|
||||
output_buffer_->clear();
|
||||
}
|
||||
|
||||
PaSampleFormat AudioManager::GetPortAudioSampleFormat(SampleFormat fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case SampleFormat::U8:
|
||||
case SampleFormat::U8P:
|
||||
return paUInt8;
|
||||
case SampleFormat::S16:
|
||||
case SampleFormat::S16P:
|
||||
return paInt16;
|
||||
case SampleFormat::S32:
|
||||
case SampleFormat::S32P:
|
||||
return paInt32;
|
||||
case SampleFormat::F32:
|
||||
case SampleFormat::F32P:
|
||||
return paFloat32;
|
||||
case SampleFormat::S64:
|
||||
case SampleFormat::S64P:
|
||||
case SampleFormat::F64:
|
||||
case SampleFormat::F64P:
|
||||
case SampleFormat::INVALID:
|
||||
case SampleFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
switch (fmt) {
|
||||
case SampleFormat::U8:
|
||||
case SampleFormat::U8P:
|
||||
return paUInt8;
|
||||
case SampleFormat::S16:
|
||||
case SampleFormat::S16P:
|
||||
return paInt16;
|
||||
case SampleFormat::S32:
|
||||
case SampleFormat::S32P:
|
||||
return paInt32;
|
||||
case SampleFormat::F32:
|
||||
case SampleFormat::F32P:
|
||||
return paFloat32;
|
||||
case SampleFormat::S64:
|
||||
case SampleFormat::S64P:
|
||||
case SampleFormat::F64:
|
||||
case SampleFormat::F64P:
|
||||
case SampleFormat::INVALID:
|
||||
case SampleFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AudioManager::CloseOutputStream()
|
||||
{
|
||||
if (output_stream_) {
|
||||
if (Pa_IsStreamActive(output_stream_)) {
|
||||
StopOutput();
|
||||
}
|
||||
Pa_CloseStream(output_stream_);
|
||||
output_stream_ = nullptr;
|
||||
}
|
||||
if (output_stream_) {
|
||||
if (Pa_IsStreamActive(output_stream_)) {
|
||||
StopOutput();
|
||||
}
|
||||
Pa_CloseStream(output_stream_);
|
||||
output_stream_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::StopOutput()
|
||||
{
|
||||
// Abort the stream so playback stops immediately
|
||||
if (output_stream_) {
|
||||
Pa_AbortStream(output_stream_);
|
||||
ClearBufferedOutput();
|
||||
}
|
||||
// Abort the stream so playback stops immediately
|
||||
if (output_stream_) {
|
||||
Pa_AbortStream(output_stream_);
|
||||
ClearBufferedOutput();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::SetOutputDevice(PaDeviceIndex device)
|
||||
{
|
||||
if (device == paNoDevice) {
|
||||
qInfo() << "No output device found";
|
||||
} else {
|
||||
qInfo() << "Setting output audio device to" << Pa_GetDeviceInfo(device)->name;
|
||||
}
|
||||
if (device == paNoDevice) {
|
||||
qInfo() << "No output device found";
|
||||
} else {
|
||||
qInfo() << "Setting output audio device to"
|
||||
<< Pa_GetDeviceInfo(device)->name;
|
||||
}
|
||||
|
||||
output_device_ = device;
|
||||
output_device_ = device;
|
||||
|
||||
CloseOutputStream();
|
||||
CloseOutputStream();
|
||||
}
|
||||
|
||||
void AudioManager::SetInputDevice(PaDeviceIndex device)
|
||||
{
|
||||
if (device == paNoDevice) {
|
||||
qInfo() << "No input device found";
|
||||
} else {
|
||||
qInfo() << "Setting input audio device to" << Pa_GetDeviceInfo(device)->name;
|
||||
}
|
||||
if (device == paNoDevice) {
|
||||
qInfo() << "No input device found";
|
||||
} else {
|
||||
qInfo() << "Setting input audio device to"
|
||||
<< Pa_GetDeviceInfo(device)->name;
|
||||
}
|
||||
|
||||
input_device_ = device;
|
||||
input_device_ = device;
|
||||
}
|
||||
|
||||
void AudioManager::HardReset()
|
||||
{
|
||||
CloseOutputStream();
|
||||
Pa_Terminate();
|
||||
Pa_Initialize();
|
||||
CloseOutputStream();
|
||||
Pa_Terminate();
|
||||
Pa_Initialize();
|
||||
}
|
||||
|
||||
bool AudioManager::StartRecording(const EncodingParams ¶ms, QString *error_str)
|
||||
bool AudioManager::StartRecording(const EncodingParams ¶ms,
|
||||
QString *error_str)
|
||||
{
|
||||
if (input_device_ == paNoDevice) {
|
||||
return false;
|
||||
}
|
||||
if (input_device_ == paNoDevice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
input_encoder_ = new FFmpegEncoder(params);
|
||||
if (!input_encoder_->Open()) {
|
||||
qCritical() << "Failed to open encoder for recording";
|
||||
return false;
|
||||
}
|
||||
input_encoder_ = new FFmpegEncoder(params);
|
||||
if (!input_encoder_->Open()) {
|
||||
qCritical() << "Failed to open encoder for recording";
|
||||
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_);
|
||||
if (r == paNoError) {
|
||||
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
|
||||
r = Pa_StartStream(input_stream_);
|
||||
if (r == paNoError) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr,
|
||||
params.audio_params().sample_rate(),
|
||||
paFramesPerBufferUnspecified, paNoFlag,
|
||||
InputCallback, input_encoder_);
|
||||
if (r == paNoError) {
|
||||
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
|
||||
r = Pa_StartStream(input_stream_);
|
||||
if (r == paNoError) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (error_str) {
|
||||
*error_str = Pa_GetErrorText(r);
|
||||
}
|
||||
if (error_str) {
|
||||
*error_str = Pa_GetErrorText(r);
|
||||
}
|
||||
|
||||
StopRecording();
|
||||
return false;
|
||||
StopRecording();
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioManager::StopRecording()
|
||||
{
|
||||
if (input_stream_) {
|
||||
if (Pa_IsStreamActive(input_stream_)) {
|
||||
Pa_StopStream(input_stream_);
|
||||
}
|
||||
Pa_CloseStream(input_stream_);
|
||||
if (input_stream_) {
|
||||
if (Pa_IsStreamActive(input_stream_)) {
|
||||
Pa_StopStream(input_stream_);
|
||||
}
|
||||
Pa_CloseStream(input_stream_);
|
||||
|
||||
input_stream_ = nullptr;
|
||||
}
|
||||
input_stream_ = nullptr;
|
||||
}
|
||||
|
||||
if (input_encoder_) {
|
||||
input_encoder_->Close();
|
||||
delete input_encoder_;
|
||||
input_encoder_ = nullptr;
|
||||
}
|
||||
if (input_encoder_) {
|
||||
input_encoder_->Close();
|
||||
delete input_encoder_;
|
||||
input_encoder_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
for (PaDeviceIndex i=0, end=Pa_GetDeviceCount(); i<end; i++) {
|
||||
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
|
||||
if (!s.isEmpty()) {
|
||||
for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) {
|
||||
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
|
||||
|
||||
if (((is_output_device && device->maxOutputChannels) || (!is_output_device && device->maxInputChannels))
|
||||
&& !s.compare(device->name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (((is_output_device && device->maxOutputChannels) ||
|
||||
(!is_output_device && device->maxInputChannels)) &&
|
||||
!s.compare(device->name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is_output_device ? Pa_GetDefaultOutputDevice() : Pa_GetDefaultInputDevice();
|
||||
return is_output_device ? Pa_GetDefaultOutputDevice() :
|
||||
Pa_GetDefaultInputDevice();
|
||||
}
|
||||
|
||||
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams ¶ms, PaDeviceIndex device)
|
||||
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams ¶ms,
|
||||
PaDeviceIndex device)
|
||||
{
|
||||
PaStreamParameters p;
|
||||
PaStreamParameters p;
|
||||
|
||||
p.channelCount = params.channel_count();
|
||||
p.device = device;
|
||||
p.hostApiSpecificStreamInfo = nullptr;
|
||||
p.sampleFormat = GetPortAudioSampleFormat(params.format());
|
||||
p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
|
||||
p.channelCount = params.channel_count();
|
||||
p.device = device;
|
||||
p.hostApiSpecificStreamInfo = nullptr;
|
||||
p.sampleFormat = GetPortAudioSampleFormat(params.format());
|
||||
p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
|
||||
|
||||
return p;
|
||||
return p;
|
||||
}
|
||||
|
||||
AudioManager::AudioManager() :
|
||||
output_stream_(nullptr),
|
||||
input_stream_(nullptr),
|
||||
input_encoder_(nullptr)
|
||||
AudioManager::AudioManager()
|
||||
: output_stream_(nullptr)
|
||||
, input_stream_(nullptr)
|
||||
, input_encoder_(nullptr)
|
||||
{
|
||||
#ifdef PA_HAS_JACK
|
||||
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
|
||||
// a QString converted to UTF-8)
|
||||
PaJack_SetClientName("Olive");
|
||||
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
|
||||
// a QString converted to UTF-8)
|
||||
PaJack_SetClientName("Olive");
|
||||
#endif
|
||||
|
||||
Pa_Initialize();
|
||||
Pa_Initialize();
|
||||
|
||||
// Get device from config
|
||||
PaDeviceIndex output_device = FindConfigDeviceByName(true);
|
||||
PaDeviceIndex input_device = FindConfigDeviceByName(false);
|
||||
// Get device from config
|
||||
PaDeviceIndex output_device = FindConfigDeviceByName(true);
|
||||
PaDeviceIndex input_device = FindConfigDeviceByName(false);
|
||||
|
||||
SetOutputDevice(output_device);
|
||||
SetInputDevice(input_device);
|
||||
SetOutputDevice(output_device);
|
||||
SetInputDevice(input_device);
|
||||
|
||||
output_buffer_ = new PreviewAudioDevice(this);
|
||||
output_buffer_->open(PreviewAudioDevice::ReadWrite);
|
||||
connect(output_buffer_, &PreviewAudioDevice::Notify, this, &AudioManager::OutputNotify);
|
||||
output_buffer_ = new PreviewAudioDevice(this);
|
||||
output_buffer_->open(PreviewAudioDevice::ReadWrite);
|
||||
connect(output_buffer_, &PreviewAudioDevice::Notify, this,
|
||||
&AudioManager::OutputNotify);
|
||||
}
|
||||
|
||||
AudioManager::~AudioManager()
|
||||
{
|
||||
CloseOutputStream();
|
||||
CloseOutputStream();
|
||||
|
||||
Pa_Terminate();
|
||||
Pa_Terminate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
-42
@@ -33,7 +33,8 @@
|
||||
#include "render/audioplaybackcache.h"
|
||||
#include "render/previewaudiodevice.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Audio input and output management class
|
||||
@@ -41,74 +42,76 @@ namespace olive {
|
||||
* Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to
|
||||
* the rest of the system.
|
||||
*/
|
||||
class AudioManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class AudioManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static void CreateInstance();
|
||||
static void DestroyInstance();
|
||||
static void CreateInstance();
|
||||
static void DestroyInstance();
|
||||
|
||||
static AudioManager* instance();
|
||||
static AudioManager *instance();
|
||||
|
||||
void SetOutputNotifyInterval(int n);
|
||||
void SetOutputNotifyInterval(int n);
|
||||
|
||||
bool PushToOutput(const AudioParams ¶ms, const QByteArray& samples, QString *error = nullptr);
|
||||
bool PushToOutput(const AudioParams ¶ms, const QByteArray &samples,
|
||||
QString *error = nullptr);
|
||||
|
||||
void ClearBufferedOutput();
|
||||
void ClearBufferedOutput();
|
||||
|
||||
void StopOutput();
|
||||
void StopOutput();
|
||||
|
||||
PaDeviceIndex GetOutputDevice() const
|
||||
{
|
||||
return output_device_;
|
||||
}
|
||||
PaDeviceIndex GetOutputDevice() const
|
||||
{
|
||||
return output_device_;
|
||||
}
|
||||
|
||||
PaDeviceIndex GetInputDevice() const
|
||||
{
|
||||
return input_device_;
|
||||
}
|
||||
PaDeviceIndex GetInputDevice() const
|
||||
{
|
||||
return input_device_;
|
||||
}
|
||||
|
||||
void SetOutputDevice(PaDeviceIndex device);
|
||||
void SetOutputDevice(PaDeviceIndex device);
|
||||
|
||||
void SetInputDevice(PaDeviceIndex device);
|
||||
void SetInputDevice(PaDeviceIndex device);
|
||||
|
||||
void HardReset();
|
||||
void HardReset();
|
||||
|
||||
bool StartRecording(const EncodingParams ¶ms, QString *error_str = nullptr);
|
||||
bool StartRecording(const EncodingParams ¶ms,
|
||||
QString *error_str = nullptr);
|
||||
|
||||
void StopRecording();
|
||||
void StopRecording();
|
||||
|
||||
static PaDeviceIndex FindConfigDeviceByName(bool is_output_device);
|
||||
static PaDeviceIndex FindDeviceByName(const QString &s, bool is_output_device);
|
||||
static PaDeviceIndex FindConfigDeviceByName(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:
|
||||
void OutputNotify();
|
||||
void OutputNotify();
|
||||
|
||||
void OutputParamsChanged();
|
||||
void OutputParamsChanged();
|
||||
|
||||
private:
|
||||
AudioManager();
|
||||
AudioManager();
|
||||
|
||||
virtual ~AudioManager() override;
|
||||
virtual ~AudioManager() override;
|
||||
|
||||
static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt);
|
||||
static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt);
|
||||
|
||||
void CloseOutputStream();
|
||||
void CloseOutputStream();
|
||||
|
||||
static AudioManager* instance_;
|
||||
static AudioManager *instance_;
|
||||
|
||||
PaDeviceIndex output_device_;
|
||||
PaStream *output_stream_;
|
||||
AudioParams output_params_;
|
||||
PreviewAudioDevice *output_buffer_;
|
||||
PaDeviceIndex output_device_;
|
||||
PaStream *output_stream_;
|
||||
AudioParams output_params_;
|
||||
PreviewAudioDevice *output_buffer_;
|
||||
|
||||
PaDeviceIndex input_device_;
|
||||
PaStream *input_stream_;
|
||||
|
||||
FFmpegEncoder *input_encoder_;
|
||||
PaDeviceIndex input_device_;
|
||||
PaStream *input_stream_;
|
||||
|
||||
FFmpegEncoder *input_encoder_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+226
-207
@@ -29,279 +29,298 @@ extern "C" {
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
AudioProcessor::AudioProcessor()
|
||||
{
|
||||
filter_graph_ = nullptr;
|
||||
in_frame_ = nullptr;
|
||||
out_frame_ = nullptr;
|
||||
filter_graph_ = nullptr;
|
||||
in_frame_ = nullptr;
|
||||
out_frame_ = nullptr;
|
||||
}
|
||||
|
||||
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_) {
|
||||
qWarning() << "Tried to open a processor that was already open";
|
||||
return false;
|
||||
}
|
||||
if (filter_graph_) {
|
||||
qWarning() << "Tried to open a processor that was already open";
|
||||
return false;
|
||||
}
|
||||
|
||||
filter_graph_ = avfilter_graph_alloc();
|
||||
if (!filter_graph_) {
|
||||
qCritical() << "Failed to allocate filter graph";
|
||||
return false;
|
||||
}
|
||||
filter_graph_ = avfilter_graph_alloc();
|
||||
if (!filter_graph_) {
|
||||
qCritical() << "Failed to allocate filter graph";
|
||||
return false;
|
||||
}
|
||||
|
||||
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
|
||||
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
|
||||
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
|
||||
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
|
||||
|
||||
// Set up audio buffer args
|
||||
char filter_args[200];
|
||||
snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
|
||||
1,
|
||||
from.sample_rate(),
|
||||
from.sample_rate(),
|
||||
from_fmt_,
|
||||
from.channel_layout().u.mask);
|
||||
// Set up audio buffer args
|
||||
char filter_args[200];
|
||||
snprintf(
|
||||
filter_args, 200,
|
||||
"time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
|
||||
1, from.sample_rate(), from.sample_rate(), from_fmt_,
|
||||
from.channel_layout().u.mask);
|
||||
|
||||
int r;
|
||||
int r;
|
||||
|
||||
// Create buffersrc (input)
|
||||
r = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("abuffer"), "in", filter_args, nullptr, filter_graph_);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to create buffersrc:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
// Create buffersrc (input)
|
||||
r = avfilter_graph_create_filter(&buffersrc_ctx_,
|
||||
avfilter_get_by_name("abuffer"), "in",
|
||||
filter_args, nullptr, filter_graph_);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to create buffersrc:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store "previous" filter for linking
|
||||
AVFilterContext *previous_filter = buffersrc_ctx_;
|
||||
// Store "previous" filter for linking
|
||||
AVFilterContext *previous_filter = buffersrc_ctx_;
|
||||
|
||||
// Create tempo
|
||||
bool create_tempo;
|
||||
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
|
||||
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
|
||||
// those boundaries, we need to daisychain more than one together.
|
||||
double base = (tempo > 1.0) ? 2.0 : 0.5;
|
||||
double speed_log = log(tempo) / log(base);
|
||||
// Create tempo
|
||||
bool create_tempo;
|
||||
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
|
||||
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
|
||||
// those boundaries, we need to daisychain more than one together.
|
||||
double base = (tempo > 1.0) ? 2.0 : 0.5;
|
||||
double speed_log = log(tempo) / log(base);
|
||||
|
||||
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
|
||||
int whole = std::floor(speed_log);
|
||||
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
|
||||
int whole = std::floor(speed_log);
|
||||
|
||||
// Set speed_log to the remainder
|
||||
speed_log -= whole;
|
||||
// Set speed_log to the remainder
|
||||
speed_log -= whole;
|
||||
|
||||
for (int i=0;i<=whole;i++) {
|
||||
double filter_tempo = (i == whole) ? std::pow(base, speed_log) : base;
|
||||
/*
|
||||
for (int i = 0; i <= whole; i++) {
|
||||
double filter_tempo = (i == whole) ? std::pow(base, speed_log) :
|
||||
base;
|
||||
/*
|
||||
if (qFuzzyCompare(filter_tempo, 1.0)) {
|
||||
// This filter would do nothing
|
||||
continue;
|
||||
}*/
|
||||
|
||||
previous_filter = CreateTempoFilter(filter_graph_,
|
||||
previous_filter,
|
||||
filter_tempo);
|
||||
previous_filter =
|
||||
CreateTempoFilter(filter_graph_, previous_filter, filter_tempo);
|
||||
|
||||
if (!previous_filter) {
|
||||
qCritical() << "Failed to create audio tempo filter";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!previous_filter) {
|
||||
qCritical() << "Failed to create audio tempo filter";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create conversion filter
|
||||
auto ch1=from.channel_layout();
|
||||
auto ch2=to.channel_layout();
|
||||
if (from.sample_rate() != to.sample_rate() || 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
|
||||
// to be converted
|
||||
snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
|
||||
av_get_sample_fmt_name(to_fmt_),
|
||||
to.sample_rate(),
|
||||
to.channel_layout().u.mask);
|
||||
// Create conversion filter
|
||||
auto ch1 = from.channel_layout();
|
||||
auto ch2 = to.channel_layout();
|
||||
if (from.sample_rate() != to.sample_rate() ||
|
||||
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
|
||||
// to be converted
|
||||
snprintf(filter_args, 200,
|
||||
"sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
|
||||
av_get_sample_fmt_name(to_fmt_), to.sample_rate(),
|
||||
to.channel_layout().u.mask);
|
||||
|
||||
AVFilterContext *c;
|
||||
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"), "fmt", filter_args, nullptr, filter_graph_);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to create format conversion filter:" << r << filter_args;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
AVFilterContext *c;
|
||||
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"),
|
||||
"fmt", filter_args, nullptr,
|
||||
filter_graph_);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to create format conversion filter:" << r
|
||||
<< filter_args;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
r = avfilter_link(previous_filter, 0, c, 0);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to link filters:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
r = avfilter_link(previous_filter, 0, c, 0);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to link filters:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
previous_filter = c;
|
||||
}
|
||||
previous_filter = c;
|
||||
}
|
||||
|
||||
// Create buffersink (output)
|
||||
r = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph_);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to create buffersink:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
// Create buffersink (output)
|
||||
r = avfilter_graph_create_filter(&buffersink_ctx_,
|
||||
avfilter_get_by_name("abuffersink"), "out",
|
||||
nullptr, nullptr, filter_graph_);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to create buffersink:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to link filters:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
char *dump = avfilter_graph_dump(filter_graph_, nullptr);
|
||||
qDebug() << dump;
|
||||
av_free(dump);
|
||||
r = avfilter_graph_config(filter_graph_, nullptr);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to configure graph:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to link filters:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
char *dump = avfilter_graph_dump(filter_graph_, nullptr);
|
||||
qDebug() << dump;
|
||||
av_free(dump);
|
||||
r = avfilter_graph_config(filter_graph_, nullptr);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to configure graph:" << r;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
in_frame_ = av_frame_alloc();
|
||||
if (in_frame_) {
|
||||
in_frame_->sample_rate = from.sample_rate();
|
||||
in_frame_->format = from_fmt_;
|
||||
in_frame_->ch_layout = from.channel_layout();
|
||||
in_frame_->pts = 0;
|
||||
} else {
|
||||
qCritical() << "Failed to allocate input frame";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
in_frame_ = av_frame_alloc();
|
||||
if (in_frame_) {
|
||||
in_frame_->sample_rate = from.sample_rate();
|
||||
in_frame_->format = from_fmt_;
|
||||
in_frame_->ch_layout = from.channel_layout();
|
||||
in_frame_->pts = 0;
|
||||
} else {
|
||||
qCritical() << "Failed to allocate input frame";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
out_frame_ = av_frame_alloc();
|
||||
if (!out_frame_) {
|
||||
qCritical() << "Failed to allocate output frame";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
out_frame_ = av_frame_alloc();
|
||||
if (!out_frame_) {
|
||||
qCritical() << "Failed to allocate output frame";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
from_ = from;
|
||||
to_ = to;
|
||||
from_ = from;
|
||||
to_ = to;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioProcessor::Close()
|
||||
{
|
||||
if (filter_graph_) {
|
||||
avfilter_graph_free(&filter_graph_);
|
||||
filter_graph_ = nullptr;
|
||||
buffersrc_ctx_ = nullptr;
|
||||
buffersink_ctx_ = nullptr;
|
||||
}
|
||||
if (filter_graph_) {
|
||||
avfilter_graph_free(&filter_graph_);
|
||||
filter_graph_ = nullptr;
|
||||
buffersrc_ctx_ = nullptr;
|
||||
buffersink_ctx_ = nullptr;
|
||||
}
|
||||
|
||||
if (in_frame_) {
|
||||
av_frame_free(&in_frame_);
|
||||
in_frame_ = nullptr;
|
||||
}
|
||||
if (in_frame_) {
|
||||
av_frame_free(&in_frame_);
|
||||
in_frame_ = nullptr;
|
||||
}
|
||||
|
||||
if (out_frame_) {
|
||||
av_frame_free(&out_frame_);
|
||||
out_frame_ = nullptr;
|
||||
}
|
||||
if (out_frame_) {
|
||||
av_frame_free(&out_frame_);
|
||||
out_frame_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output)
|
||||
int AudioProcessor::Convert(float **in, int nb_in_samples,
|
||||
AudioProcessor::Buffer *output)
|
||||
{
|
||||
if (!IsOpen()) {
|
||||
qCritical() << "Tried to convert on closed processor";
|
||||
return -1;
|
||||
}
|
||||
if (!IsOpen()) {
|
||||
qCritical() << "Tried to convert on closed processor";
|
||||
return -1;
|
||||
}
|
||||
|
||||
int r = 0;
|
||||
int r = 0;
|
||||
|
||||
if (in && nb_in_samples) {
|
||||
// Set frame parameters
|
||||
in_frame_->nb_samples = nb_in_samples;
|
||||
for (int i=0; i<from_.channel_count(); i++) {
|
||||
in_frame_->data[i] = reinterpret_cast<uint8_t*>(in[i]);
|
||||
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
|
||||
}
|
||||
if (in && nb_in_samples) {
|
||||
// Set frame parameters
|
||||
in_frame_->nb_samples = nb_in_samples;
|
||||
for (int i = 0; i < from_.channel_count(); i++) {
|
||||
in_frame_->data[i] = reinterpret_cast<uint8_t *>(in[i]);
|
||||
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);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to add frame to buffersrc:" << r;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_,
|
||||
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to add frame to buffersrc:" << r;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (output) {
|
||||
int nb_channels = to_.channel_count();
|
||||
if (output) {
|
||||
int nb_channels = to_.channel_count();
|
||||
|
||||
if (to_.format().is_packed()) {
|
||||
nb_channels = 1;
|
||||
}
|
||||
if (to_.format().is_packed()) {
|
||||
nb_channels = 1;
|
||||
}
|
||||
|
||||
AudioProcessor::Buffer &result = *output;
|
||||
result.resize(nb_channels);
|
||||
AudioProcessor::Buffer &result = *output;
|
||||
result.resize(nb_channels);
|
||||
|
||||
int byte_offset = 0;
|
||||
int byte_offset = 0;
|
||||
|
||||
while (true) {
|
||||
av_frame_unref(out_frame_);
|
||||
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
|
||||
if (r < 0) {
|
||||
if (r == AVERROR(EAGAIN)) {
|
||||
r = 0;
|
||||
} else {
|
||||
// Handle unexpected error
|
||||
qCritical() << "Failed to pull from buffersink:" << r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
while (true) {
|
||||
av_frame_unref(out_frame_);
|
||||
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
|
||||
if (r < 0) {
|
||||
if (r == AVERROR(EAGAIN)) {
|
||||
r = 0;
|
||||
} else {
|
||||
// Handle unexpected error
|
||||
qCritical() << "Failed to pull from buffersink:" << r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int nb_bytes = out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
|
||||
if (to_.format().is_packed()) {
|
||||
nb_bytes *= to_.channel_count();
|
||||
}
|
||||
int nb_bytes =
|
||||
out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
|
||||
if (to_.format().is_packed()) {
|
||||
nb_bytes *= to_.channel_count();
|
||||
}
|
||||
|
||||
for (int i=0; i<nb_channels; i++) {
|
||||
result[i].resize(byte_offset + nb_bytes);
|
||||
memcpy(result[i].data() + byte_offset, out_frame_->data[i], nb_bytes);
|
||||
}
|
||||
byte_offset += nb_bytes;
|
||||
}
|
||||
av_frame_unref(out_frame_);
|
||||
}
|
||||
for (int i = 0; i < nb_channels; i++) {
|
||||
result[i].resize(byte_offset + nb_bytes);
|
||||
memcpy(result[i].data() + byte_offset, out_frame_->data[i],
|
||||
nb_bytes);
|
||||
}
|
||||
byte_offset += nb_bytes;
|
||||
}
|
||||
av_frame_unref(out_frame_);
|
||||
}
|
||||
|
||||
return r;
|
||||
return r;
|
||||
}
|
||||
|
||||
void AudioProcessor::Flush()
|
||||
{
|
||||
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to flush:" << r;
|
||||
}
|
||||
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr,
|
||||
AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||
if (r < 0) {
|
||||
qCritical() << "Failed to flush:" << r;
|
||||
}
|
||||
}
|
||||
|
||||
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilterContext* link, const double &tempo)
|
||||
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph *graph,
|
||||
AVFilterContext *link,
|
||||
const double &tempo)
|
||||
{
|
||||
// Set up tempo param, which is taken as a C string
|
||||
char speed_param[20];
|
||||
snprintf(speed_param, 20, "%f", tempo);
|
||||
// Set up tempo param, which is taken as a C string
|
||||
char speed_param[20];
|
||||
snprintf(speed_param, 20, "%f", tempo);
|
||||
|
||||
AVFilterContext* tempo_ctx = nullptr;
|
||||
AVFilterContext *tempo_ctx = nullptr;
|
||||
|
||||
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0
|
||||
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) {
|
||||
return tempo_ctx;
|
||||
}
|
||||
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"),
|
||||
"atempo", speed_param, nullptr,
|
||||
graph) >= 0 &&
|
||||
avfilter_link(link, 0, tempo_ctx, 0) == 0) {
|
||||
return tempo_ctx;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+36
-25
@@ -31,52 +31,63 @@ extern "C" {
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
class AudioProcessor
|
||||
{
|
||||
class AudioProcessor {
|
||||
public:
|
||||
AudioProcessor();
|
||||
AudioProcessor();
|
||||
|
||||
~AudioProcessor();
|
||||
~AudioProcessor();
|
||||
|
||||
DISABLE_COPY_MOVE(AudioProcessor)
|
||||
DISABLE_COPY_MOVE(AudioProcessor)
|
||||
|
||||
bool Open(const AudioParams &from, const AudioParams &to, double tempo = 1.0);
|
||||
bool Open(const AudioParams &from, const AudioParams &to,
|
||||
double tempo = 1.0);
|
||||
|
||||
void Close();
|
||||
void Close();
|
||||
|
||||
bool IsOpen() const { return filter_graph_; }
|
||||
bool IsOpen() const
|
||||
{
|
||||
return filter_graph_;
|
||||
}
|
||||
|
||||
using Buffer = QVector<QByteArray>;
|
||||
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
|
||||
using Buffer = QVector<QByteArray>;
|
||||
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
|
||||
|
||||
void Flush();
|
||||
void Flush();
|
||||
|
||||
const AudioParams &from() const { return from_; }
|
||||
const AudioParams &to() const { return to_; }
|
||||
const AudioParams &from() const
|
||||
{
|
||||
return from_;
|
||||
}
|
||||
const AudioParams &to() const
|
||||
{
|
||||
return to_;
|
||||
}
|
||||
|
||||
private:
|
||||
static AVFilterContext* CreateTempoFilter(AVFilterGraph *graph, AVFilterContext *link, const double& tempo);
|
||||
static AVFilterContext *CreateTempoFilter(AVFilterGraph *graph,
|
||||
AVFilterContext *link,
|
||||
const double &tempo);
|
||||
|
||||
AVFilterGraph* filter_graph_;
|
||||
AVFilterGraph *filter_graph_;
|
||||
|
||||
AVFilterContext* buffersrc_ctx_;
|
||||
AVFilterContext *buffersrc_ctx_;
|
||||
|
||||
AVFilterContext* buffersink_ctx_;
|
||||
AVFilterContext *buffersink_ctx_;
|
||||
|
||||
AudioParams from_;
|
||||
AVSampleFormat from_fmt_;
|
||||
AudioParams from_;
|
||||
AVSampleFormat from_fmt_;
|
||||
|
||||
AudioParams to_;
|
||||
AVSampleFormat to_fmt_;
|
||||
AudioParams to_;
|
||||
AVSampleFormat to_fmt_;
|
||||
|
||||
AVFrame *in_frame_;
|
||||
|
||||
AVFrame *out_frame_;
|
||||
AVFrame *in_frame_;
|
||||
|
||||
AVFrame *out_frame_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+371
-315
@@ -25,480 +25,536 @@
|
||||
|
||||
#include "config/config.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8);
|
||||
const rational AudioVisualWaveform::kMaximumSampleRate = 1024;
|
||||
|
||||
AudioVisualWaveform::AudioVisualWaveform() :
|
||||
channels_(0)
|
||||
AudioVisualWaveform::AudioVisualWaveform()
|
||||
: channels_(0)
|
||||
{
|
||||
for (rational i=kMinimumSampleRate; i<=kMaximumSampleRate; i*=2) {
|
||||
mipmapped_data_.insert({i, Sample()});
|
||||
}
|
||||
for (rational i = kMinimumSampleRate; i <= kMaximumSampleRate; i *= 2) {
|
||||
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);
|
||||
samples_length = time_to_samples(static_cast<double>(samples.sample_count()) / static_cast<double>(sample_rate), 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);
|
||||
|
||||
size_t end_index = start_index + samples_length;
|
||||
if (data.size() < end_index) {
|
||||
data.resize(end_index);
|
||||
}
|
||||
size_t end_index = start_index + samples_length;
|
||||
if (data.size() < end_index) {
|
||||
data.resize(end_index);
|
||||
}
|
||||
|
||||
double chunk_size = double(sample_rate) / double(target_rate);
|
||||
double chunk_size = double(sample_rate) / double(target_rate);
|
||||
|
||||
for (size_t i=0; i<samples_length; i+=channels_) {
|
||||
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());
|
||||
for (size_t i = 0; i < samples_length; i += 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());
|
||||
|
||||
Sample summary = SumSamples(samples,
|
||||
src_start,
|
||||
src_end - src_start);
|
||||
Sample summary = SumSamples(samples, src_start, src_end - src_start);
|
||||
|
||||
memcpy(&data.data()[i + start_index],
|
||||
summary.data(),
|
||||
summary.size() * sizeof(SamplePerChannel));
|
||||
}
|
||||
memcpy(&data.data()[i + start_index], summary.data(),
|
||||
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 samples_length = time_to_samples(static_cast<double>(input_length / channels_) / input_sample_rate, 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 end_index = start_index + samples_length;
|
||||
if (output_data.size() < end_index) {
|
||||
output_data.resize(end_index);
|
||||
}
|
||||
size_t end_index = start_index + samples_length;
|
||||
if (output_data.size() < end_index) {
|
||||
output_data.resize(end_index);
|
||||
}
|
||||
|
||||
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
|
||||
size_t chunk_size = input_sample_rate / output_rate;
|
||||
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
|
||||
size_t chunk_size = input_sample_rate / output_rate;
|
||||
|
||||
for (size_t i=0; i<samples_length; i+=channels_) {
|
||||
Sample summary = ReSumSamples(&input.data()[input_start + (i*chunk_size)], chunk_size * channels_, 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_);
|
||||
|
||||
memcpy(&output_data.data()[i + start_index],
|
||||
summary.data(),
|
||||
summary.size() * sizeof(SamplePerChannel));
|
||||
}
|
||||
memcpy(&output_data.data()[i + start_index], summary.data(),
|
||||
summary.size() * sizeof(SamplePerChannel));
|
||||
}
|
||||
|
||||
input_start = start_index;
|
||||
input_length = samples_length;
|
||||
input_start = start_index;
|
||||
input_length = samples_length;
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::ValidateVirtualStart(const rational &new_start)
|
||||
{
|
||||
if (length_ == 0) {
|
||||
virtual_start_ = new_start;
|
||||
} else if (virtual_start_ > new_start) {
|
||||
TrimIn(new_start - virtual_start_);
|
||||
}
|
||||
if (length_ == 0) {
|
||||
virtual_start_ = new_start;
|
||||
} else if (virtual_start_ > new_start) {
|
||||
TrimIn(new_start - virtual_start_);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational &start)
|
||||
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples,
|
||||
int sample_rate,
|
||||
const rational &start)
|
||||
{
|
||||
if (!channels_) {
|
||||
qWarning() << "Failed to write samples - channel count is zero";
|
||||
return;
|
||||
}
|
||||
if (!channels_) {
|
||||
qWarning() << "Failed to write samples - channel count is zero";
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateVirtualStart(start);
|
||||
ValidateVirtualStart(start);
|
||||
|
||||
// Process the largest mipmap directly for the samples
|
||||
auto current_mipmap = mipmapped_data_.rbegin();
|
||||
size_t input_start, input_length;
|
||||
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_, current_mipmap->first.toDouble(), current_mipmap->second, input_start, input_length);
|
||||
// Process the largest mipmap directly for the samples
|
||||
auto current_mipmap = mipmapped_data_.rbegin();
|
||||
size_t input_start, input_length;
|
||||
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_,
|
||||
current_mipmap->first.toDouble(),
|
||||
current_mipmap->second, input_start,
|
||||
input_length);
|
||||
|
||||
while (true) {
|
||||
// For each smaller mipmap, we just process from the mipmap before it, making each one
|
||||
// exponentially faster to create
|
||||
auto previous_mipmap = current_mipmap;
|
||||
current_mipmap++;
|
||||
if (current_mipmap == mipmapped_data_.rend()) {
|
||||
break;
|
||||
}
|
||||
while (true) {
|
||||
// For each smaller mipmap, we just process from the mipmap before it, making each one
|
||||
// exponentially faster to create
|
||||
auto previous_mipmap = current_mipmap;
|
||||
current_mipmap++;
|
||||
if (current_mipmap == mipmapped_data_.rend()) {
|
||||
break;
|
||||
}
|
||||
|
||||
OverwriteSamplesFromMipmap(previous_mipmap->second, previous_mipmap->first.toDouble(),
|
||||
input_start, input_length, start - virtual_start_, current_mipmap->first.toDouble(),
|
||||
current_mipmap->second);
|
||||
}
|
||||
OverwriteSamplesFromMipmap(
|
||||
previous_mipmap->second, previous_mipmap->first.toDouble(),
|
||||
input_start, input_length, start - virtual_start_,
|
||||
current_mipmap->first.toDouble(), current_mipmap->second);
|
||||
}
|
||||
|
||||
rational sample_length(samples.sample_count(), sample_rate);
|
||||
length_ = qMax(length_, start + sample_length);
|
||||
rational sample_length(samples.sample_count(), sample_rate);
|
||||
length_ = qMax(length_, start + sample_length);
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const rational &dest, const rational& offset, const rational& length)
|
||||
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums,
|
||||
const rational &dest,
|
||||
const rational &offset,
|
||||
const rational &length)
|
||||
{
|
||||
ValidateVirtualStart(dest);
|
||||
ValidateVirtualStart(dest);
|
||||
|
||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
|
||||
Sample& our_arr = it->second;
|
||||
const Sample& their_arr = sums.mipmapped_data_.at(rate);
|
||||
Sample &our_arr = it->second;
|
||||
const Sample &their_arr = sums.mipmapped_data_.at(rate);
|
||||
|
||||
double rate_dbl = rate.toDouble();
|
||||
double rate_dbl = rate.toDouble();
|
||||
|
||||
// Get our destination sample
|
||||
size_t our_start_index = time_to_samples(dest - virtual_start_, rate_dbl);
|
||||
// Get our destination sample
|
||||
size_t our_start_index =
|
||||
time_to_samples(dest - virtual_start_, rate_dbl);
|
||||
|
||||
// Get our source sample
|
||||
size_t their_start_index = time_to_samples(offset, rate_dbl);
|
||||
if (their_start_index >= their_arr.size()) {
|
||||
continue;
|
||||
}
|
||||
// Get our source sample
|
||||
size_t their_start_index = time_to_samples(offset, rate_dbl);
|
||||
if (their_start_index >= their_arr.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine how much we're copying
|
||||
size_t copy_len = their_arr.size() - their_start_index;
|
||||
if (!length.isNull()) {
|
||||
copy_len = qMin(copy_len, time_to_samples(length, rate_dbl));
|
||||
if (copy_len == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Determine how much we're copying
|
||||
size_t copy_len = their_arr.size() - their_start_index;
|
||||
if (!length.isNull()) {
|
||||
copy_len = qMin(copy_len, time_to_samples(length, rate_dbl));
|
||||
if (copy_len == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine end index of our array
|
||||
size_t end_index = our_start_index + copy_len;
|
||||
if (our_arr.size() < end_index) {
|
||||
our_arr.resize(end_index);
|
||||
}
|
||||
// Determine end index of our array
|
||||
size_t end_index = our_start_index + copy_len;
|
||||
if (our_arr.size() < end_index) {
|
||||
our_arr.resize(end_index);
|
||||
}
|
||||
|
||||
memcpy(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel),
|
||||
reinterpret_cast<const char*>(their_arr.data()) + their_start_index * sizeof(SamplePerChannel),
|
||||
copy_len * sizeof(SamplePerChannel));
|
||||
}
|
||||
memcpy(reinterpret_cast<char *>(our_arr.data()) +
|
||||
our_start_index * sizeof(SamplePerChannel),
|
||||
reinterpret_cast<const char *>(their_arr.data()) +
|
||||
their_start_index * sizeof(SamplePerChannel),
|
||||
copy_len * sizeof(SamplePerChannel));
|
||||
}
|
||||
|
||||
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset : length));
|
||||
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset :
|
||||
length));
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational &length)
|
||||
void AudioVisualWaveform::OverwriteSilence(const rational &start,
|
||||
const rational &length)
|
||||
{
|
||||
ValidateVirtualStart(start);
|
||||
ValidateVirtualStart(start);
|
||||
|
||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
|
||||
Sample& our_arr = it->second;
|
||||
Sample &our_arr = it->second;
|
||||
|
||||
double rate_dbl = rate.toDouble();
|
||||
double rate_dbl = rate.toDouble();
|
||||
|
||||
// Get our destination sample
|
||||
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_end_index = our_start_index + our_length_index;
|
||||
// Get our destination sample
|
||||
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_end_index = our_start_index + our_length_index;
|
||||
|
||||
if (our_arr.size() < our_end_index) {
|
||||
our_arr.resize(our_end_index);
|
||||
}
|
||||
if (our_arr.size() < our_end_index) {
|
||||
our_arr.resize(our_end_index);
|
||||
}
|
||||
|
||||
memset(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel), 0, our_length_index * sizeof(SamplePerChannel));
|
||||
}
|
||||
memset(reinterpret_cast<char *>(our_arr.data()) +
|
||||
our_start_index * sizeof(SamplePerChannel),
|
||||
0, our_length_index * sizeof(SamplePerChannel));
|
||||
}
|
||||
|
||||
length_ = qMax(length_, start + length);
|
||||
length_ = qMax(length_, start + length);
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::TrimIn(rational length)
|
||||
{
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
virtual_start_ += length;
|
||||
virtual_start_ += length;
|
||||
|
||||
bool negative = (length < 0);
|
||||
if (negative) {
|
||||
length = -length;
|
||||
}
|
||||
bool negative = (length < 0);
|
||||
if (negative) {
|
||||
length = -length;
|
||||
}
|
||||
|
||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
double rate_dbl = rate.toDouble();
|
||||
Sample& data = it->second;
|
||||
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
double rate_dbl = rate.toDouble();
|
||||
Sample &data = it->second;
|
||||
|
||||
size_t chop_length = time_to_samples(length, rate_dbl);
|
||||
if (chop_length == 0) {
|
||||
continue;
|
||||
}
|
||||
size_t chop_length = time_to_samples(length, rate_dbl);
|
||||
if (chop_length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!negative) {
|
||||
data = Sample(data.begin() + chop_length, data.end());
|
||||
} else {
|
||||
data.insert(data.begin(), chop_length, SamplePerChannel());
|
||||
}
|
||||
}
|
||||
if (!negative) {
|
||||
data = Sample(data.begin() + chop_length, data.end());
|
||||
} else {
|
||||
data.insert(data.begin(), chop_length, SamplePerChannel());
|
||||
}
|
||||
}
|
||||
|
||||
length_ = qMax(rational(0), length_ - length);
|
||||
length_ = qMax(rational(0), length_ - length);
|
||||
}
|
||||
|
||||
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const
|
||||
{
|
||||
AudioVisualWaveform mid = *this;
|
||||
AudioVisualWaveform mid = *this;
|
||||
|
||||
mid.TrimIn(offset - virtual_start_);
|
||||
mid.TrimIn(offset - virtual_start_);
|
||||
|
||||
return mid;
|
||||
return mid;
|
||||
}
|
||||
|
||||
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset, const rational &length) const
|
||||
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset,
|
||||
const rational &length) const
|
||||
{
|
||||
AudioVisualWaveform mid = *this;
|
||||
AudioVisualWaveform mid = *this;
|
||||
|
||||
mid.TrimRange(offset - virtual_start_, length);
|
||||
mid.TrimRange(offset - virtual_start_, length);
|
||||
|
||||
return mid;
|
||||
return mid;
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::Resize(const rational &length)
|
||||
{
|
||||
if (length_ == length) {
|
||||
return;
|
||||
}
|
||||
if (length_ == length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
double rate_dbl = rate.toDouble();
|
||||
Sample& data = it->second;
|
||||
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
|
||||
rational rate = it->first;
|
||||
double rate_dbl = rate.toDouble();
|
||||
Sample &data = it->second;
|
||||
|
||||
size_t chop_length = time_to_samples(length, rate_dbl);
|
||||
size_t chop_length = time_to_samples(length, rate_dbl);
|
||||
|
||||
data.resize(chop_length);
|
||||
}
|
||||
data.resize(chop_length);
|
||||
}
|
||||
|
||||
length_ = length;
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
void AudioVisualWaveform::TrimRange(const rational &in, const rational &length)
|
||||
{
|
||||
TrimIn(in);
|
||||
Resize(length);
|
||||
TrimIn(in);
|
||||
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
|
||||
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
|
||||
// Find mipmap that requires
|
||||
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
|
||||
|
||||
double rate_dbl = using_mipmap->first.toDouble();
|
||||
double rate_dbl = using_mipmap->first.toDouble();
|
||||
|
||||
size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl);
|
||||
size_t sample_length = time_to_samples(length, rate_dbl);
|
||||
size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl);
|
||||
size_t sample_length = time_to_samples(length, rate_dbl);
|
||||
|
||||
const Sample &mipmap_data = using_mipmap->second;
|
||||
const Sample &mipmap_data = using_mipmap->second;
|
||||
|
||||
// Determine if the array actually has this sample
|
||||
sample_length = qMin(sample_length, mipmap_data.size() - start_sample);
|
||||
// Determine if the array actually has this sample
|
||||
sample_length = qMin(sample_length, mipmap_data.size() - start_sample);
|
||||
|
||||
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
|
||||
// array and nothing can be returned.
|
||||
if (sample_length > 0) {
|
||||
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length, channels_);
|
||||
}
|
||||
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
|
||||
// array and nothing can be returned.
|
||||
if (sample_length > 0) {
|
||||
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length,
|
||||
channels_);
|
||||
}
|
||||
|
||||
// Return null samples
|
||||
return AudioVisualWaveform::Sample(channel_count(), {0, 0});
|
||||
// Return null samples
|
||||
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)
|
||||
// SSE optimized
|
||||
// SSE optimized
|
||||
|
||||
// load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits)
|
||||
__m128 max = _mm_loadu_ps(a);
|
||||
__m128 min = _mm_loadu_ps(a);
|
||||
// load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits)
|
||||
__m128 max = _mm_loadu_ps(a);
|
||||
__m128 min = _mm_loadu_ps(a);
|
||||
|
||||
// loop over 'a' and compare current elements with min and max 4 by 4.
|
||||
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
|
||||
for(size_t i = 4; i < length-4; i+=4) {
|
||||
__m128 cur = _mm_loadu_ps(a + i);
|
||||
max = _mm_max_ps(max, cur);
|
||||
min = _mm_min_ps(min, cur);
|
||||
}
|
||||
// so we read the last 4 (or less) elements in a safe manner.
|
||||
__m128 cur = _mm_loadu_ps(a + length - 4);
|
||||
max = _mm_max_ps(max, cur);
|
||||
min = _mm_min_ps(min, cur);
|
||||
// this potentially overlaps up to the last 3 elements but it's not an issue.
|
||||
// loop over 'a' and compare current elements with min and max 4 by 4.
|
||||
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
|
||||
for (size_t i = 4; i < length - 4; i += 4) {
|
||||
__m128 cur = _mm_loadu_ps(a + i);
|
||||
max = _mm_max_ps(max, cur);
|
||||
min = _mm_min_ps(min, cur);
|
||||
}
|
||||
// so we read the last 4 (or less) elements in a safe manner.
|
||||
__m128 cur = _mm_loadu_ps(a + length - 4);
|
||||
max = _mm_max_ps(max, cur);
|
||||
min = _mm_min_ps(min, cur);
|
||||
// this potentially overlaps up to the last 3 elements but it's not an issue.
|
||||
|
||||
// min and max will contain 4 min and max. To get the absolute min and max
|
||||
// we need to compare the 4 values over themselves by shuffling each time.
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93));
|
||||
min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93));
|
||||
}
|
||||
// now min and max contain 4 identical items each representing min and max value respectively.
|
||||
// min and max will contain 4 min and max. To get the absolute min and max
|
||||
// we need to compare the 4 values over themselves by shuffling each time.
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93));
|
||||
min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93));
|
||||
}
|
||||
// now min and max contain 4 identical items each representing min and max value respectively.
|
||||
|
||||
// and we store the first one into a float variable.
|
||||
_mm_store_ss(&max_val, max);
|
||||
_mm_store_ss(&min_val, min);
|
||||
// I bet you don't find annotated low level code very often.
|
||||
// and we store the first one into a float variable.
|
||||
_mm_store_ss(&max_val, max);
|
||||
_mm_store_ss(&min_val, min);
|
||||
// I bet you don't find annotated low level code very often.
|
||||
#else
|
||||
// Standard unoptimized function
|
||||
for (size_t i=0; i<length; i++) {
|
||||
min_val = std::min(min_val, a[i]);
|
||||
max_val = std::max(max_val, a[i]);
|
||||
}
|
||||
// Standard unoptimized function
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
min_val = std::min(min_val, a[i]);
|
||||
max_val = std::max(max_val, a[i]);
|
||||
}
|
||||
#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();
|
||||
AudioVisualWaveform::Sample summed_samples(channels);
|
||||
int channels = samples.audio_params().channel_count();
|
||||
AudioVisualWaveform::Sample summed_samples(channels);
|
||||
|
||||
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) {
|
||||
ExpandMinMaxChannel(samples.data(channel) + start_index, length, summed_samples[channel].min, summed_samples[channel].max);
|
||||
}
|
||||
for (int channel = 0; channel < samples.audio_params().channel_count();
|
||||
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 (size_t i=start_index; i<end_index; i++) {
|
||||
// ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]);
|
||||
// }
|
||||
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
|
||||
// for (size_t i=start_index; i<end_index; i++) {
|
||||
// ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]);
|
||||
// }
|
||||
|
||||
return summed_samples;
|
||||
return summed_samples;
|
||||
}
|
||||
|
||||
AudioVisualWaveform::Sample AudioVisualWaveform::ReSumSamples(const SamplePerChannel* samples,
|
||||
size_t nb_samples,
|
||||
int nb_channels)
|
||||
AudioVisualWaveform::Sample
|
||||
AudioVisualWaveform::ReSumSamples(const SamplePerChannel *samples,
|
||||
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 (int j=0;j<nb_channels;j++) {
|
||||
const AudioVisualWaveform::SamplePerChannel& sample = samples[i + j];
|
||||
for (size_t i = 0; i < nb_samples; i += nb_channels) {
|
||||
for (int j = 0; j < nb_channels; j++) {
|
||||
const AudioVisualWaveform::SamplePerChannel &sample =
|
||||
samples[i + j];
|
||||
|
||||
if (sample.min < summed_samples[j].min) {
|
||||
summed_samples[j].min = sample.min;
|
||||
}
|
||||
if (sample.min < summed_samples[j].min) {
|
||||
summed_samples[j].min = sample.min;
|
||||
}
|
||||
|
||||
if (sample.max > summed_samples[j].max) {
|
||||
summed_samples[j].max = sample.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sample.max > summed_samples[j].max) {
|
||||
summed_samples[j].max = sample.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return summed_samples;
|
||||
return summed_samples;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline int round_away_from_zero(T t)
|
||||
template <typename 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()) {
|
||||
return;
|
||||
}
|
||||
if (sample.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int channel_height = height / sample.size();
|
||||
int channel_half_height = channel_height / 2;
|
||||
int channel_height = height / sample.size();
|
||||
int channel_half_height = channel_height / 2;
|
||||
|
||||
for (size_t i=0;i<sample.size();i++) {
|
||||
float max = qMin(sample.at(i).max, 1.0f);
|
||||
float min = qMax(sample.at(i).min, -1.0f);
|
||||
for (size_t i = 0; i < sample.size(); i++) {
|
||||
float max = qMin(sample.at(i).max, 1.0f);
|
||||
float min = qMax(sample.at(i).min, -1.0f);
|
||||
|
||||
if (rectified) {
|
||||
int channel_bottom = y + channel_height * (i + 1);
|
||||
if (rectified) {
|
||||
int channel_bottom = y + channel_height * (i + 1);
|
||||
|
||||
int diff = round_away_from_zero((max - min) * channel_half_height);
|
||||
int diff = round_away_from_zero((max - min) * channel_half_height);
|
||||
|
||||
painter->drawLine(x,
|
||||
channel_bottom - diff,
|
||||
x,
|
||||
channel_bottom);
|
||||
} else {
|
||||
int channel_mid = y + channel_height * i + channel_half_height;
|
||||
painter->drawLine(x, channel_bottom - diff, x, channel_bottom);
|
||||
} else {
|
||||
int channel_mid = y + channel_height * i + channel_half_height;
|
||||
|
||||
// We subtract the sample so that positive Y values go up on the screen rather than down,
|
||||
// which is how waveforms are usually rendered
|
||||
painter->drawLine(x,
|
||||
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)));
|
||||
}
|
||||
}
|
||||
// We subtract the sample so that positive Y values go up on the screen rather than down,
|
||||
// which is how waveforms are usually rendered
|
||||
painter->drawLine(
|
||||
x,
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
if (samples.mipmapped_data_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto using_mipmap = samples.GetMipmapForScale(scale);
|
||||
auto using_mipmap = samples.GetMipmapForScale(scale);
|
||||
|
||||
rational rate = using_mipmap->first;
|
||||
double rate_dbl = rate.toDouble();
|
||||
const Sample& arr = using_mipmap->second;
|
||||
rational rate = using_mipmap->first;
|
||||
double rate_dbl = rate.toDouble();
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
if (start_sample_index >= arr.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t next_sample_index = start_sample_index;
|
||||
size_t sample_index;
|
||||
size_t next_sample_index = start_sample_index;
|
||||
size_t sample_index;
|
||||
|
||||
Sample summary;
|
||||
size_t summary_index = -1;
|
||||
Sample summary;
|
||||
size_t summary_index = -1;
|
||||
|
||||
const QRect& viewport = painter->viewport();
|
||||
QPoint top_left = painter->transform().map(viewport.topLeft());
|
||||
const QRect &viewport = painter->viewport();
|
||||
QPoint top_left = painter->transform().map(viewport.topLeft());
|
||||
|
||||
size_t start = qMax(rect.x(), -top_left.x());
|
||||
size_t end = qMin(rect.right(), -top_left.x() + viewport.width());
|
||||
size_t start = qMax(rect.x(), -top_left.x());
|
||||
size_t end = qMin(rect.right(), -top_left.x() + viewport.width());
|
||||
|
||||
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
|
||||
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
|
||||
|
||||
for (size_t i=start;i<end;i++) {
|
||||
sample_index = next_sample_index;
|
||||
for (size_t i = start; i < end; i++) {
|
||||
sample_index = next_sample_index;
|
||||
|
||||
if (sample_index == arr.size()) {
|
||||
break;
|
||||
}
|
||||
if (sample_index == arr.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
next_sample_index = std::min(arr.size(),
|
||||
size_t(start_sample_index + std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) / scale) * samples.channel_count()));
|
||||
next_sample_index = std::min(
|
||||
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) {
|
||||
summary = AudioVisualWaveform::ReSumSamples(&arr.at(sample_index),
|
||||
qMax(size_t(samples.channel_count()), next_sample_index - sample_index),
|
||||
samples.channel_count());
|
||||
summary_index = sample_index;
|
||||
}
|
||||
if (summary_index != sample_index) {
|
||||
summary = AudioVisualWaveform::ReSumSamples(
|
||||
&arr.at(sample_index),
|
||||
qMax(size_t(samples.channel_count()),
|
||||
next_sample_index - sample_index),
|
||||
samples.channel_count());
|
||||
summary_index = sample_index;
|
||||
}
|
||||
|
||||
DrawSample(painter, summary, i, rect.y(), rect.height(), rectified);
|
||||
}
|
||||
DrawSample(painter, summary, i, rect.y(), rect.height(), rectified);
|
||||
}
|
||||
}
|
||||
|
||||
size_t AudioVisualWaveform::time_to_samples(const rational &time, double sample_rate) const
|
||||
size_t AudioVisualWaveform::time_to_samples(const rational &time,
|
||||
double sample_rate) const
|
||||
{
|
||||
return time_to_samples(time.toDouble(), sample_rate);
|
||||
return time_to_samples(time.toDouble(), sample_rate);
|
||||
}
|
||||
|
||||
size_t AudioVisualWaveform::time_to_samples(const double &time, double sample_rate) const
|
||||
size_t AudioVisualWaveform::time_to_samples(const double &time,
|
||||
double sample_rate) const
|
||||
{
|
||||
return std::floor(time * sample_rate) * channels_;
|
||||
return std::floor(time * sample_rate) * channels_;
|
||||
}
|
||||
|
||||
std::map<rational, AudioVisualWaveform::Sample>::const_iterator AudioVisualWaveform::GetMipmapForScale(double scale) const
|
||||
std::map<rational, AudioVisualWaveform::Sample>::const_iterator
|
||||
AudioVisualWaveform::GetMipmapForScale(double scale) const
|
||||
{
|
||||
// Find largest mipmap for this scale (or the largest if we don't find one sufficient)
|
||||
for (auto it=mipmapped_data_.cbegin(); it!=mipmapped_data_.cend(); it++) {
|
||||
if (it->first.toDouble() >= scale) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
// 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++) {
|
||||
if (it->first.toDouble() >= scale) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have a mipmap large enough for this scale, so just return the largest we have
|
||||
return std::prev(mipmapped_data_.cend());
|
||||
// We don't have a mipmap large enough for this scale, so just return the largest we have
|
||||
return std::prev(mipmapped_data_.cend());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
#include <QPainter>
|
||||
#include <QVector>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
@@ -37,38 +38,39 @@ using namespace core;
|
||||
*/
|
||||
class AudioVisualWaveform {
|
||||
public:
|
||||
AudioVisualWaveform();
|
||||
AudioVisualWaveform();
|
||||
|
||||
struct SamplePerChannel {
|
||||
float min;
|
||||
float max;
|
||||
};
|
||||
struct SamplePerChannel {
|
||||
float min;
|
||||
float max;
|
||||
};
|
||||
|
||||
using Sample = std::vector<SamplePerChannel>;
|
||||
using Sample = std::vector<SamplePerChannel>;
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return channels_;
|
||||
}
|
||||
int channel_count() const
|
||||
{
|
||||
return channels_;
|
||||
}
|
||||
|
||||
void set_channel_count(int channels)
|
||||
{
|
||||
channels_ = channels;
|
||||
}
|
||||
void set_channel_count(int channels)
|
||||
{
|
||||
channels_ = channels;
|
||||
}
|
||||
|
||||
const rational& length() const
|
||||
{
|
||||
return length_;
|
||||
}
|
||||
const rational &length() const
|
||||
{
|
||||
return length_;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Writes samples into the visual waveform buffer
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @param sums
|
||||
@@ -87,53 +89,70 @@ public:
|
||||
*
|
||||
* 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 rational &length) const;
|
||||
AudioVisualWaveform Mid(const rational &offset) 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
|
||||
static const rational kMinimumSampleRate;
|
||||
static const rational kMaximumSampleRate;
|
||||
// Must be a power of 2
|
||||
static const rational kMinimumSampleRate;
|
||||
static const rational kMaximumSampleRate;
|
||||
|
||||
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 double& 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;
|
||||
|
||||
std::map<rational, Sample>::const_iterator GetMipmapForScale(double scale) const;
|
||||
std::map<rational, Sample>::const_iterator
|
||||
GetMipmapForScale(double scale) const;
|
||||
|
||||
void ValidateVirtualStart(const rational &new_start);
|
||||
void ValidateVirtualStart(const rational &new_start);
|
||||
|
||||
rational virtual_start_;
|
||||
rational virtual_start_;
|
||||
|
||||
int channels_;
|
||||
int channels_;
|
||||
|
||||
std::map<rational, Sample> mipmapped_data_;
|
||||
|
||||
rational length_;
|
||||
std::map<rational, Sample> mipmapped_data_;
|
||||
|
||||
rational length_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
|
||||
#include "cliexportmanager.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
CLIExportManager::CLIExportManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
#include "task/export/export.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CLIExportManager : public QObject
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CLIExportManager : public QObject {
|
||||
public:
|
||||
CLIExportManager();
|
||||
CLIExportManager();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -22,85 +22,87 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace olive {
|
||||
|
||||
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) :
|
||||
QObject(parent),
|
||||
title_(title),
|
||||
progress_(-1),
|
||||
drawn_(false)
|
||||
namespace olive
|
||||
{
|
||||
SetProgress(0);
|
||||
|
||||
CLIProgressDialog::CLIProgressDialog(const QString &title, QObject *parent)
|
||||
: QObject(parent)
|
||||
, title_(title)
|
||||
, progress_(-1)
|
||||
, drawn_(false)
|
||||
{
|
||||
SetProgress(0);
|
||||
}
|
||||
|
||||
void CLIProgressDialog::Update()
|
||||
{
|
||||
if (drawn_) {
|
||||
// We've been here before, do a carriage return back to the start of the terminal line
|
||||
std::cout << "\r";
|
||||
} else {
|
||||
drawn_ = true;
|
||||
}
|
||||
if (drawn_) {
|
||||
// We've been here before, do a carriage return back to the start of the terminal line
|
||||
std::cout << "\r";
|
||||
} else {
|
||||
drawn_ = true;
|
||||
}
|
||||
|
||||
// FIXME: Get real column count
|
||||
int columns = 80;
|
||||
// FIXME: Get real column count
|
||||
int columns = 80;
|
||||
|
||||
int title_columns = columns / 2 - 1;
|
||||
int title_columns = columns / 2 - 1;
|
||||
|
||||
// Print "title" text
|
||||
QString sized_title = title_;
|
||||
// Print "title" text
|
||||
QString sized_title = title_;
|
||||
|
||||
if (title_.size() > title_columns) {
|
||||
sized_title = title_.left(title_columns - 3).append(QStringLiteral("..."));
|
||||
} else {
|
||||
sized_title = title_;
|
||||
}
|
||||
if (title_.size() > title_columns) {
|
||||
sized_title =
|
||||
title_.left(title_columns - 3).append(QStringLiteral("..."));
|
||||
} else {
|
||||
sized_title = title_;
|
||||
}
|
||||
|
||||
std::cout << sized_title.toUtf8().constData();
|
||||
std::cout << sized_title.toUtf8().constData();
|
||||
|
||||
// Pad out the rest of the title area if necessary
|
||||
for (int i=sized_title.size(); i<title_columns; i++) {
|
||||
std::cout << " ";
|
||||
}
|
||||
// Pad out the rest of the title area if necessary
|
||||
for (int i = sized_title.size(); i < title_columns; i++) {
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
// Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters
|
||||
int progress_bar_columns = columns / 2 - 7;
|
||||
// Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters
|
||||
int progress_bar_columns = columns / 2 - 7;
|
||||
|
||||
std::cout << "[";
|
||||
std::cout << "[";
|
||||
|
||||
// Get UI bar progress
|
||||
int bar_prog = qRound(progress_ * progress_bar_columns);
|
||||
// Get UI bar progress
|
||||
int bar_prog = qRound(progress_ * progress_bar_columns);
|
||||
|
||||
// Draw filled in bar
|
||||
for (int i=0;i<bar_prog;i++) {
|
||||
std::cout << "=";
|
||||
}
|
||||
// Draw filled in bar
|
||||
for (int i = 0; i < bar_prog; i++) {
|
||||
std::cout << "=";
|
||||
}
|
||||
|
||||
// Draw empty space
|
||||
for (int i=bar_prog;i<progress_bar_columns;i++) {
|
||||
std::cout << " ";
|
||||
}
|
||||
// Draw empty space
|
||||
for (int i = bar_prog; i < progress_bar_columns; i++) {
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
std::cout << "] ";
|
||||
std::cout << "] ";
|
||||
|
||||
if (progress_ < 100) {
|
||||
std::cout << " ";
|
||||
}
|
||||
if (progress_ < 100) {
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
if (progress_ < 10) {
|
||||
std::cout << " ";
|
||||
}
|
||||
if (progress_ < 10) {
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
|
||||
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
|
||||
}
|
||||
|
||||
void CLIProgressDialog::SetProgress(double p)
|
||||
{
|
||||
if (progress_ != p) {
|
||||
progress_ = p;
|
||||
if (progress_ != p) {
|
||||
progress_ = p;
|
||||
|
||||
Update();
|
||||
}
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,25 +26,24 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CLIProgressDialog : public QObject
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CLIProgressDialog : public QObject {
|
||||
public:
|
||||
CLIProgressDialog(const QString &title, QObject* parent = nullptr);
|
||||
CLIProgressDialog(const QString &title, QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void SetProgress(double p);
|
||||
void SetProgress(double p);
|
||||
|
||||
private:
|
||||
void Update();
|
||||
void Update();
|
||||
|
||||
QString title_;
|
||||
QString title_;
|
||||
|
||||
double progress_;
|
||||
|
||||
bool drawn_;
|
||||
double progress_;
|
||||
|
||||
bool drawn_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,18 +20,19 @@
|
||||
|
||||
#include "clitaskdialog.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) :
|
||||
CLIProgressDialog(task->GetTitle(), parent),
|
||||
task_(task)
|
||||
namespace olive
|
||||
{
|
||||
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
|
||||
|
||||
CLITaskDialog::CLITaskDialog(Task *task, QObject *parent)
|
||||
: CLIProgressDialog(task->GetTitle(), parent)
|
||||
, task_(task)
|
||||
{
|
||||
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
|
||||
}
|
||||
|
||||
bool CLITaskDialog::Run()
|
||||
{
|
||||
return task_->Start();
|
||||
return task_->Start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,19 +24,18 @@
|
||||
#include "cli/cliprogress/cliprogressdialog.h"
|
||||
#include "task/task.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CLITaskDialog : public CLIProgressDialog
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CLITaskDialog(Task *task, QObject* parent = nullptr);
|
||||
|
||||
bool Run();
|
||||
class CLITaskDialog : public CLIProgressDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CLITaskDialog(Task *task, QObject *parent = nullptr);
|
||||
|
||||
bool Run();
|
||||
|
||||
private:
|
||||
Task* task_;
|
||||
|
||||
Task *task_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -4,123 +4,137 @@
|
||||
|
||||
#include "task/taskmanager.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
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
|
||||
QMutexLocker locker(&mutex_);
|
||||
// Mutex because we'll need to check the status of a conform task
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
// Return existing conform if exists
|
||||
QVector<QString> filenames = GetConformedFilename(cache_path, stream, params);
|
||||
if (AllConformsExist(filenames)) {
|
||||
return {kConformExists, filenames, nullptr};
|
||||
}
|
||||
// Return existing conform if exists
|
||||
QVector<QString> filenames =
|
||||
GetConformedFilename(cache_path, stream, params);
|
||||
if (AllConformsExist(filenames)) {
|
||||
return { kConformExists, filenames, nullptr };
|
||||
}
|
||||
|
||||
ConformTask *conforming_task = nullptr;
|
||||
ConformTask *conforming_task = nullptr;
|
||||
|
||||
foreach (const ConformData &data, conforming_) {
|
||||
if (data.stream == stream && data.params == params) {
|
||||
// Already creating conform in a task
|
||||
conforming_task = data.task;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (const ConformData &data, conforming_) {
|
||||
if (data.stream == stream && data.params == params) {
|
||||
// Already creating conform in a task
|
||||
conforming_task = data.task;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!conforming_task) {
|
||||
// Not conforming yet, create a task to do so
|
||||
if (!conforming_task) {
|
||||
// Not conforming yet, create a task to do so
|
||||
|
||||
// We conform to a different filename until it's done to make it clear even across sessions
|
||||
// whether this conform is ready or not
|
||||
QVector<QString> working_filenames = filenames;
|
||||
for (int i=0; i<working_filenames.size(); i++) {
|
||||
working_filenames[i].append(QStringLiteral(".working"));
|
||||
}
|
||||
// We conform to a different filename until it's done to make it clear even across sessions
|
||||
// whether this conform is ready or not
|
||||
QVector<QString> working_filenames = filenames;
|
||||
for (int i = 0; i < working_filenames.size(); i++) {
|
||||
working_filenames[i].append(QStringLiteral(".working"));
|
||||
}
|
||||
|
||||
conforming_task = new ConformTask(decoder_id, stream, params, working_filenames);
|
||||
connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished);
|
||||
conforming_task->moveToThread(TaskManager::instance()->thread());
|
||||
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task));
|
||||
conforming_task =
|
||||
new ConformTask(decoder_id, stream, params, working_filenames);
|
||||
connect(conforming_task, &ConformTask::Finished, this,
|
||||
&ConformManager::ConformTaskFinished);
|
||||
conforming_task->moveToThread(TaskManager::instance()->thread());
|
||||
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(Task *, conforming_task));
|
||||
|
||||
conforming_.append({stream, params, conforming_task, working_filenames, filenames});
|
||||
}
|
||||
conforming_.append(
|
||||
{ stream, params, conforming_task, working_filenames, filenames });
|
||||
}
|
||||
|
||||
if (wait) {
|
||||
do {
|
||||
conform_done_condition_.wait(&mutex_);
|
||||
} while (!AllConformsExist(filenames));
|
||||
return {kConformExists, filenames, nullptr};
|
||||
}
|
||||
if (wait) {
|
||||
do {
|
||||
conform_done_condition_.wait(&mutex_);
|
||||
} while (!AllConformsExist(filenames));
|
||||
return { kConformExists, filenames, nullptr };
|
||||
}
|
||||
|
||||
return {kConformGenerating, QVector<QString>(), conforming_task};
|
||||
return { kConformGenerating, QVector<QString>(), conforming_task };
|
||||
}
|
||||
|
||||
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms)
|
||||
QVector<QString>
|
||||
ConformManager::GetConformedFilename(const QString &cache_path,
|
||||
const Decoder::CodecStream &stream,
|
||||
const AudioParams ¶ms)
|
||||
{
|
||||
QVector<QString> filenames(params.channel_count());
|
||||
QVector<QString> filenames(params.channel_count());
|
||||
|
||||
for (int i=0; i<filenames.size(); i++) {
|
||||
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
|
||||
QString::number(stream.stream()),
|
||||
QString::number(params.sample_rate()),
|
||||
QString::number(params.format()),
|
||||
QString::number(params.channel_layout().u.mask),
|
||||
QString::number(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::number(stream.stream()),
|
||||
QString::number(params.sample_rate()),
|
||||
QString::number(params.format()),
|
||||
QString::number(params.channel_layout().u.mask),
|
||||
QString::number(i));
|
||||
|
||||
filenames[i] = QDir(cache_path).filePath(index_fn);
|
||||
}
|
||||
filenames[i] = QDir(cache_path).filePath(index_fn);
|
||||
}
|
||||
|
||||
return filenames;
|
||||
return filenames;
|
||||
}
|
||||
|
||||
bool ConformManager::AllConformsExist(const QVector<QString> &filenames)
|
||||
{
|
||||
foreach (const QString &fn, filenames) {
|
||||
if (!QFileInfo::exists(fn)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach (const QString &fn, filenames) {
|
||||
if (!QFileInfo::exists(fn)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
ConformData data;
|
||||
ConformData data;
|
||||
|
||||
// Remove conform data from list
|
||||
for (int i=0; i<conforming_.size(); i++) {
|
||||
const ConformData &c = conforming_.at(i);
|
||||
if (c.task == task) {
|
||||
data = c;
|
||||
conforming_.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Remove conform data from list
|
||||
for (int i = 0; i < conforming_.size(); i++) {
|
||||
const ConformData &c = conforming_.at(i);
|
||||
if (c.task == task) {
|
||||
data = c;
|
||||
conforming_.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (succeeded) {
|
||||
// Move file to standard conform name, making it clear this conform is ready for use
|
||||
for (int i=0; i<data.finished_filename.size(); i++) {
|
||||
const QString &finished = data.finished_filename.at(i);
|
||||
const QString &working = data.working_filename.at(i);
|
||||
if (succeeded) {
|
||||
// Move file to standard conform name, making it clear this conform is ready for use
|
||||
for (int i = 0; i < data.finished_filename.size(); i++) {
|
||||
const QString &finished = data.finished_filename.at(i);
|
||||
const QString &working = data.working_filename.at(i);
|
||||
|
||||
QFile::remove(finished);
|
||||
QFile::rename(working, finished);
|
||||
}
|
||||
QFile::remove(finished);
|
||||
QFile::rename(working, finished);
|
||||
}
|
||||
|
||||
conform_done_condition_.wakeAll();
|
||||
locker.unlock();
|
||||
emit ConformReady();
|
||||
} else {
|
||||
// Failed, just delete the working filename if exists
|
||||
for (int i=0; i<data.working_filename.size(); i++) {
|
||||
QFile::remove(data.working_filename.at(i));
|
||||
}
|
||||
}
|
||||
conform_done_condition_.wakeAll();
|
||||
locker.unlock();
|
||||
emit ConformReady();
|
||||
} else {
|
||||
// Failed, just delete the working filename if exists
|
||||
for (int i = 0; i < data.working_filename.size(); i++) {
|
||||
QFile::remove(data.working_filename.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+57
-55
@@ -7,82 +7,84 @@
|
||||
#include "decoder.h"
|
||||
#include "task/conform/conform.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ConformManager : public QObject
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class ConformManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static void CreateInstance()
|
||||
{
|
||||
if (!instance_) {
|
||||
instance_ = new ConformManager();
|
||||
}
|
||||
}
|
||||
static void CreateInstance()
|
||||
{
|
||||
if (!instance_) {
|
||||
instance_ = new ConformManager();
|
||||
}
|
||||
}
|
||||
|
||||
static void DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
static void DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
static ConformManager *instance()
|
||||
{
|
||||
return instance_;
|
||||
}
|
||||
static ConformManager *instance()
|
||||
{
|
||||
return instance_;
|
||||
}
|
||||
|
||||
enum ConformState {
|
||||
kConformExists,
|
||||
kConformGenerating
|
||||
};
|
||||
enum ConformState { kConformExists, kConformGenerating };
|
||||
|
||||
struct Conform {
|
||||
ConformState state;
|
||||
QVector<QString> filenames;
|
||||
ConformTask *task;
|
||||
};
|
||||
struct Conform {
|
||||
ConformState state;
|
||||
QVector<QString> filenames;
|
||||
ConformTask *task;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get conform state, and start conforming if no conform exists
|
||||
*
|
||||
* Thread-safe.
|
||||
*/
|
||||
Conform GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms, bool wait);
|
||||
/**
|
||||
* @brief Get conform state, and start conforming if no conform exists
|
||||
*
|
||||
* Thread-safe.
|
||||
*/
|
||||
Conform GetConformState(const QString &decoder_id,
|
||||
const QString &cache_path,
|
||||
const Decoder::CodecStream &stream,
|
||||
const AudioParams ¶ms, bool wait);
|
||||
|
||||
signals:
|
||||
void ConformReady();
|
||||
void ConformReady();
|
||||
|
||||
private:
|
||||
ConformManager() = default;
|
||||
ConformManager() = default;
|
||||
|
||||
static ConformManager *instance_;
|
||||
static ConformManager *instance_;
|
||||
|
||||
QMutex mutex_;
|
||||
QMutex mutex_;
|
||||
|
||||
QWaitCondition conform_done_condition_;
|
||||
QWaitCondition conform_done_condition_;
|
||||
|
||||
struct ConformData {
|
||||
Decoder::CodecStream stream;
|
||||
AudioParams params;
|
||||
ConformTask *task;
|
||||
QVector<QString> working_filename;
|
||||
QVector<QString> finished_filename;
|
||||
};
|
||||
struct ConformData {
|
||||
Decoder::CodecStream stream;
|
||||
AudioParams params;
|
||||
ConformTask *task;
|
||||
QVector<QString> working_filename;
|
||||
QVector<QString> finished_filename;
|
||||
};
|
||||
|
||||
QVector<ConformData> conforming_;
|
||||
QVector<ConformData> conforming_;
|
||||
|
||||
/**
|
||||
* @brief Get the destination filename of an audio stream conformed to a set of parameters
|
||||
*/
|
||||
static QVector<QString> GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms);
|
||||
/**
|
||||
* @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 bool AllConformsExist(const QVector<QString> &filenames);
|
||||
static bool AllConformsExist(const QVector<QString> &filenames);
|
||||
|
||||
private slots:
|
||||
void ConformTaskFinished(Task *task, bool succeeded);
|
||||
|
||||
void ConformTaskFinished(Task *task, bool succeeded);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace olive
|
||||
|
||||
#endif // CONFORMMANAGER_H
|
||||
|
||||
+215
-185
@@ -33,151 +33,162 @@
|
||||
#include "node/project.h"
|
||||
#include "task/taskmanager.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const rational Decoder::kAnyTimecode = RATIONAL_MIN;
|
||||
|
||||
Decoder::Decoder() :
|
||||
cached_texture_(nullptr)
|
||||
Decoder::Decoder()
|
||||
: cached_texture_(nullptr)
|
||||
{
|
||||
UpdateLastAccessed();
|
||||
UpdateLastAccessed();
|
||||
}
|
||||
|
||||
void Decoder::IncrementAccessTime(qint64 t)
|
||||
{
|
||||
last_accessed_ += t;
|
||||
last_accessed_ += t;
|
||||
}
|
||||
|
||||
bool Decoder::Open(const CodecStream &stream)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
UpdateLastAccessed();
|
||||
UpdateLastAccessed();
|
||||
|
||||
if (stream_.IsValid()) {
|
||||
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
|
||||
if (stream_ == stream) {
|
||||
return true;
|
||||
} else {
|
||||
qWarning() << "Tried to open a decoder that was already open with another stream";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Stream was not open, try opening it now
|
||||
if (!stream.IsValid()) {
|
||||
// Cannot open null stream
|
||||
qCritical() << "Decoder attempted to open null stream";
|
||||
return false;
|
||||
}
|
||||
if (stream_.IsValid()) {
|
||||
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
|
||||
if (stream_ == stream) {
|
||||
return true;
|
||||
} else {
|
||||
qWarning()
|
||||
<< "Tried to open a decoder that was already open with another stream";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Stream was not open, try opening it now
|
||||
if (!stream.IsValid()) {
|
||||
// Cannot open null stream
|
||||
qCritical() << "Decoder attempted to open null stream";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stream.Exists()) {
|
||||
// Cannot open file that doesn't exist
|
||||
qCritical() << "Decoder attempted to open file that doesn't exist";
|
||||
return false;
|
||||
}
|
||||
if (!stream.Exists()) {
|
||||
// Cannot open file that doesn't exist
|
||||
qCritical() << "Decoder attempted to open file that doesn't exist";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set stream
|
||||
stream_ = stream;
|
||||
// Set stream
|
||||
stream_ = stream;
|
||||
|
||||
// Try open internal
|
||||
if (OpenInternal()) {
|
||||
return true;
|
||||
} else {
|
||||
// Unset stream
|
||||
qCritical() << "Failed to open" << stream_.filename() << "stream" << stream_.stream();
|
||||
CloseInternal();
|
||||
stream_.Reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Try open internal
|
||||
if (OpenInternal()) {
|
||||
return true;
|
||||
} else {
|
||||
// Unset stream
|
||||
qCritical() << "Failed to open" << stream_.filename() << "stream"
|
||||
<< stream_.stream();
|
||||
CloseInternal();
|
||||
stream_.Reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
UpdateLastAccessed();
|
||||
UpdateLastAccessed();
|
||||
|
||||
if (!stream_.IsValid()) {
|
||||
qCritical() << "Can't retrieve video on a closed decoder";
|
||||
return nullptr;
|
||||
}
|
||||
if (!stream_.IsValid()) {
|
||||
qCritical() << "Can't retrieve video on a closed decoder";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!SupportsVideo()) {
|
||||
qCritical() << "Decoder doesn't support video";
|
||||
return nullptr;
|
||||
}
|
||||
if (!SupportsVideo()) {
|
||||
qCritical() << "Decoder doesn't support video";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (p.cancelled && p.cancelled->IsCancelled()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (p.cancelled && p.cancelled->IsCancelled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cached_texture_ && cached_time_ == p.time && cached_divider_ == p.divider) {
|
||||
return cached_texture_;
|
||||
}
|
||||
if (cached_texture_ && cached_time_ == p.time &&
|
||||
cached_divider_ == p.divider) {
|
||||
return cached_texture_;
|
||||
}
|
||||
|
||||
cached_texture_ = RetrieveVideoInternal(p);
|
||||
cached_time_ = p.time;
|
||||
cached_divider_ = p.divider;
|
||||
cached_texture_ = RetrieveVideoInternal(p);
|
||||
cached_time_ = p.time;
|
||||
cached_divider_ = p.divider;
|
||||
|
||||
return cached_texture_;
|
||||
return cached_texture_;
|
||||
}
|
||||
|
||||
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode)
|
||||
Decoder::RetrieveAudioStatus
|
||||
Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
|
||||
const AudioParams ¶ms, const QString &cache_path,
|
||||
LoopMode loop_mode, RenderMode::Mode mode)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
UpdateLastAccessed();
|
||||
UpdateLastAccessed();
|
||||
|
||||
if (!stream_.IsValid()) {
|
||||
qCritical() << "Can't retrieve audio on a closed decoder";
|
||||
return kInvalid;
|
||||
}
|
||||
if (!stream_.IsValid()) {
|
||||
qCritical() << "Can't retrieve audio on a closed decoder";
|
||||
return kInvalid;
|
||||
}
|
||||
|
||||
if (!SupportsAudio()) {
|
||||
qCritical() << "Decoder doesn't support audio";
|
||||
return kInvalid;
|
||||
}
|
||||
if (!SupportsAudio()) {
|
||||
qCritical() << "Decoder doesn't support audio";
|
||||
return kInvalid;
|
||||
}
|
||||
|
||||
// Get conform state from ConformManager
|
||||
ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
|
||||
if (conform.state == ConformManager::kConformGenerating) {
|
||||
// If we need the task, it's available in `conform.task`
|
||||
return kWaitingForConform;
|
||||
}
|
||||
// Get conform state from ConformManager
|
||||
ConformManager::Conform conform =
|
||||
ConformManager::instance()->GetConformState(
|
||||
id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
|
||||
if (conform.state == ConformManager::kConformGenerating) {
|
||||
// If we need the task, it's available in `conform.task`
|
||||
return kWaitingForConform;
|
||||
}
|
||||
|
||||
// See if we got the conform
|
||||
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) {
|
||||
return kOK;
|
||||
} else {
|
||||
return kUnknownError;
|
||||
}
|
||||
// See if we got the conform
|
||||
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode,
|
||||
params)) {
|
||||
return kOK;
|
||||
} else {
|
||||
return kUnknownError;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 Decoder::GetLastAccessedTime()
|
||||
{
|
||||
return last_accessed_;
|
||||
return last_accessed_;
|
||||
}
|
||||
|
||||
void Decoder::Close()
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
UpdateLastAccessed();
|
||||
UpdateLastAccessed();
|
||||
|
||||
cached_texture_ = nullptr;
|
||||
cached_texture_ = nullptr;
|
||||
|
||||
if (stream_.IsValid()) {
|
||||
CloseInternal();
|
||||
stream_.Reset();
|
||||
} else {
|
||||
qWarning() << "Tried to close a decoder that wasn't open";
|
||||
}
|
||||
if (stream_.IsValid()) {
|
||||
CloseInternal();
|
||||
stream_.Reset();
|
||||
} else {
|
||||
qWarning() << "Tried to close a decoder that wasn't open";
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams ¶ms, CancelAtom *cancelled)
|
||||
bool Decoder::ConformAudio(const QVector<QString> &output_filenames,
|
||||
const AudioParams ¶ms, CancelAtom *cancelled)
|
||||
{
|
||||
return ConformAudioInternal(output_filenames, params, cancelled);
|
||||
return ConformAudioInternal(output_filenames, params, cancelled);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -186,159 +197,178 @@ bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const Audio
|
||||
|
||||
QVector<DecoderPtr> Decoder::ReceiveListOfAllDecoders()
|
||||
{
|
||||
QVector<DecoderPtr> decoders;
|
||||
QVector<DecoderPtr> decoders;
|
||||
|
||||
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
|
||||
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
|
||||
decoders.append(std::make_shared<OIIODecoder>());
|
||||
decoders.append(std::make_shared<FFmpegDecoder>());
|
||||
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
|
||||
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
|
||||
decoders.append(std::make_shared<OIIODecoder>());
|
||||
decoders.append(std::make_shared<FFmpegDecoder>());
|
||||
|
||||
return decoders;
|
||||
return decoders;
|
||||
}
|
||||
|
||||
DecoderPtr Decoder::CreateFromID(const QString &id)
|
||||
{
|
||||
if (id.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (id.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create list to iterate through
|
||||
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
|
||||
// Create list to iterate through
|
||||
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
|
||||
|
||||
foreach (DecoderPtr d, decoder_list) {
|
||||
if (d->id() == id) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
foreach (DecoderPtr d, decoder_list) {
|
||||
if (d->id() == id) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
|
||||
{
|
||||
if (duration != AV_NOPTS_VALUE && duration != 0) {
|
||||
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(duration));
|
||||
}
|
||||
if (duration != AV_NOPTS_VALUE && duration != 0) {
|
||||
emit IndexProgress(static_cast<double>(ts) /
|
||||
static_cast<double>(duration));
|
||||
}
|
||||
}
|
||||
|
||||
QString Decoder::TransformImageSequenceFileName(const QString &filename, const int64_t& number)
|
||||
QString Decoder::TransformImageSequenceFileName(const QString &filename,
|
||||
const int64_t &number)
|
||||
{
|
||||
int digit_count = GetImageSequenceDigitCount(filename);
|
||||
int digit_count = GetImageSequenceDigitCount(filename);
|
||||
|
||||
QFileInfo file_info(filename);
|
||||
QFileInfo file_info(filename);
|
||||
|
||||
QString original_basename = file_info.completeBaseName();
|
||||
QString original_basename = file_info.completeBaseName();
|
||||
|
||||
QString new_basename = original_basename.left(original_basename.size() - digit_count)
|
||||
.append(QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
|
||||
QString new_basename =
|
||||
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)
|
||||
{
|
||||
QString basename = QFileInfo(filename).completeBaseName();
|
||||
QString basename = QFileInfo(filename).completeBaseName();
|
||||
|
||||
// See if basename contains a number at the end
|
||||
int digit_count = 0;
|
||||
// See if basename contains a number at the end
|
||||
int digit_count = 0;
|
||||
|
||||
for (int i=basename.size()-1;i>=0;i--) {
|
||||
if (basename.at(i).isDigit()) {
|
||||
digit_count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = basename.size() - 1; i >= 0; i--) {
|
||||
if (basename.at(i).isDigit()) {
|
||||
digit_count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return digit_count;
|
||||
return digit_count;
|
||||
}
|
||||
|
||||
int64_t Decoder::GetImageSequenceIndex(const QString &filename)
|
||||
{
|
||||
int digit_count = GetImageSequenceDigitCount(filename);
|
||||
int digit_count = GetImageSequenceDigitCount(filename);
|
||||
|
||||
QFileInfo file_info(filename);
|
||||
QFileInfo file_info(filename);
|
||||
|
||||
QString original_basename = file_info.completeBaseName();
|
||||
QString original_basename = file_info.completeBaseName();
|
||||
|
||||
QString number_only = original_basename.mid(original_basename.size() - digit_count);
|
||||
QString number_only =
|
||||
original_basename.mid(original_basename.size() - digit_count);
|
||||
|
||||
return number_only.toLongLong();
|
||||
return number_only.toLongLong();
|
||||
}
|
||||
|
||||
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||
{
|
||||
Q_UNUSED(p)
|
||||
return nullptr;
|
||||
Q_UNUSED(p)
|
||||
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(cancelled)
|
||||
Q_UNUSED(params)
|
||||
return false;
|
||||
Q_UNUSED(filenames)
|
||||
Q_UNUSED(cancelled)
|
||||
Q_UNUSED(params)
|
||||
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;
|
||||
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
||||
// Offset range by audio start offset
|
||||
range -= GetAudioStartOffset();
|
||||
PlanarFileDevice input;
|
||||
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
||||
// Offset range by audio start offset
|
||||
range -= GetAudioStartOffset();
|
||||
|
||||
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
|
||||
qint64 write_index = 0;
|
||||
qint64 read_index = input_params.time_to_bytes(range.in()) /
|
||||
input_params.channel_count();
|
||||
qint64 write_index = 0;
|
||||
|
||||
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel();
|
||||
const qint64 buffer_length_in_bytes =
|
||||
sample_buffer.sample_count() *
|
||||
input_params.bytes_per_sample_per_channel();
|
||||
|
||||
while (write_index < buffer_length_in_bytes) {
|
||||
if (loop_mode == LoopMode::kLoopModeLoop) {
|
||||
while (read_index >= input.size()) {
|
||||
read_index -= input.size();
|
||||
}
|
||||
while (write_index < buffer_length_in_bytes) {
|
||||
if (loop_mode == LoopMode::kLoopModeLoop) {
|
||||
while (read_index >= input.size()) {
|
||||
read_index -= input.size();
|
||||
}
|
||||
|
||||
while (read_index < 0) {
|
||||
read_index += input.size();
|
||||
}
|
||||
}
|
||||
while (read_index < 0) {
|
||||
read_index += input.size();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 write_count = 0;
|
||||
qint64 write_count = 0;
|
||||
|
||||
if (read_index < 0) {
|
||||
// Reading before 0, write silence here until audio data would actually start
|
||||
write_count = qMin(-read_index, buffer_length_in_bytes);
|
||||
sample_buffer.silence_bytes(write_index, write_index + write_count);
|
||||
} else if (read_index >= input.size()) {
|
||||
// Reading after data length, write silence until the end of the buffer
|
||||
write_count = buffer_length_in_bytes - write_index;
|
||||
sample_buffer.silence_bytes(write_index, write_index + write_count);
|
||||
} else {
|
||||
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
|
||||
input.seek(read_index);
|
||||
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index);
|
||||
}
|
||||
if (read_index < 0) {
|
||||
// Reading before 0, write silence here until audio data would actually start
|
||||
write_count = qMin(-read_index, buffer_length_in_bytes);
|
||||
sample_buffer.silence_bytes(write_index,
|
||||
write_index + write_count);
|
||||
} else if (read_index >= input.size()) {
|
||||
// Reading after data length, write silence until the end of the buffer
|
||||
write_count = buffer_length_in_bytes - write_index;
|
||||
sample_buffer.silence_bytes(write_index,
|
||||
write_index + write_count);
|
||||
} else {
|
||||
write_count = qMin(input.size() - read_index,
|
||||
buffer_length_in_bytes - write_index);
|
||||
input.seek(read_index);
|
||||
input.read(reinterpret_cast<char **>(
|
||||
sample_buffer.to_raw_ptrs().data()),
|
||||
write_count, write_index);
|
||||
}
|
||||
|
||||
read_index += write_count;
|
||||
write_index += write_count;
|
||||
}
|
||||
read_index += write_count;
|
||||
write_index += write_count;
|
||||
}
|
||||
|
||||
input.close();
|
||||
input.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Decoder::UpdateLastAccessed()
|
||||
{
|
||||
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
uint qHash(Decoder::CodecStream stream, uint seed)
|
||||
{
|
||||
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^ qHash(stream.block(), seed);
|
||||
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^
|
||||
qHash(stream.block(), seed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+143
-126
@@ -36,12 +36,17 @@ extern "C" {
|
||||
#include "render/cancelatom.h"
|
||||
#include "render/rendermodes.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class 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
|
||||
@@ -59,89 +64,88 @@ using DecoderPtr = std::shared_ptr<Decoder>;
|
||||
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
|
||||
* to be utilized in the rest of the rendering pipeline.
|
||||
*/
|
||||
class Decoder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class Decoder : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum RetrieveState {
|
||||
kReady,
|
||||
kFailedToOpen,
|
||||
kIndexUnavailable
|
||||
};
|
||||
enum RetrieveState { kReady, kFailedToOpen, kIndexUnavailable };
|
||||
|
||||
Decoder();
|
||||
Decoder();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Unique decoder ID
|
||||
*/
|
||||
virtual QString id() const = 0;
|
||||
virtual QString id() const = 0;
|
||||
|
||||
virtual bool SupportsVideo(){return false;}
|
||||
virtual bool SupportsAudio(){return false;}
|
||||
virtual bool SupportsVideo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool SupportsAudio()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void IncrementAccessTime(qint64 t);
|
||||
void IncrementAccessTime(qint64 t);
|
||||
|
||||
class CodecStream
|
||||
{
|
||||
public:
|
||||
CodecStream() :
|
||||
stream_(-1),
|
||||
block_(nullptr)
|
||||
{
|
||||
}
|
||||
class CodecStream {
|
||||
public:
|
||||
CodecStream()
|
||||
: stream_(-1)
|
||||
, block_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CodecStream(const QString& filename, int stream, Block *block) :
|
||||
filename_(filename),
|
||||
stream_(stream),
|
||||
block_(block)
|
||||
{
|
||||
}
|
||||
CodecStream(const QString &filename, int stream, Block *block)
|
||||
: filename_(filename)
|
||||
, stream_(stream)
|
||||
, block_(block)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return !filename_.isEmpty() && stream_ >= 0;
|
||||
}
|
||||
bool IsValid() const
|
||||
{
|
||||
return !filename_.isEmpty() && stream_ >= 0;
|
||||
}
|
||||
|
||||
bool Exists() const
|
||||
{
|
||||
return QFileInfo::exists(filename_);
|
||||
}
|
||||
bool Exists() const
|
||||
{
|
||||
return QFileInfo::exists(filename_);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
*this = CodecStream();
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
*this = CodecStream();
|
||||
}
|
||||
|
||||
bool operator==(const CodecStream& rhs) const
|
||||
{
|
||||
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
|
||||
}
|
||||
bool operator==(const CodecStream &rhs) const
|
||||
{
|
||||
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
|
||||
}
|
||||
|
||||
const QString& filename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
const QString &filename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
|
||||
int stream() const
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
int stream() const
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
|
||||
Block *block() const
|
||||
{
|
||||
return block_;
|
||||
}
|
||||
Block *block() const
|
||||
{
|
||||
return block_;
|
||||
}
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
int stream_;
|
||||
int stream_;
|
||||
|
||||
Block *block_;
|
||||
Block *block_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open stream for decoding
|
||||
*
|
||||
* This function is thread safe.
|
||||
@@ -150,22 +154,21 @@ public:
|
||||
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
|
||||
* be opened OR if already open and the stream is NOT the same.
|
||||
*/
|
||||
bool Open(const CodecStream& stream);
|
||||
bool Open(const CodecStream &stream);
|
||||
|
||||
static const rational kAnyTimecode;
|
||||
static const rational kAnyTimecode;
|
||||
|
||||
struct RetrieveVideoParams
|
||||
{
|
||||
Renderer *renderer = nullptr;
|
||||
rational time;
|
||||
int divider = 1;
|
||||
PixelFormat maximum_format = PixelFormat::INVALID;
|
||||
CancelAtom *cancelled = nullptr;
|
||||
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
|
||||
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
|
||||
};
|
||||
struct RetrieveVideoParams {
|
||||
Renderer *renderer = nullptr;
|
||||
rational time;
|
||||
int divider = 1;
|
||||
PixelFormat maximum_format = PixelFormat::INVALID;
|
||||
CancelAtom *cancelled = nullptr;
|
||||
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
|
||||
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves a video frame from footage
|
||||
*
|
||||
* This function will always return a valid frame unless a fatal error occurs (in such case,
|
||||
@@ -175,16 +178,16 @@ public:
|
||||
*
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
TexturePtr RetrieveVideo(const RetrieveVideoParams& p);
|
||||
TexturePtr RetrieveVideo(const RetrieveVideoParams &p);
|
||||
|
||||
enum RetrieveAudioStatus {
|
||||
kInvalid = -1,
|
||||
kOK,
|
||||
kWaitingForConform,
|
||||
kUnknownError
|
||||
};
|
||||
enum RetrieveAudioStatus {
|
||||
kInvalid = -1,
|
||||
kOK,
|
||||
kWaitingForConform,
|
||||
kUnknownError
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieve audio data from footage
|
||||
*
|
||||
* This function will always return a sample buffer unless a fatal error occurs (in such case,
|
||||
@@ -192,14 +195,17 @@ public:
|
||||
*
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
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
|
||||
*/
|
||||
qint64 GetLastAccessedTime();
|
||||
qint64 GetLastAccessedTime();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Generate a Footage object from a file
|
||||
*
|
||||
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
|
||||
@@ -210,39 +216,43 @@ public:
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
void Close();
|
||||
void Close();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A Decoder instance or nullptr if a Decoder with this ID does not exist
|
||||
*/
|
||||
static DecoderPtr CreateFromID(const QString& id);
|
||||
static DecoderPtr CreateFromID(const QString &id);
|
||||
|
||||
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number);
|
||||
static QString TransformImageSequenceFileName(const QString &filename,
|
||||
const int64_t &number);
|
||||
|
||||
static int GetImageSequenceDigitCount(const QString& filename);
|
||||
static int GetImageSequenceDigitCount(const QString &filename);
|
||||
|
||||
static int64_t GetImageSequenceIndex(const QString& filename);
|
||||
static int64_t GetImageSequenceIndex(const QString &filename);
|
||||
|
||||
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
|
||||
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
|
||||
|
||||
protected:
|
||||
/**
|
||||
/**
|
||||
* @brief Internal open function
|
||||
*
|
||||
* Sub-classes must override this function. Function will already be mutexed, so there is no need
|
||||
@@ -254,62 +264,69 @@ protected:
|
||||
* return FALSE. If this function returns false, Decoder will call CloseInternal to clean any
|
||||
* memory allocated during OpenInternal.
|
||||
*/
|
||||
virtual bool OpenInternal() = 0;
|
||||
virtual bool OpenInternal() = 0;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal close function
|
||||
*
|
||||
* Sub-classes must override this function. Function should be able to safely clear all allocated
|
||||
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
|
||||
*/
|
||||
virtual void CloseInternal() = 0;
|
||||
virtual void CloseInternal() = 0;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal frame retrieval function
|
||||
*
|
||||
* Sub-classes must override this function IF they support video. Function is already mutexed
|
||||
* so sub-classes don't need to worry about thread safety.
|
||||
*/
|
||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p);
|
||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p);
|
||||
|
||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, CancelAtom *cancelled);
|
||||
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
|
||||
const AudioParams ¶ms,
|
||||
CancelAtom *cancelled);
|
||||
|
||||
void SignalProcessingProgress(int64_t ts, int64_t duration);
|
||||
void SignalProcessingProgress(int64_t ts, int64_t duration);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Return currently open stream
|
||||
*
|
||||
* This function is NOT thread safe and should therefore only be called by thread safe functions.
|
||||
*/
|
||||
const CodecStream& stream() const
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
const CodecStream &stream() const
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
|
||||
virtual rational GetAudioStartOffset() const { return 0; }
|
||||
virtual rational GetAudioStartOffset() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
signals:
|
||||
/**
|
||||
/**
|
||||
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
|
||||
* available
|
||||
*/
|
||||
void IndexProgress(double);
|
||||
void IndexProgress(double);
|
||||
|
||||
private:
|
||||
void UpdateLastAccessed();
|
||||
void UpdateLastAccessed();
|
||||
|
||||
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams ¶ms);
|
||||
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer,
|
||||
const QVector<QString> &conform_filenames,
|
||||
TimeRange range, LoopMode loop_mode,
|
||||
const AudioParams ¶ms);
|
||||
|
||||
CodecStream stream_;
|
||||
CodecStream stream_;
|
||||
|
||||
QMutex mutex_;
|
||||
QMutex mutex_;
|
||||
|
||||
std::atomic_int64_t last_accessed_;
|
||||
|
||||
TexturePtr cached_texture_;
|
||||
rational cached_time_;
|
||||
int cached_divider_;
|
||||
std::atomic_int64_t last_accessed_;
|
||||
|
||||
TexturePtr cached_texture_;
|
||||
rational cached_time_;
|
||||
int cached_divider_;
|
||||
};
|
||||
|
||||
uint qHash(Decoder::CodecStream stream, uint seed = 0);
|
||||
|
||||
+415
-339
@@ -26,492 +26,568 @@
|
||||
#include "ffmpeg/ffmpegencoder.h"
|
||||
#include "oiio/oiioencoder.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QRegularExpression Encoder::kImageSequenceContainsDigits = QRegularExpression(QStringLiteral("\\[[#]+\\]"));
|
||||
const QRegularExpression Encoder::kImageSequenceRemoveDigits = QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
|
||||
const QRegularExpression Encoder::kImageSequenceContainsDigits =
|
||||
QRegularExpression(QStringLiteral("\\[[#]+\\]"));
|
||||
const QRegularExpression Encoder::kImageSequenceRemoveDigits =
|
||||
QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
|
||||
|
||||
Encoder::Encoder(const EncodingParams ¶ms) :
|
||||
params_(params)
|
||||
Encoder::Encoder(const EncodingParams ¶ms)
|
||||
: params_(params)
|
||||
{
|
||||
}
|
||||
|
||||
const EncodingParams &Encoder::params() const
|
||||
{
|
||||
return params_;
|
||||
return params_;
|
||||
}
|
||||
|
||||
QString Encoder::GetFilenameForFrame(const rational &frame)
|
||||
{
|
||||
if (params().video_is_image_sequence()) {
|
||||
// Transform!
|
||||
int64_t frame_index = Timecode::time_to_timestamp(frame, params().video_params().frame_rate_as_time_base());
|
||||
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
|
||||
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
|
||||
if (params().video_is_image_sequence()) {
|
||||
// Transform!
|
||||
int64_t frame_index = Timecode::time_to_timestamp(
|
||||
frame, params().video_params().frame_rate_as_time_base());
|
||||
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
|
||||
QString frame_index_str =
|
||||
QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
|
||||
|
||||
QString f = params_.filename();
|
||||
f.replace(kImageSequenceContainsDigits, frame_index_str);
|
||||
return f;
|
||||
} else {
|
||||
// Keep filename
|
||||
return params_.filename();
|
||||
}
|
||||
QString f = params_.filename();
|
||||
f.replace(kImageSequenceContainsDigits, frame_index_str);
|
||||
return f;
|
||||
} else {
|
||||
// Keep filename
|
||||
return params_.filename();
|
||||
}
|
||||
}
|
||||
|
||||
int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
|
||||
{
|
||||
int start = filename.indexOf(kImageSequenceContainsDigits);
|
||||
int digit_count = 0;
|
||||
for (int i=start+1; i<filename.size(); i++) {
|
||||
if (filename.at(i) == '#') {
|
||||
digit_count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return digit_count;
|
||||
int start = filename.indexOf(kImageSequenceContainsDigits);
|
||||
int digit_count = 0;
|
||||
for (int i = start + 1; i < filename.size(); i++) {
|
||||
if (filename.at(i) == '#') {
|
||||
digit_count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return digit_count;
|
||||
}
|
||||
|
||||
bool Encoder::FilenameContainsDigitPlaceholder(const QString& filename)
|
||||
bool Encoder::FilenameContainsDigitPlaceholder(const QString &filename)
|
||||
{
|
||||
return filename.contains(kImageSequenceContainsDigits);
|
||||
return filename.contains(kImageSequenceContainsDigits);
|
||||
}
|
||||
|
||||
QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
|
||||
{
|
||||
return filename.remove(kImageSequenceRemoveDigits);
|
||||
return filename.remove(kImageSequenceRemoveDigits);
|
||||
}
|
||||
|
||||
EncodingParams::EncodingParams() :
|
||||
video_enabled_(false),
|
||||
video_bit_rate_(0),
|
||||
video_min_bit_rate_(0),
|
||||
video_max_bit_rate_(0),
|
||||
video_buffer_size_(0),
|
||||
video_threads_(0),
|
||||
video_is_image_sequence_(false),
|
||||
audio_enabled_(false),
|
||||
audio_bit_rate_(0),
|
||||
subtitles_enabled_(false),
|
||||
subtitles_are_sidecar_(false),
|
||||
video_scaling_method_(kStretch),
|
||||
has_custom_range_(false)
|
||||
EncodingParams::EncodingParams()
|
||||
: video_enabled_(false)
|
||||
, video_bit_rate_(0)
|
||||
, video_min_bit_rate_(0)
|
||||
, video_max_bit_rate_(0)
|
||||
, video_buffer_size_(0)
|
||||
, video_threads_(0)
|
||||
, video_is_image_sequence_(false)
|
||||
, audio_enabled_(false)
|
||||
, audio_bit_rate_(0)
|
||||
, subtitles_enabled_(false)
|
||||
, subtitles_are_sidecar_(false)
|
||||
, video_scaling_method_(kStretch)
|
||||
, has_custom_range_(false)
|
||||
{
|
||||
}
|
||||
|
||||
QDir EncodingParams::GetPresetPath()
|
||||
{
|
||||
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets"));
|
||||
return QDir(FileFunctions::GetConfigurationLocation())
|
||||
.filePath(QStringLiteral("exportpresets"));
|
||||
}
|
||||
|
||||
QStringList EncodingParams::GetListOfPresets()
|
||||
{
|
||||
QDir d = EncodingParams::GetPresetPath();
|
||||
return d.entryList(QDir::Files);
|
||||
QDir d = EncodingParams::GetPresetPath();
|
||||
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_params_ = video_params;
|
||||
video_codec_ = vcodec;
|
||||
video_enabled_ = true;
|
||||
video_params_ = video_params;
|
||||
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_params_ = audio_params;
|
||||
audio_codec_ = acodec;
|
||||
audio_enabled_ = true;
|
||||
audio_params_ = audio_params;
|
||||
audio_codec_ = acodec;
|
||||
}
|
||||
|
||||
void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
|
||||
{
|
||||
subtitles_enabled_ = true;
|
||||
subtitles_codec_ = scodec;
|
||||
subtitles_enabled_ = true;
|
||||
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_are_sidecar_ = true;
|
||||
subtitle_sidecar_fmt_ = sfmt;
|
||||
subtitles_codec_ = scodec;
|
||||
subtitles_enabled_ = true;
|
||||
subtitles_are_sidecar_ = true;
|
||||
subtitle_sidecar_fmt_ = sfmt;
|
||||
subtitles_codec_ = scodec;
|
||||
}
|
||||
|
||||
void EncodingParams::DisableVideo()
|
||||
{
|
||||
video_enabled_ = false;
|
||||
video_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::DisableAudio()
|
||||
{
|
||||
audio_enabled_ = false;
|
||||
audio_enabled_ = false;
|
||||
}
|
||||
|
||||
void EncodingParams::DisableSubtitles()
|
||||
{
|
||||
subtitles_enabled_ = false;
|
||||
subtitles_enabled_ = false;
|
||||
}
|
||||
|
||||
bool EncodingParams::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("export")) {
|
||||
int version = 0;
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("export")) {
|
||||
int version = 0;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("version")) {
|
||||
version = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
XMLAttributeLoop(reader, attr)
|
||||
{
|
||||
if (attr.name() == QStringLiteral("version")) {
|
||||
version = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
return LoadV1(reader);
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
switch (version) {
|
||||
case 1:
|
||||
return LoadV1(reader);
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EncodingParams::Load(QIODevice *device)
|
||||
{
|
||||
QXmlStreamReader reader(device);
|
||||
return Load(&reader);
|
||||
QXmlStreamReader reader(device);
|
||||
return Load(&reader);
|
||||
}
|
||||
|
||||
void EncodingParams::Save(QIODevice *device) const
|
||||
{
|
||||
QXmlStreamWriter writer(device);
|
||||
Save(&writer);
|
||||
QXmlStreamWriter writer(device);
|
||||
Save(&writer);
|
||||
}
|
||||
|
||||
void EncodingParams::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartDocument();
|
||||
writer->writeStartDocument();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("export"));
|
||||
writer->writeStartElement(QStringLiteral("export"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion));
|
||||
writer->writeAttribute(QStringLiteral("version"),
|
||||
QString::number(kEncoderParamsVersion));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("filename"), filename_);
|
||||
writer->writeTextElement(QStringLiteral("format"), QString::number(format_));
|
||||
writer->writeTextElement(QStringLiteral("filename"), filename_);
|
||||
writer->writeTextElement(QStringLiteral("format"),
|
||||
QString::number(format_));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_));
|
||||
writer->writeTextElement(QStringLiteral("customrangein"), QString::fromStdString(custom_range_.in().toString()));
|
||||
writer->writeTextElement(QStringLiteral("customrangeout"), QString::fromStdString(custom_range_.out().toString()));
|
||||
writer->writeTextElement(QStringLiteral("range"),
|
||||
QString::number(has_custom_range_));
|
||||
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_) {
|
||||
writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_));
|
||||
writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width()));
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height()));
|
||||
writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format()));
|
||||
writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(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("imgseq"), QString::number(video_is_image_sequence_));
|
||||
if (video_enabled_) {
|
||||
writer->writeTextElement(QStringLiteral("codec"),
|
||||
QString::number(video_codec_));
|
||||
writer->writeTextElement(QStringLiteral("width"),
|
||||
QString::number(video_params_.width()));
|
||||
writer->writeTextElement(QStringLiteral("height"),
|
||||
QString::number(video_params_.height()));
|
||||
writer->writeTextElement(QStringLiteral("format"),
|
||||
QString::number(video_params_.format()));
|
||||
writer->writeTextElement(
|
||||
QStringLiteral("pixelaspect"),
|
||||
QString::fromStdString(
|
||||
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("imgseq"),
|
||||
QString::number(video_is_image_sequence_));
|
||||
|
||||
writer->writeStartElement(QStringLiteral("color"));
|
||||
writer->writeTextElement(QStringLiteral("output"), color_transform_.output());
|
||||
writer->writeEndElement(); // colortransform
|
||||
writer->writeStartElement(QStringLiteral("color"));
|
||||
writer->writeTextElement(QStringLiteral("output"),
|
||||
color_transform_.output());
|
||||
writer->writeEndElement(); // colortransform
|
||||
|
||||
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
|
||||
writer->writeTextElement(QStringLiteral("vscale"),
|
||||
QString::number(video_scaling_method_));
|
||||
|
||||
if (!video_opts_.isEmpty()) {
|
||||
writer->writeStartElement(QStringLiteral("opts"));
|
||||
if (!video_opts_.isEmpty()) {
|
||||
writer->writeStartElement(QStringLiteral("opts"));
|
||||
|
||||
QHash<QString, QString>::const_iterator i;
|
||||
for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) {
|
||||
writer->writeStartElement(QStringLiteral("entry"));
|
||||
QHash<QString, QString>::const_iterator i;
|
||||
for (i = video_opts_.constBegin(); i != video_opts_.constEnd();
|
||||
i++) {
|
||||
writer->writeStartElement(QStringLiteral("entry"));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("key"), i.key());
|
||||
writer->writeTextElement(QStringLiteral("value"), i.value());
|
||||
writer->writeTextElement(QStringLiteral("key"), i.key());
|
||||
writer->writeTextElement(QStringLiteral("value"), i.value());
|
||||
|
||||
writer->writeEndElement(); // entry
|
||||
}
|
||||
writer->writeEndElement(); // entry
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // opts
|
||||
}
|
||||
}
|
||||
writer->writeEndElement(); // opts
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // video
|
||||
writer->writeEndElement(); // video
|
||||
|
||||
writer->writeStartElement(QStringLiteral("audio"));
|
||||
writer->writeStartElement(QStringLiteral("audio"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_));
|
||||
writer->writeAttribute(QStringLiteral("enabled"),
|
||||
QString::number(audio_enabled_));
|
||||
|
||||
if (audio_enabled_) {
|
||||
writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_));
|
||||
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate()));
|
||||
if (audio_enabled_) {
|
||||
writer->writeTextElement(QStringLiteral("codec"),
|
||||
QString::number(audio_codec_));
|
||||
writer->writeTextElement(QStringLiteral("samplerate"),
|
||||
QString::number(audio_params_.sample_rate()));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("channellayout"),
|
||||
QString::number(audio_params().channel_layout().u.mask));
|
||||
writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string()));
|
||||
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_));
|
||||
}
|
||||
writer->writeTextElement(
|
||||
QStringLiteral("channellayout"),
|
||||
QString::number(audio_params().channel_layout().u.mask));
|
||||
writer->writeTextElement(
|
||||
QStringLiteral("format"),
|
||||
QString::fromStdString(audio_params_.format().to_string()));
|
||||
writer->writeTextElement(QStringLiteral("bitrate"),
|
||||
QString::number(audio_bit_rate_));
|
||||
}
|
||||
|
||||
writer->writeStartElement(QStringLiteral("subtitles"));
|
||||
writer->writeStartElement(QStringLiteral("subtitles"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("enabled"), QString::number(subtitles_enabled_));
|
||||
writer->writeAttribute(QStringLiteral("enabled"),
|
||||
QString::number(subtitles_enabled_));
|
||||
|
||||
if (subtitles_enabled_) {
|
||||
writer->writeTextElement(QStringLiteral("sidecar"), QString::number(subtitles_are_sidecar_));
|
||||
writer->writeTextElement(QStringLiteral("sidecarformat"), QString::number(subtitle_sidecar_fmt_));
|
||||
if (subtitles_enabled_) {
|
||||
writer->writeTextElement(QStringLiteral("sidecar"),
|
||||
QString::number(subtitles_are_sidecar_));
|
||||
writer->writeTextElement(QStringLiteral("sidecarformat"),
|
||||
QString::number(subtitle_sidecar_fmt_));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("codec"), QString::number(subtitles_codec_));
|
||||
}
|
||||
writer->writeTextElement(QStringLiteral("codec"),
|
||||
QString::number(subtitles_codec_));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // subtitles
|
||||
writer->writeEndElement(); // subtitles
|
||||
|
||||
writer->writeEndElement(); // audio
|
||||
writer->writeEndElement(); // audio
|
||||
|
||||
writer->writeEndElement(); // export
|
||||
writer->writeEndElement(); // export
|
||||
|
||||
writer->writeEndDocument();
|
||||
writer->writeEndDocument();
|
||||
}
|
||||
|
||||
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params)
|
||||
Encoder *Encoder::CreateFromID(Type id, const EncodingParams ¶ms)
|
||||
{
|
||||
switch (id) {
|
||||
case kEncoderTypeNone:
|
||||
break;
|
||||
case kEncoderTypeFFmpeg:
|
||||
return new FFmpegEncoder(params);
|
||||
case kEncoderTypeOIIO:
|
||||
return new OIIOEncoder(params);
|
||||
}
|
||||
switch (id) {
|
||||
case kEncoderTypeNone:
|
||||
break;
|
||||
case kEncoderTypeFFmpeg:
|
||||
return new FFmpegEncoder(params);
|
||||
case kEncoderTypeOIIO:
|
||||
return new OIIOEncoder(params);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
|
||||
{
|
||||
switch (f) {
|
||||
case ExportFormat::kFormatDNxHD:
|
||||
case ExportFormat::kFormatMatroska:
|
||||
case ExportFormat::kFormatQuickTime:
|
||||
case ExportFormat::kFormatMPEG4Video:
|
||||
case ExportFormat::kFormatMPEG4Audio:
|
||||
case ExportFormat::kFormatWAV:
|
||||
case ExportFormat::kFormatAIFF:
|
||||
case ExportFormat::kFormatMP3:
|
||||
case ExportFormat::kFormatFLAC:
|
||||
case ExportFormat::kFormatOgg:
|
||||
case ExportFormat::kFormatWebM:
|
||||
case ExportFormat::kFormatSRT:
|
||||
return kEncoderTypeFFmpeg;
|
||||
case ExportFormat::kFormatOpenEXR:
|
||||
case ExportFormat::kFormatPNG:
|
||||
case ExportFormat::kFormatTIFF:
|
||||
return kEncoderTypeOIIO;
|
||||
case ExportFormat::kFormatCount:
|
||||
break;
|
||||
}
|
||||
switch (f) {
|
||||
case ExportFormat::kFormatDNxHD:
|
||||
case ExportFormat::kFormatMatroska:
|
||||
case ExportFormat::kFormatQuickTime:
|
||||
case ExportFormat::kFormatMPEG4Video:
|
||||
case ExportFormat::kFormatMPEG4Audio:
|
||||
case ExportFormat::kFormatWAV:
|
||||
case ExportFormat::kFormatAIFF:
|
||||
case ExportFormat::kFormatMP3:
|
||||
case ExportFormat::kFormatFLAC:
|
||||
case ExportFormat::kFormatOgg:
|
||||
case ExportFormat::kFormatWebM:
|
||||
case ExportFormat::kFormatSRT:
|
||||
return kEncoderTypeFFmpeg;
|
||||
case ExportFormat::kFormatOpenEXR:
|
||||
case ExportFormat::kFormatPNG:
|
||||
case ExportFormat::kFormatTIFF:
|
||||
return kEncoderTypeOIIO;
|
||||
case ExportFormat::kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return kEncoderTypeNone;
|
||||
return kEncoderTypeNone;
|
||||
}
|
||||
|
||||
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms)
|
||||
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f,
|
||||
const EncodingParams ¶ms)
|
||||
{
|
||||
return CreateFromID(GetTypeFromFormat(f), params);
|
||||
return CreateFromID(GetTypeFromFormat(f), params);
|
||||
}
|
||||
|
||||
Encoder *Encoder::CreateFromParams(const EncodingParams ¶ms)
|
||||
{
|
||||
return CreateFromFormat(params.format(), params);
|
||||
return CreateFromFormat(params.format(), params);
|
||||
}
|
||||
|
||||
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,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height)
|
||||
QMatrix4x4
|
||||
EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height)
|
||||
{
|
||||
QMatrix4x4 preview_matrix;
|
||||
QMatrix4x4 preview_matrix;
|
||||
|
||||
if (method == EncodingParams::kStretch) {
|
||||
return preview_matrix;
|
||||
}
|
||||
if (method == EncodingParams::kStretch) {
|
||||
return preview_matrix;
|
||||
}
|
||||
|
||||
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height);
|
||||
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height);
|
||||
float export_ar =
|
||||
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)) {
|
||||
return preview_matrix;
|
||||
}
|
||||
if (qFuzzyCompare(export_ar, source_ar)) {
|
||||
return preview_matrix;
|
||||
}
|
||||
|
||||
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
|
||||
preview_matrix.scale(source_ar / export_ar, 1.0F);
|
||||
} else {
|
||||
preview_matrix.scale(1.0F, export_ar / source_ar);
|
||||
}
|
||||
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
|
||||
preview_matrix.scale(source_ar / export_ar, 1.0F);
|
||||
} else {
|
||||
preview_matrix.scale(1.0F, export_ar / source_ar);
|
||||
}
|
||||
|
||||
return preview_matrix;
|
||||
return preview_matrix;
|
||||
}
|
||||
|
||||
bool EncodingParams::LoadV1(QXmlStreamReader *reader)
|
||||
{
|
||||
rational custom_range_in, custom_range_out;
|
||||
rational custom_range_in, custom_range_out;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("filename")) {
|
||||
filename_ = reader->readElementText();
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
format_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("range")) {
|
||||
has_custom_range_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("customrangein")) {
|
||||
custom_range_in = rational::fromString(reader->readElementText().toStdString());
|
||||
} else if (reader->name() == QStringLiteral("customrangeout")) {
|
||||
custom_range_out = rational::fromString(reader->readElementText().toStdString());
|
||||
} else if (reader->name() == QStringLiteral("video")) {
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("enabled")) {
|
||||
video_enabled_ = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("filename")) {
|
||||
filename_ = reader->readElementText();
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
format_ = static_cast<ExportFormat::Format>(
|
||||
reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("range")) {
|
||||
has_custom_range_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("customrangein")) {
|
||||
custom_range_in =
|
||||
rational::fromString(reader->readElementText().toStdString());
|
||||
} else if (reader->name() == QStringLiteral("customrangeout")) {
|
||||
custom_range_out =
|
||||
rational::fromString(reader->readElementText().toStdString());
|
||||
} else if (reader->name() == QStringLiteral("video")) {
|
||||
XMLAttributeLoop(reader, attr)
|
||||
{
|
||||
if (attr.name() == QStringLiteral("enabled")) {
|
||||
video_enabled_ = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("codec")) {
|
||||
video_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("width")) {
|
||||
video_params_.set_width(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("height")) {
|
||||
video_params_.set_height(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
video_params_.set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt()));
|
||||
} else if (reader->name() == QStringLiteral("pixelaspect")) {
|
||||
video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString()));
|
||||
} else if (reader->name() == QStringLiteral("timebase")) {
|
||||
video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString()));
|
||||
} else if (reader->name() == QStringLiteral("divider")) {
|
||||
video_params_.set_divider(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||
video_bit_rate_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("minbitrate")) {
|
||||
video_min_bit_rate_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("maxbitrate")) {
|
||||
video_max_bit_rate_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("bufsize")) {
|
||||
video_buffer_size_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("threads")) {
|
||||
video_threads_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("pixfmt")) {
|
||||
video_pix_fmt_ = reader->readElementText();
|
||||
} else if (reader->name() == QStringLiteral("imgseq")) {
|
||||
video_is_image_sequence_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("output")) {
|
||||
color_transform_ = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("vscale")) {
|
||||
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("opts")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("entry")) {
|
||||
QString key, value;
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("key")) {
|
||||
key = reader->readElementText();
|
||||
} else if (reader->name() == QStringLiteral("value")) {
|
||||
value = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
set_video_option(key, value);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("codec")) {
|
||||
video_codec_ = static_cast<ExportCodec::Codec>(
|
||||
reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("width")) {
|
||||
video_params_.set_width(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("height")) {
|
||||
video_params_.set_height(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
video_params_.set_format(static_cast<PixelFormat::Format>(
|
||||
reader->readElementText().toInt()));
|
||||
} else if (reader->name() == QStringLiteral("pixelaspect")) {
|
||||
video_params_.set_pixel_aspect_ratio(rational::fromString(
|
||||
reader->readElementText().toStdString()));
|
||||
} else if (reader->name() == QStringLiteral("timebase")) {
|
||||
video_params_.set_time_base(rational::fromString(
|
||||
reader->readElementText().toStdString()));
|
||||
} else if (reader->name() == QStringLiteral("divider")) {
|
||||
video_params_.set_divider(
|
||||
reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||
video_bit_rate_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("minbitrate")) {
|
||||
video_min_bit_rate_ =
|
||||
reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("maxbitrate")) {
|
||||
video_max_bit_rate_ =
|
||||
reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("bufsize")) {
|
||||
video_buffer_size_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("threads")) {
|
||||
video_threads_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("pixfmt")) {
|
||||
video_pix_fmt_ = reader->readElementText();
|
||||
} else if (reader->name() == QStringLiteral("imgseq")) {
|
||||
video_is_image_sequence_ =
|
||||
reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("output")) {
|
||||
color_transform_ = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("vscale")) {
|
||||
video_scaling_method_ = static_cast<VideoScalingMethod>(
|
||||
reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("opts")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("entry")) {
|
||||
QString key, value;
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("key")) {
|
||||
key = reader->readElementText();
|
||||
} else if (reader->name() ==
|
||||
QStringLiteral("value")) {
|
||||
value = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
set_video_option(key, value);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
|
||||
if (video_params_.pixel_aspect_ratio().isNull()) {
|
||||
video_params_.set_pixel_aspect_ratio(1);
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("audio")) {
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("enabled")) {
|
||||
audio_enabled_ = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
|
||||
if (video_params_.pixel_aspect_ratio().isNull()) {
|
||||
video_params_.set_pixel_aspect_ratio(1);
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("audio")) {
|
||||
XMLAttributeLoop(reader, attr)
|
||||
{
|
||||
if (attr.name() == QStringLiteral("enabled")) {
|
||||
audio_enabled_ = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("codec")) {
|
||||
audio_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("samplerate")) {
|
||||
audio_params_.set_sample_rate(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("channellayout")) {
|
||||
AVChannelLayout av_channel_layout;
|
||||
av_channel_layout_from_mask(&av_channel_layout, reader->readElementText().toLongLong());
|
||||
audio_params_.set_channel_layout(av_channel_layout);
|
||||
av_channel_layout_uninit(&av_channel_layout);
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString()));
|
||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||
audio_bit_rate_ = reader->readElementText().toLongLong();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("codec")) {
|
||||
audio_codec_ = static_cast<ExportCodec::Codec>(
|
||||
reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("samplerate")) {
|
||||
audio_params_.set_sample_rate(
|
||||
reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("channellayout")) {
|
||||
AVChannelLayout av_channel_layout;
|
||||
av_channel_layout_from_mask(
|
||||
&av_channel_layout,
|
||||
reader->readElementText().toLongLong());
|
||||
audio_params_.set_channel_layout(av_channel_layout);
|
||||
av_channel_layout_uninit(&av_channel_layout);
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
audio_params_.set_format(SampleFormat::from_string(
|
||||
reader->readElementText().toStdString()));
|
||||
} else if (reader->name() == QStringLiteral("bitrate")) {
|
||||
audio_bit_rate_ = reader->readElementText().toLongLong();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Resolve bug where I forgot to serialize the audio bit rate
|
||||
if (!audio_bit_rate_) {
|
||||
audio_bit_rate_ = 320000;
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("subtitles")) {
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("enabled")) {
|
||||
subtitles_enabled_ = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
// HACK: Resolve bug where I forgot to serialize the audio bit rate
|
||||
if (!audio_bit_rate_) {
|
||||
audio_bit_rate_ = 320000;
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("subtitles")) {
|
||||
XMLAttributeLoop(reader, attr)
|
||||
{
|
||||
if (attr.name() == QStringLiteral("enabled")) {
|
||||
subtitles_enabled_ = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("sidecar")) {
|
||||
subtitles_are_sidecar_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("sidecarformat")) {
|
||||
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("codec")) {
|
||||
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("sidecar")) {
|
||||
subtitles_are_sidecar_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("sidecarformat")) {
|
||||
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(
|
||||
reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("codec")) {
|
||||
subtitles_codec_ = static_cast<ExportCodec::Codec>(
|
||||
reader->readElementText().toInt());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+270
-148
@@ -34,218 +34,340 @@
|
||||
#include "render/subtitleparams.h"
|
||||
#include "render/videoparams.h"
|
||||
//这个代码也许是导出编码视频用的?
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class Encoder;
|
||||
using EncoderPtr = std::shared_ptr<Encoder>;
|
||||
|
||||
class EncodingParams
|
||||
{
|
||||
class EncodingParams {
|
||||
public:
|
||||
enum VideoScalingMethod {
|
||||
kFit,
|
||||
kStretch,
|
||||
kCrop
|
||||
};
|
||||
enum VideoScalingMethod { kFit, kStretch, kCrop };
|
||||
|
||||
EncodingParams();
|
||||
EncodingParams();
|
||||
|
||||
static QDir GetPresetPath();
|
||||
static QStringList GetListOfPresets();
|
||||
static QDir GetPresetPath();
|
||||
static QStringList GetListOfPresets();
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
|
||||
}
|
||||
bool IsValid() const
|
||||
{
|
||||
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 EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec);
|
||||
void EnableSubtitles(const ExportCodec::Codec &scodec);
|
||||
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec);
|
||||
void EnableVideo(const VideoParams &video_params,
|
||||
const ExportCodec::Codec &vcodec);
|
||||
void EnableAudio(const AudioParams &audio_params,
|
||||
const ExportCodec::Codec &acodec);
|
||||
void EnableSubtitles(const ExportCodec::Codec &scodec);
|
||||
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
|
||||
const ExportCodec::Codec &scodec);
|
||||
|
||||
void DisableVideo();
|
||||
void DisableAudio();
|
||||
void DisableSubtitles();
|
||||
void DisableVideo();
|
||||
void DisableAudio();
|
||||
void DisableSubtitles();
|
||||
|
||||
const ExportFormat::Format &format() const { return format_; }
|
||||
void set_format(const ExportFormat::Format &format) { format_ = format; }
|
||||
const ExportFormat::Format &format() const
|
||||
{
|
||||
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_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; }
|
||||
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; }
|
||||
void set_video_option(const QString &key, const QString &value)
|
||||
{
|
||||
video_opts_.insert(key, 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;
|
||||
}
|
||||
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_; }
|
||||
const ExportCodec::Codec& video_codec() const { return video_codec_; }
|
||||
const VideoParams& video_params() const { return video_params_; }
|
||||
const QHash<QString, QString>& video_opts() const { 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 video_enabled() const
|
||||
{
|
||||
return video_enabled_;
|
||||
}
|
||||
const ExportCodec::Codec &video_codec() const
|
||||
{
|
||||
return video_codec_;
|
||||
}
|
||||
const VideoParams &video_params() const
|
||||
{
|
||||
return video_params_;
|
||||
}
|
||||
const QHash<QString, QString> &video_opts() const
|
||||
{
|
||||
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_; }
|
||||
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_; }
|
||||
bool audio_enabled() const
|
||||
{
|
||||
return audio_enabled_;
|
||||
}
|
||||
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_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_; }
|
||||
bool subtitles_enabled() const
|
||||
{
|
||||
return subtitles_enabled_;
|
||||
}
|
||||
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_; }
|
||||
void SetExportLength(const rational& export_length) { export_length_ = export_length; }
|
||||
const rational &GetExportLength() const
|
||||
{
|
||||
return export_length_;
|
||||
}
|
||||
void SetExportLength(const rational &export_length)
|
||||
{
|
||||
export_length_ = export_length;
|
||||
}
|
||||
|
||||
bool Load(QIODevice *device);
|
||||
bool Load(QXmlStreamReader *reader);
|
||||
bool Load(QIODevice *device);
|
||||
bool Load(QXmlStreamReader *reader);
|
||||
|
||||
void Save(QIODevice *device) const;
|
||||
void Save(QXmlStreamWriter* writer) const;
|
||||
void Save(QIODevice *device) const;
|
||||
void Save(QXmlStreamWriter *writer) const;
|
||||
|
||||
bool has_custom_range() const { return has_custom_range_; }
|
||||
const TimeRange& custom_range() const { return custom_range_; }
|
||||
void set_custom_range(const TimeRange& custom_range)
|
||||
{
|
||||
has_custom_range_ = true;
|
||||
custom_range_ = custom_range;
|
||||
}
|
||||
bool has_custom_range() const
|
||||
{
|
||||
return has_custom_range_;
|
||||
}
|
||||
const TimeRange &custom_range() const
|
||||
{
|
||||
return custom_range_;
|
||||
}
|
||||
void set_custom_range(const TimeRange &custom_range)
|
||||
{
|
||||
has_custom_range_ = true;
|
||||
custom_range_ = custom_range;
|
||||
}
|
||||
|
||||
const VideoScalingMethod& video_scaling_method() const { return video_scaling_method_; }
|
||||
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method) { video_scaling_method_ = video_scaling_method; }
|
||||
const VideoScalingMethod &video_scaling_method() const
|
||||
{
|
||||
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,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height);
|
||||
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height);
|
||||
|
||||
private:
|
||||
static const int kEncoderParamsVersion = 1;
|
||||
static const int kEncoderParamsVersion = 1;
|
||||
|
||||
bool LoadV1(QXmlStreamReader *reader);
|
||||
bool LoadV1(QXmlStreamReader *reader);
|
||||
|
||||
QString filename_;
|
||||
ExportFormat::Format format_;
|
||||
QString filename_;
|
||||
ExportFormat::Format format_;
|
||||
|
||||
bool video_enabled_;
|
||||
ExportCodec::Codec video_codec_;
|
||||
VideoParams video_params_;
|
||||
QHash<QString, QString> video_opts_;
|
||||
int64_t video_bit_rate_;
|
||||
int64_t video_min_bit_rate_;
|
||||
int64_t video_max_bit_rate_;
|
||||
int64_t video_buffer_size_;
|
||||
int video_threads_;
|
||||
QString video_pix_fmt_;
|
||||
bool video_is_image_sequence_;
|
||||
ColorTransform color_transform_;
|
||||
bool video_enabled_;
|
||||
ExportCodec::Codec video_codec_;
|
||||
VideoParams video_params_;
|
||||
QHash<QString, QString> video_opts_;
|
||||
int64_t video_bit_rate_;
|
||||
int64_t video_min_bit_rate_;
|
||||
int64_t video_max_bit_rate_;
|
||||
int64_t video_buffer_size_;
|
||||
int video_threads_;
|
||||
QString video_pix_fmt_;
|
||||
bool video_is_image_sequence_;
|
||||
ColorTransform color_transform_;
|
||||
|
||||
bool audio_enabled_;
|
||||
ExportCodec::Codec audio_codec_;
|
||||
AudioParams audio_params_;
|
||||
int64_t audio_bit_rate_;
|
||||
bool audio_enabled_;
|
||||
ExportCodec::Codec audio_codec_;
|
||||
AudioParams audio_params_;
|
||||
int64_t audio_bit_rate_;
|
||||
|
||||
bool subtitles_enabled_;
|
||||
bool subtitles_are_sidecar_;
|
||||
ExportFormat::Format subtitle_sidecar_fmt_;
|
||||
ExportCodec::Codec subtitles_codec_;
|
||||
bool subtitles_enabled_;
|
||||
bool subtitles_are_sidecar_;
|
||||
ExportFormat::Format subtitle_sidecar_fmt_;
|
||||
ExportCodec::Codec subtitles_codec_;
|
||||
|
||||
rational export_length_;
|
||||
VideoScalingMethod video_scaling_method_;
|
||||
|
||||
bool has_custom_range_;
|
||||
TimeRange custom_range_;
|
||||
rational export_length_;
|
||||
VideoScalingMethod video_scaling_method_;
|
||||
|
||||
bool has_custom_range_;
|
||||
TimeRange custom_range_;
|
||||
};
|
||||
|
||||
class Encoder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class Encoder : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Encoder(const EncodingParams& params);
|
||||
Encoder(const EncodingParams ¶ms);
|
||||
|
||||
enum Type {
|
||||
kEncoderTypeNone = -1,
|
||||
kEncoderTypeFFmpeg,
|
||||
kEncoderTypeOIIO
|
||||
};
|
||||
enum Type { kEncoderTypeNone = -1, kEncoderTypeFFmpeg, kEncoderTypeOIIO };
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Create a Encoder instance using a Encoder ID
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A Encoder instance or nullptr if a Decoder with this ID does not exist
|
||||
*/
|
||||
static Encoder *CreateFromID(Type id, const EncodingParams ¶ms);
|
||||
static Encoder *CreateFromID(Type id, const EncodingParams ¶ms);
|
||||
|
||||
static Type GetTypeFromFormat(ExportFormat::Format f);
|
||||
static Type GetTypeFromFormat(ExportFormat::Format f);
|
||||
|
||||
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams ¶ms);
|
||||
static Encoder *CreateFromFormat(ExportFormat::Format f,
|
||||
const EncodingParams ¶ms);
|
||||
|
||||
static Encoder *CreateFromParams(const EncodingParams ¶ms);
|
||||
static Encoder *CreateFromParams(const EncodingParams ¶ms);
|
||||
|
||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
||||
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const;
|
||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
|
||||
virtual std::vector<SampleFormat>
|
||||
GetSampleFormatsForCodec(ExportCodec::Codec c) const;
|
||||
|
||||
const EncodingParams& params() const;
|
||||
const EncodingParams ¶ms() const;
|
||||
|
||||
virtual PixelFormat GetDesiredPixelFormat() const
|
||||
{
|
||||
return PixelFormat::INVALID;
|
||||
}
|
||||
virtual PixelFormat GetDesiredPixelFormat() const
|
||||
{
|
||||
return PixelFormat::INVALID;
|
||||
}
|
||||
|
||||
const QString& GetError() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
const QString &GetError() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
|
||||
QString GetFilenameForFrame(const rational& frame);
|
||||
QString GetFilenameForFrame(const rational &frame);
|
||||
|
||||
static int GetImageSequencePlaceholderDigitCount(const QString& filename);
|
||||
static int GetImageSequencePlaceholderDigitCount(const QString &filename);
|
||||
|
||||
static bool FilenameContainsDigitPlaceholder(const QString &filename);
|
||||
static QString FilenameRemoveDigitPlaceholder(QString filename);
|
||||
static bool FilenameContainsDigitPlaceholder(const QString &filename);
|
||||
static QString FilenameRemoveDigitPlaceholder(QString filename);
|
||||
|
||||
static const QRegularExpression kImageSequenceContainsDigits;
|
||||
static const QRegularExpression kImageSequenceRemoveDigits;
|
||||
static const QRegularExpression kImageSequenceContainsDigits;
|
||||
static const QRegularExpression kImageSequenceRemoveDigits;
|
||||
|
||||
public slots:
|
||||
virtual bool Open() = 0;
|
||||
virtual bool Open() = 0;
|
||||
|
||||
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) = 0;
|
||||
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
|
||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
||||
virtual bool WriteFrame(olive::FramePtr frame,
|
||||
olive::core::rational time) = 0;
|
||||
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
|
||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
||||
|
||||
virtual void Close() = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
protected:
|
||||
void SetError(const QString& err)
|
||||
{
|
||||
error_ = err;
|
||||
}
|
||||
void SetError(const QString &err)
|
||||
{
|
||||
error_ = err;
|
||||
}
|
||||
|
||||
private:
|
||||
EncodingParams params_;
|
||||
|
||||
QString error_;
|
||||
EncodingParams params_;
|
||||
|
||||
QString error_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+96
-95
@@ -25,115 +25,116 @@ extern "C" {
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
QString ExportCodec::GetCodecName(ExportCodec::Codec c)
|
||||
{
|
||||
switch (c) {
|
||||
case kCodecDNxHD:
|
||||
return tr("DNxHD");
|
||||
case kCodecH264:
|
||||
return tr("H.264");
|
||||
case kCodecH264rgb:
|
||||
return tr("H.264 RGB");
|
||||
case kCodecH265:
|
||||
return tr("H.265");
|
||||
case kCodecOpenEXR:
|
||||
return tr("OpenEXR");
|
||||
case kCodecPNG:
|
||||
return tr("PNG");
|
||||
case kCodecProRes:
|
||||
return tr("ProRes");
|
||||
case kCodecCineform:
|
||||
return tr("Cineform");
|
||||
case kCodecTIFF:
|
||||
return tr("TIFF");
|
||||
case kCodecMP2:
|
||||
return tr("MP2");
|
||||
case kCodecMP3:
|
||||
return tr("MP3");
|
||||
case kCodecAAC:
|
||||
return tr("AAC");
|
||||
case kCodecPCM:
|
||||
return tr("PCM (Uncompressed)");
|
||||
case kCodecFLAC:
|
||||
return tr("FLAC");
|
||||
case kCodecOpus:
|
||||
return tr("Opus");
|
||||
case kCodecVorbis:
|
||||
return tr("Vorbis");
|
||||
case kCodecVP9:
|
||||
return tr("VP9");
|
||||
case kCodecAV1:
|
||||
return tr("AV1");
|
||||
case kCodecSRT:
|
||||
return tr("SubRip SRT");
|
||||
case kCodecCount:
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case kCodecDNxHD:
|
||||
return tr("DNxHD");
|
||||
case kCodecH264:
|
||||
return tr("H.264");
|
||||
case kCodecH264rgb:
|
||||
return tr("H.264 RGB");
|
||||
case kCodecH265:
|
||||
return tr("H.265");
|
||||
case kCodecOpenEXR:
|
||||
return tr("OpenEXR");
|
||||
case kCodecPNG:
|
||||
return tr("PNG");
|
||||
case kCodecProRes:
|
||||
return tr("ProRes");
|
||||
case kCodecCineform:
|
||||
return tr("Cineform");
|
||||
case kCodecTIFF:
|
||||
return tr("TIFF");
|
||||
case kCodecMP2:
|
||||
return tr("MP2");
|
||||
case kCodecMP3:
|
||||
return tr("MP3");
|
||||
case kCodecAAC:
|
||||
return tr("AAC");
|
||||
case kCodecPCM:
|
||||
return tr("PCM (Uncompressed)");
|
||||
case kCodecFLAC:
|
||||
return tr("FLAC");
|
||||
case kCodecOpus:
|
||||
return tr("Opus");
|
||||
case kCodecVorbis:
|
||||
return tr("Vorbis");
|
||||
case kCodecVP9:
|
||||
return tr("VP9");
|
||||
case kCodecAV1:
|
||||
return tr("AV1");
|
||||
case kCodecSRT:
|
||||
return tr("SubRip SRT");
|
||||
case kCodecCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return tr("Unknown");
|
||||
return tr("Unknown");
|
||||
}
|
||||
|
||||
bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
|
||||
{
|
||||
switch (c) {
|
||||
case kCodecDNxHD:
|
||||
case kCodecH264:
|
||||
case kCodecH264rgb:
|
||||
case kCodecH265:
|
||||
case kCodecProRes:
|
||||
case kCodecCineform:
|
||||
case kCodecMP2:
|
||||
case kCodecMP3:
|
||||
case kCodecAAC:
|
||||
case kCodecPCM:
|
||||
case kCodecVorbis:
|
||||
case kCodecOpus:
|
||||
case kCodecFLAC:
|
||||
case kCodecVP9:
|
||||
case kCodecAV1:
|
||||
case kCodecSRT:
|
||||
return false;
|
||||
case kCodecOpenEXR:
|
||||
case kCodecPNG:
|
||||
case kCodecTIFF:
|
||||
return true;
|
||||
case kCodecCount:
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case kCodecDNxHD:
|
||||
case kCodecH264:
|
||||
case kCodecH264rgb:
|
||||
case kCodecH265:
|
||||
case kCodecProRes:
|
||||
case kCodecCineform:
|
||||
case kCodecMP2:
|
||||
case kCodecMP3:
|
||||
case kCodecAAC:
|
||||
case kCodecPCM:
|
||||
case kCodecVorbis:
|
||||
case kCodecOpus:
|
||||
case kCodecFLAC:
|
||||
case kCodecVP9:
|
||||
case kCodecAV1:
|
||||
case kCodecSRT:
|
||||
return false;
|
||||
case kCodecOpenEXR:
|
||||
case kCodecPNG:
|
||||
case kCodecTIFF:
|
||||
return true;
|
||||
case kCodecCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExportCodec::IsCodecLossless(Codec c)
|
||||
{
|
||||
switch (c) {
|
||||
case kCodecPCM:
|
||||
case kCodecFLAC:
|
||||
return true;
|
||||
case kCodecDNxHD:
|
||||
case kCodecH264:
|
||||
case kCodecH264rgb:
|
||||
case kCodecH265:
|
||||
case kCodecProRes:
|
||||
case kCodecCineform:
|
||||
case kCodecMP2:
|
||||
case kCodecMP3:
|
||||
case kCodecAAC:
|
||||
case kCodecVorbis:
|
||||
case kCodecOpus:
|
||||
case kCodecVP9:
|
||||
case kCodecAV1:
|
||||
case kCodecSRT:
|
||||
case kCodecOpenEXR:
|
||||
case kCodecPNG:
|
||||
case kCodecTIFF:
|
||||
case kCodecCount:
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case kCodecPCM:
|
||||
case kCodecFLAC:
|
||||
return true;
|
||||
case kCodecDNxHD:
|
||||
case kCodecH264:
|
||||
case kCodecH264rgb:
|
||||
case kCodecH265:
|
||||
case kCodecProRes:
|
||||
case kCodecCineform:
|
||||
case kCodecMP2:
|
||||
case kCodecMP3:
|
||||
case kCodecAAC:
|
||||
case kCodecVorbis:
|
||||
case kCodecOpus:
|
||||
case kCodecVP9:
|
||||
case kCodecAV1:
|
||||
case kCodecSRT:
|
||||
case kCodecOpenEXR:
|
||||
case kCodecPNG:
|
||||
case kCodecTIFF:
|
||||
case kCodecCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-31
@@ -27,43 +27,42 @@
|
||||
#include "common/define.h"
|
||||
#include "render/subtitleparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ExportCodec : public QObject
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class ExportCodec : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Only append to this list (never insert) because indexes are used in serialized files
|
||||
enum Codec {
|
||||
kCodecDNxHD,
|
||||
kCodecH264,
|
||||
kCodecH264rgb,
|
||||
kCodecH265,
|
||||
kCodecOpenEXR,
|
||||
kCodecPNG,
|
||||
kCodecProRes,
|
||||
kCodecCineform,
|
||||
kCodecTIFF,
|
||||
kCodecVP9,
|
||||
kCodecMP2,
|
||||
kCodecMP3,
|
||||
kCodecAAC,
|
||||
kCodecPCM,
|
||||
kCodecOpus,
|
||||
kCodecVorbis,
|
||||
kCodecFLAC,
|
||||
kCodecSRT,
|
||||
kCodecAV1,
|
||||
// Only append to this list (never insert) because indexes are used in serialized files
|
||||
enum Codec {
|
||||
kCodecDNxHD,
|
||||
kCodecH264,
|
||||
kCodecH264rgb,
|
||||
kCodecH265,
|
||||
kCodecOpenEXR,
|
||||
kCodecPNG,
|
||||
kCodecProRes,
|
||||
kCodecCineform,
|
||||
kCodecTIFF,
|
||||
kCodecVP9,
|
||||
kCodecMP2,
|
||||
kCodecMP3,
|
||||
kCodecAAC,
|
||||
kCodecPCM,
|
||||
kCodecOpus,
|
||||
kCodecVorbis,
|
||||
kCodecFLAC,
|
||||
kCodecSRT,
|
||||
kCodecAV1,
|
||||
|
||||
kCodecCount
|
||||
};
|
||||
kCodecCount
|
||||
};
|
||||
|
||||
static QString GetCodecName(Codec c);
|
||||
static QString GetCodecName(Codec c);
|
||||
|
||||
static bool IsCodecAStillImage(Codec c);
|
||||
|
||||
static bool IsCodecLossless(Codec c);
|
||||
static bool IsCodecAStillImage(Codec c);
|
||||
|
||||
static bool IsCodecLossless(Codec c);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+184
-171
@@ -22,214 +22,227 @@
|
||||
|
||||
#include "encoder.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
QString ExportFormat::GetName(olive::ExportFormat::Format f)
|
||||
{
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
return tr("DNxHD");
|
||||
case kFormatMatroska:
|
||||
return tr("Matroska Video");
|
||||
case kFormatMPEG4Video:
|
||||
return tr("MPEG-4 Video");
|
||||
case kFormatMPEG4Audio:
|
||||
return tr("MPEG-4 Audio");
|
||||
case kFormatOpenEXR:
|
||||
return tr("OpenEXR");
|
||||
case kFormatPNG:
|
||||
return tr("PNG");
|
||||
case kFormatTIFF:
|
||||
return tr("TIFF");
|
||||
case kFormatQuickTime:
|
||||
return tr("QuickTime");
|
||||
case kFormatWAV:
|
||||
return tr("Wave Audio");
|
||||
case kFormatAIFF:
|
||||
return tr("AIFF");
|
||||
case kFormatMP3:
|
||||
return tr("MP3");
|
||||
case kFormatFLAC:
|
||||
return tr("FLAC");
|
||||
case kFormatOgg:
|
||||
return tr("Ogg");
|
||||
case kFormatWebM:
|
||||
return tr("WebM");
|
||||
case kFormatSRT:
|
||||
return tr("SubRip SRT");
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
return tr("DNxHD");
|
||||
case kFormatMatroska:
|
||||
return tr("Matroska Video");
|
||||
case kFormatMPEG4Video:
|
||||
return tr("MPEG-4 Video");
|
||||
case kFormatMPEG4Audio:
|
||||
return tr("MPEG-4 Audio");
|
||||
case kFormatOpenEXR:
|
||||
return tr("OpenEXR");
|
||||
case kFormatPNG:
|
||||
return tr("PNG");
|
||||
case kFormatTIFF:
|
||||
return tr("TIFF");
|
||||
case kFormatQuickTime:
|
||||
return tr("QuickTime");
|
||||
case kFormatWAV:
|
||||
return tr("Wave Audio");
|
||||
case kFormatAIFF:
|
||||
return tr("AIFF");
|
||||
case kFormatMP3:
|
||||
return tr("MP3");
|
||||
case kFormatFLAC:
|
||||
return tr("FLAC");
|
||||
case kFormatOgg:
|
||||
return tr("Ogg");
|
||||
case kFormatWebM:
|
||||
return tr("WebM");
|
||||
case kFormatSRT:
|
||||
return tr("SubRip SRT");
|
||||
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return tr("Unknown");
|
||||
return tr("Unknown");
|
||||
}
|
||||
|
||||
QString ExportFormat::GetExtension(ExportFormat::Format f)
|
||||
{
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
return QStringLiteral("mxf");
|
||||
case kFormatMatroska:
|
||||
return QStringLiteral("mkv");
|
||||
case kFormatMPEG4Video:
|
||||
return QStringLiteral("mp4");
|
||||
case kFormatMPEG4Audio:
|
||||
return QStringLiteral("m4a");
|
||||
case kFormatOpenEXR:
|
||||
return QStringLiteral("exr");
|
||||
case kFormatPNG:
|
||||
return QStringLiteral("png");
|
||||
case kFormatTIFF:
|
||||
return QStringLiteral("tiff");
|
||||
case kFormatQuickTime:
|
||||
return QStringLiteral("mov");
|
||||
case kFormatWAV:
|
||||
return QStringLiteral("wav");
|
||||
case kFormatAIFF:
|
||||
return QStringLiteral("aiff");
|
||||
case kFormatMP3:
|
||||
return QStringLiteral("mp3");
|
||||
case kFormatFLAC:
|
||||
return QStringLiteral("flac");
|
||||
case kFormatOgg:
|
||||
return QStringLiteral("ogg");
|
||||
case kFormatWebM:
|
||||
return QStringLiteral("webm");
|
||||
case kFormatSRT:
|
||||
return QStringLiteral("srt");
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
return QStringLiteral("mxf");
|
||||
case kFormatMatroska:
|
||||
return QStringLiteral("mkv");
|
||||
case kFormatMPEG4Video:
|
||||
return QStringLiteral("mp4");
|
||||
case kFormatMPEG4Audio:
|
||||
return QStringLiteral("m4a");
|
||||
case kFormatOpenEXR:
|
||||
return QStringLiteral("exr");
|
||||
case kFormatPNG:
|
||||
return QStringLiteral("png");
|
||||
case kFormatTIFF:
|
||||
return QStringLiteral("tiff");
|
||||
case kFormatQuickTime:
|
||||
return QStringLiteral("mov");
|
||||
case kFormatWAV:
|
||||
return QStringLiteral("wav");
|
||||
case kFormatAIFF:
|
||||
return QStringLiteral("aiff");
|
||||
case kFormatMP3:
|
||||
return QStringLiteral("mp3");
|
||||
case kFormatFLAC:
|
||||
return QStringLiteral("flac");
|
||||
case kFormatOgg:
|
||||
return QStringLiteral("ogg");
|
||||
case kFormatWebM:
|
||||
return QStringLiteral("webm");
|
||||
case kFormatSRT:
|
||||
return QStringLiteral("srt");
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
return QString();
|
||||
}
|
||||
|
||||
QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
||||
{
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
return {ExportCodec::kCodecDNxHD};
|
||||
case kFormatMatroska:
|
||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9};
|
||||
case kFormatMPEG4Video:
|
||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265};
|
||||
case kFormatOpenEXR:
|
||||
return {ExportCodec::kCodecOpenEXR};
|
||||
case kFormatPNG:
|
||||
return {ExportCodec::kCodecPNG};
|
||||
case kFormatTIFF:
|
||||
return {ExportCodec::kCodecTIFF};
|
||||
case kFormatQuickTime:
|
||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform};
|
||||
case kFormatWebM:
|
||||
return {ExportCodec::kCodecAV1, ExportCodec::kCodecVP9};
|
||||
case kFormatOgg:
|
||||
case kFormatWAV:
|
||||
case kFormatMPEG4Audio:
|
||||
case kFormatAIFF:
|
||||
case kFormatMP3:
|
||||
case kFormatFLAC:
|
||||
case kFormatSRT:
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
return { ExportCodec::kCodecDNxHD };
|
||||
case kFormatMatroska:
|
||||
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||
ExportCodec::kCodecH265, ExportCodec::kCodecVP9 };
|
||||
case kFormatMPEG4Video:
|
||||
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||
ExportCodec::kCodecH265 };
|
||||
case kFormatOpenEXR:
|
||||
return { ExportCodec::kCodecOpenEXR };
|
||||
case kFormatPNG:
|
||||
return { ExportCodec::kCodecPNG };
|
||||
case kFormatTIFF:
|
||||
return { ExportCodec::kCodecTIFF };
|
||||
case kFormatQuickTime:
|
||||
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
|
||||
ExportCodec::kCodecH265, ExportCodec::kCodecProRes,
|
||||
ExportCodec::kCodecCineform };
|
||||
case kFormatWebM:
|
||||
return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
|
||||
case kFormatOgg:
|
||||
case kFormatWAV:
|
||||
case kFormatMPEG4Audio:
|
||||
case kFormatAIFF:
|
||||
case kFormatMP3:
|
||||
case kFormatFLAC:
|
||||
case kFormatSRT:
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
|
||||
{
|
||||
switch (f) {
|
||||
// Video/audio formats
|
||||
case kFormatDNxHD:
|
||||
return {ExportCodec::kCodecPCM};
|
||||
case kFormatMatroska:
|
||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC};
|
||||
case kFormatMPEG4Video:
|
||||
case kFormatMPEG4Audio:
|
||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3};
|
||||
case kFormatQuickTime:
|
||||
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
|
||||
case kFormatWebM:
|
||||
return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis};
|
||||
switch (f) {
|
||||
// Video/audio formats
|
||||
case kFormatDNxHD:
|
||||
return { ExportCodec::kCodecPCM };
|
||||
case kFormatMatroska:
|
||||
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM,
|
||||
ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus,
|
||||
ExportCodec::kCodecFLAC };
|
||||
case kFormatMPEG4Video:
|
||||
case kFormatMPEG4Audio:
|
||||
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||
ExportCodec::kCodecMP3 };
|
||||
case kFormatQuickTime:
|
||||
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
|
||||
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM };
|
||||
case kFormatWebM:
|
||||
return { ExportCodec::kCodecOpus, ExportCodec::kCodecAAC,
|
||||
ExportCodec::kCodecMP2, ExportCodec::kCodecMP3,
|
||||
ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis };
|
||||
|
||||
// Audio only formats
|
||||
case kFormatWAV:
|
||||
return {ExportCodec::kCodecPCM};
|
||||
case kFormatAIFF:
|
||||
return {ExportCodec::kCodecPCM};
|
||||
case kFormatMP3:
|
||||
return {ExportCodec::kCodecMP3};
|
||||
case kFormatFLAC:
|
||||
return {ExportCodec::kCodecFLAC};
|
||||
case kFormatOgg:
|
||||
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM};
|
||||
// Audio only formats
|
||||
case kFormatWAV:
|
||||
return { ExportCodec::kCodecPCM };
|
||||
case kFormatAIFF:
|
||||
return { ExportCodec::kCodecPCM };
|
||||
case kFormatMP3:
|
||||
return { ExportCodec::kCodecMP3 };
|
||||
case kFormatFLAC:
|
||||
return { ExportCodec::kCodecFLAC };
|
||||
case kFormatOgg:
|
||||
return { ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis,
|
||||
ExportCodec::kCodecPCM };
|
||||
|
||||
// Video only formats
|
||||
case kFormatOpenEXR:
|
||||
case kFormatPNG:
|
||||
case kFormatTIFF:
|
||||
case kFormatSRT:
|
||||
case kFormatCount:
|
||||
break;
|
||||
// Video only formats
|
||||
case kFormatOpenEXR:
|
||||
case kFormatPNG:
|
||||
case kFormatTIFF:
|
||||
case kFormatSRT:
|
||||
case kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
|
||||
{
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
case kFormatMPEG4Video:
|
||||
case kFormatMPEG4Audio:
|
||||
case kFormatOpenEXR:
|
||||
case kFormatQuickTime:
|
||||
case kFormatPNG:
|
||||
case kFormatTIFF:
|
||||
case kFormatWAV:
|
||||
case kFormatAIFF:
|
||||
case kFormatMP3:
|
||||
case kFormatFLAC:
|
||||
case kFormatOgg:
|
||||
case kFormatWebM:
|
||||
case kFormatCount:
|
||||
break;
|
||||
case kFormatMatroska:
|
||||
case kFormatSRT:
|
||||
return {ExportCodec::kCodecSRT};
|
||||
}
|
||||
switch (f) {
|
||||
case kFormatDNxHD:
|
||||
case kFormatMPEG4Video:
|
||||
case kFormatMPEG4Audio:
|
||||
case kFormatOpenEXR:
|
||||
case kFormatQuickTime:
|
||||
case kFormatPNG:
|
||||
case kFormatTIFF:
|
||||
case kFormatWAV:
|
||||
case kFormatAIFF:
|
||||
case kFormatMP3:
|
||||
case kFormatFLAC:
|
||||
case kFormatOgg:
|
||||
case kFormatWebM:
|
||||
case kFormatCount:
|
||||
break;
|
||||
case kFormatMatroska:
|
||||
case kFormatSRT:
|
||||
return { ExportCodec::kCodecSRT };
|
||||
}
|
||||
|
||||
return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c)
|
||||
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f,
|
||||
ExportCodec::Codec c)
|
||||
{
|
||||
Encoder* e = Encoder::CreateFromFormat(f, EncodingParams());
|
||||
QStringList list;
|
||||
Encoder *e = Encoder::CreateFromFormat(f, EncodingParams());
|
||||
QStringList list;
|
||||
|
||||
if (e) {
|
||||
list = e->GetPixelFormatsForCodec(c);
|
||||
delete e;
|
||||
}
|
||||
if (e) {
|
||||
list = e->GetPixelFormatsForCodec(c);
|
||||
delete e;
|
||||
}
|
||||
|
||||
return list;
|
||||
return list;
|
||||
}
|
||||
|
||||
std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
|
||||
std::vector<SampleFormat>
|
||||
ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
|
||||
{
|
||||
std::vector<SampleFormat> f;
|
||||
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
|
||||
std::vector<SampleFormat> f;
|
||||
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
|
||||
|
||||
if (e) {
|
||||
f = e->GetSampleFormatsForCodec(c);
|
||||
delete e;
|
||||
}
|
||||
if (e) {
|
||||
f = e->GetSampleFormatsForCodec(c);
|
||||
delete e;
|
||||
}
|
||||
|
||||
|
||||
return f;
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+31
-31
@@ -27,42 +27,42 @@
|
||||
#include "common/define.h"
|
||||
#include "exportcodec.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ExportFormat : public QObject
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class ExportFormat : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Only append to this list (never insert) because indexes are used in serialized files
|
||||
enum Format {
|
||||
kFormatDNxHD,
|
||||
kFormatMatroska,
|
||||
kFormatMPEG4Video,
|
||||
kFormatOpenEXR,
|
||||
kFormatQuickTime,
|
||||
kFormatPNG,
|
||||
kFormatTIFF,
|
||||
kFormatWAV,
|
||||
kFormatAIFF,
|
||||
kFormatMP3,
|
||||
kFormatFLAC,
|
||||
kFormatOgg,
|
||||
kFormatWebM,
|
||||
kFormatSRT,
|
||||
kFormatMPEG4Audio,
|
||||
// Only append to this list (never insert) because indexes are used in serialized files
|
||||
enum Format {
|
||||
kFormatDNxHD,
|
||||
kFormatMatroska,
|
||||
kFormatMPEG4Video,
|
||||
kFormatOpenEXR,
|
||||
kFormatQuickTime,
|
||||
kFormatPNG,
|
||||
kFormatTIFF,
|
||||
kFormatWAV,
|
||||
kFormatAIFF,
|
||||
kFormatMP3,
|
||||
kFormatFLAC,
|
||||
kFormatOgg,
|
||||
kFormatWebM,
|
||||
kFormatSRT,
|
||||
kFormatMPEG4Audio,
|
||||
|
||||
kFormatCount
|
||||
};
|
||||
kFormatCount
|
||||
};
|
||||
|
||||
static QString GetName(Format f);
|
||||
static QString GetExtension(Format f);
|
||||
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
|
||||
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
|
||||
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
|
||||
|
||||
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||
static std::vector<SampleFormat> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||
static QString GetName(Format f);
|
||||
static QString GetExtension(Format f);
|
||||
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
|
||||
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
|
||||
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
|
||||
|
||||
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||
static std::vector<SampleFormat>
|
||||
GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+936
-860
File diff suppressed because it is too large
Load Diff
@@ -39,92 +39,100 @@ extern "C" {
|
||||
#include "codec/decoder.h"
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||
*/
|
||||
class FFmpegDecoder : public Decoder
|
||||
{
|
||||
Q_OBJECT
|
||||
class FFmpegDecoder : public Decoder {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Constructor
|
||||
FFmpegDecoder();
|
||||
// Constructor
|
||||
FFmpegDecoder();
|
||||
|
||||
// Destructor
|
||||
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
|
||||
// Destructor
|
||||
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
|
||||
|
||||
virtual QString id() const override;
|
||||
virtual QString id() const override;
|
||||
|
||||
virtual bool SupportsVideo() override{return true;}
|
||||
virtual bool SupportsAudio() override{return true;}
|
||||
virtual bool SupportsVideo() override
|
||||
{
|
||||
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:
|
||||
virtual bool OpenInternal() override;
|
||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
|
||||
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams ¶ms, CancelAtom *cancelled) override;
|
||||
virtual void CloseInternal() override;
|
||||
virtual bool OpenInternal() override;
|
||||
virtual TexturePtr
|
||||
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
|
||||
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
|
||||
const AudioParams ¶ms,
|
||||
CancelAtom *cancelled) override;
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
virtual rational GetAudioStartOffset() const override;
|
||||
virtual rational GetAudioStartOffset() const override;
|
||||
|
||||
private:
|
||||
class Instance
|
||||
{
|
||||
public:
|
||||
Instance();
|
||||
class Instance {
|
||||
public:
|
||||
Instance();
|
||||
|
||||
~Instance()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
~Instance()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool Open(const char* filename, int stream_index);
|
||||
bool Open(const char *filename, int stream_index);
|
||||
|
||||
bool IsOpen() const
|
||||
{
|
||||
return fmt_ctx_;
|
||||
}
|
||||
bool IsOpen() const
|
||||
{
|
||||
return fmt_ctx_;
|
||||
}
|
||||
|
||||
void Close();
|
||||
void Close();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* An FFmpeg error code, or >= 0 on success
|
||||
*/
|
||||
int GetFrame(AVPacket* pkt, AVFrame* frame);
|
||||
int GetFrame(AVPacket *pkt, AVFrame *frame);
|
||||
|
||||
const char *GetSubtitleHeader() const;
|
||||
const char *GetSubtitleHeader() const;
|
||||
|
||||
int GetSubtitle(AVPacket* pkt, AVSubtitle* sub);
|
||||
int GetSubtitle(AVPacket *pkt, AVSubtitle *sub);
|
||||
|
||||
int GetPacket(AVPacket *pkt);
|
||||
int GetPacket(AVPacket *pkt);
|
||||
|
||||
void Seek(int64_t timestamp);
|
||||
void Seek(int64_t timestamp);
|
||||
|
||||
AVFormatContext* fmt_ctx() const
|
||||
{
|
||||
return fmt_ctx_;
|
||||
}
|
||||
AVFormatContext *fmt_ctx() const
|
||||
{
|
||||
return fmt_ctx_;
|
||||
}
|
||||
|
||||
AVStream* avstream() const
|
||||
{
|
||||
return avstream_;
|
||||
}
|
||||
AVStream *avstream() const
|
||||
{
|
||||
return avstream_;
|
||||
}
|
||||
|
||||
private:
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVCodecContext* codec_ctx_;
|
||||
AVStream* avstream_;
|
||||
AVDictionary* opts_;
|
||||
private:
|
||||
AVFormatContext *fmt_ctx_;
|
||||
AVCodecContext *codec_ctx_;
|
||||
AVStream *avstream_;
|
||||
AVDictionary *opts_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Handle an FFmpeg error code
|
||||
*
|
||||
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
|
||||
@@ -132,54 +140,56 @@ private:
|
||||
*
|
||||
* @param error_code
|
||||
*/
|
||||
static QString FFmpegError(int error_code);
|
||||
static QString FFmpegError(int error_code);
|
||||
|
||||
void FreeScaler();
|
||||
void FreeScaler();
|
||||
|
||||
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
|
||||
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
|
||||
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
|
||||
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
|
||||
|
||||
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
|
||||
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
|
||||
|
||||
static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
|
||||
static const char *
|
||||
GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
|
||||
|
||||
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
|
||||
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
|
||||
|
||||
AVFramePtr GetFrameFromCache(const int64_t &t) const;
|
||||
AVFramePtr GetFrameFromCache(const int64_t &t) const;
|
||||
|
||||
void ClearFrameCache();
|
||||
void ClearFrameCache();
|
||||
|
||||
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
|
||||
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
|
||||
|
||||
TexturePtr ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original);
|
||||
TexturePtr ProcessFrameIntoTexture(AVFramePtr f,
|
||||
const RetrieveVideoParams &p,
|
||||
const AVFramePtr original);
|
||||
|
||||
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
|
||||
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
|
||||
|
||||
void RemoveFirstFrame();
|
||||
void RemoveFirstFrame();
|
||||
|
||||
static int MaximumQueueSize();
|
||||
static int MaximumQueueSize();
|
||||
|
||||
SwsContext *sws_ctx_;
|
||||
int sws_src_width_;
|
||||
int sws_src_height_;
|
||||
AVPixelFormat sws_src_format_;
|
||||
int sws_dst_width_;
|
||||
int sws_dst_height_;
|
||||
AVPixelFormat sws_dst_format_;
|
||||
AVColorRange sws_colrange_;
|
||||
AVColorSpace sws_colspace_;
|
||||
SwsContext *sws_ctx_;
|
||||
int sws_src_width_;
|
||||
int sws_src_height_;
|
||||
AVPixelFormat sws_src_format_;
|
||||
int sws_dst_width_;
|
||||
int sws_dst_height_;
|
||||
AVPixelFormat sws_dst_format_;
|
||||
AVColorRange sws_colrange_;
|
||||
AVColorSpace sws_colspace_;
|
||||
|
||||
AVPacket *working_packet_;
|
||||
AVPacket *working_packet_;
|
||||
|
||||
int64_t second_ts_;
|
||||
int64_t second_ts_;
|
||||
|
||||
std::list<AVFramePtr> cached_frames_;
|
||||
std::list<AVFramePtr> cached_frames_;
|
||||
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
Instance instance_;
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
Instance instance_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+741
-661
File diff suppressed because it is too large
Load Diff
@@ -31,37 +31,41 @@ extern "C" {
|
||||
|
||||
#include "codec/encoder.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class FFmpegEncoder : public Encoder
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class FFmpegEncoder : public Encoder {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FFmpegEncoder(const EncodingParams ¶ms);
|
||||
FFmpegEncoder(const EncodingParams ¶ms);
|
||||
|
||||
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
|
||||
virtual QStringList
|
||||
GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
|
||||
|
||||
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
|
||||
virtual std::vector<SampleFormat>
|
||||
GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
|
||||
|
||||
virtual bool Open() override;
|
||||
virtual bool Open() override;
|
||||
|
||||
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override;
|
||||
virtual bool WriteFrame(olive::FramePtr frame,
|
||||
olive::core::rational time) override;
|
||||
|
||||
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
|
||||
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
|
||||
|
||||
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count);
|
||||
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data,
|
||||
int input_sample_count);
|
||||
|
||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||
|
||||
virtual void Close() override;
|
||||
virtual void Close() override;
|
||||
|
||||
virtual PixelFormat GetDesiredPixelFormat() const override
|
||||
{
|
||||
return video_conversion_fmt_;
|
||||
}
|
||||
virtual PixelFormat GetDesiredPixelFormat() const override
|
||||
{
|
||||
return video_conversion_fmt_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
/**
|
||||
* @brief Handle an FFmpeg error code
|
||||
*
|
||||
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
|
||||
@@ -69,43 +73,48 @@ private:
|
||||
*
|
||||
* @param error_code
|
||||
*/
|
||||
void FFmpegError(const QString &context, int error_code);
|
||||
void FFmpegError(const QString &context, int error_code);
|
||||
|
||||
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream);
|
||||
bool WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
|
||||
AVStream *stream);
|
||||
|
||||
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec);
|
||||
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, const AVCodec* codec);
|
||||
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, const AVCodec *codec);
|
||||
bool InitializeStream(enum AVMediaType type, AVStream **stream,
|
||||
AVCodecContext **codec_ctx,
|
||||
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 FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream);
|
||||
void FlushEncoders();
|
||||
void FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream);
|
||||
|
||||
bool InitializeResampleContext(const AudioParams &audio);
|
||||
bool InitializeResampleContext(const AudioParams &audio);
|
||||
|
||||
static const AVCodec *GetEncoder(ExportCodec::Codec c, SampleFormat aformat);
|
||||
static const AVCodec *GetEncoder(ExportCodec::Codec c,
|
||||
SampleFormat aformat);
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVFormatContext *fmt_ctx_;
|
||||
|
||||
AVStream* video_stream_;
|
||||
AVCodecContext* video_codec_ctx_;
|
||||
AVFilterGraph *video_scale_ctx_;
|
||||
AVFilterContext *video_buffersrc_ctx_;
|
||||
AVFilterContext *video_buffersink_ctx_;
|
||||
PixelFormat video_conversion_fmt_;
|
||||
AVStream *video_stream_;
|
||||
AVCodecContext *video_codec_ctx_;
|
||||
AVFilterGraph *video_scale_ctx_;
|
||||
AVFilterContext *video_buffersrc_ctx_;
|
||||
AVFilterContext *video_buffersink_ctx_;
|
||||
PixelFormat video_conversion_fmt_;
|
||||
|
||||
AVStream* audio_stream_;
|
||||
AVCodecContext* audio_codec_ctx_;
|
||||
SwrContext* audio_resample_ctx_;
|
||||
AVFrame* audio_frame_;
|
||||
int audio_max_samples_;
|
||||
int audio_frame_offset_;
|
||||
int audio_write_count_;
|
||||
AVStream *audio_stream_;
|
||||
AVCodecContext *audio_codec_ctx_;
|
||||
SwrContext *audio_resample_ctx_;
|
||||
AVFrame *audio_frame_;
|
||||
int audio_max_samples_;
|
||||
int audio_frame_offset_;
|
||||
int audio_write_count_;
|
||||
|
||||
AVStream* subtitle_stream_;
|
||||
AVCodecContext* subtitle_codec_ctx_;
|
||||
|
||||
bool open_;
|
||||
AVStream *subtitle_stream_;
|
||||
AVCodecContext *subtitle_codec_ctx_;
|
||||
|
||||
bool open_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+87
-79
@@ -28,153 +28,161 @@
|
||||
#include "common/oiioutils.h"
|
||||
#include "render/framemanager.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
Frame::Frame() :
|
||||
data_(nullptr),
|
||||
data_size_(0),
|
||||
timestamp_(0)
|
||||
Frame::Frame()
|
||||
: data_(nullptr)
|
||||
, data_size_(0)
|
||||
, timestamp_(0)
|
||||
{
|
||||
}
|
||||
|
||||
Frame::~Frame()
|
||||
{
|
||||
destroy();
|
||||
destroy();
|
||||
}
|
||||
|
||||
FramePtr Frame::Create()
|
||||
{
|
||||
return std::make_shared<Frame>();
|
||||
return std::make_shared<Frame>();
|
||||
}
|
||||
|
||||
const VideoParams &Frame::video_params() const
|
||||
{
|
||||
return params_;
|
||||
return params_;
|
||||
}
|
||||
|
||||
void Frame::set_video_params(const VideoParams ¶ms)
|
||||
{
|
||||
params_ = params;
|
||||
params_ = params;
|
||||
|
||||
linesize_ = generate_linesize_bytes(width(), params_.format(), params_.channel_count());
|
||||
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
|
||||
linesize_ = generate_linesize_bytes(width(), params_.format(),
|
||||
params_.channel_count());
|
||||
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
|
||||
}
|
||||
|
||||
FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
|
||||
{
|
||||
if (top->video_params() != bottom->video_params()) {
|
||||
qCritical() << "Tried to interlace two frames that had incompatible parameters";
|
||||
return nullptr;
|
||||
}
|
||||
if (top->video_params() != bottom->video_params()) {
|
||||
qCritical()
|
||||
<< "Tried to interlace two frames that had incompatible parameters";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FramePtr interlaced = Frame::Create();
|
||||
interlaced->set_video_params(top->video_params());
|
||||
interlaced->allocate();
|
||||
FramePtr interlaced = Frame::Create();
|
||||
interlaced->set_video_params(top->video_params());
|
||||
interlaced->allocate();
|
||||
|
||||
int linesize = interlaced->linesize_bytes();
|
||||
int linesize = interlaced->linesize_bytes();
|
||||
|
||||
for (int i=0; i<interlaced->height(); i++) {
|
||||
FramePtr which = (i%2 == 0) ? top : bottom;
|
||||
for (int i = 0; i < interlaced->height(); i++) {
|
||||
FramePtr which = (i % 2 == 0) ? top : bottom;
|
||||
|
||||
memcpy(interlaced->data() + i*linesize,
|
||||
which->const_data() + i*linesize,
|
||||
linesize);
|
||||
}
|
||||
memcpy(interlaced->data() + i * linesize,
|
||||
which->const_data() + i * 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?)
|
||||
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31);
|
||||
// Align to 32 bytes (not sure if this is necessary?)
|
||||
return VideoParams::GetBytesPerPixel(format, channel_count) *
|
||||
((width + 31) & ~31);
|
||||
}
|
||||
|
||||
Color Frame::get_pixel(int x, int y) const
|
||||
{
|
||||
if (!contains_pixel(x, y)) {
|
||||
return Color();
|
||||
}
|
||||
if (!contains_pixel(x, y)) {
|
||||
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
|
||||
{
|
||||
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
|
||||
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
|
||||
}
|
||||
|
||||
void Frame::set_pixel(int x, int y, const Color &c)
|
||||
{
|
||||
if (!contains_pixel(x, y)) {
|
||||
return;
|
||||
}
|
||||
if (!contains_pixel(x, y)) {
|
||||
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()
|
||||
{
|
||||
// Assume this frame is intended to be a video frame
|
||||
if (!params_.is_valid()) {
|
||||
qWarning() << "Tried to allocate a frame with invalid parameters";
|
||||
return false;
|
||||
}
|
||||
// Assume this frame is intended to be a video frame
|
||||
if (!params_.is_valid()) {
|
||||
qWarning() << "Tried to allocate a frame with invalid parameters";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_allocated()) {
|
||||
// Already allocated
|
||||
return true;
|
||||
}
|
||||
if (is_allocated()) {
|
||||
// Already allocated
|
||||
return true;
|
||||
}
|
||||
|
||||
data_size_ = linesize_ * height();
|
||||
data_ = FrameManager::Allocate(data_size_);
|
||||
data_size_ = linesize_ * height();
|
||||
data_ = FrameManager::Allocate(data_size_);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frame::destroy()
|
||||
{
|
||||
if (is_allocated()) {
|
||||
FrameManager::Deallocate(data_size_, data_);
|
||||
if (is_allocated()) {
|
||||
FrameManager::Deallocate(data_size_, data_);
|
||||
|
||||
data_size_ = 0;
|
||||
data_ = nullptr;
|
||||
}
|
||||
data_size_ = 0;
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FramePtr Frame::convert(PixelFormat format) const
|
||||
{
|
||||
// Create new params with destination format
|
||||
VideoParams params = params_;
|
||||
params.set_format(format);
|
||||
// Create new params with destination format
|
||||
VideoParams params = params_;
|
||||
params.set_format(format);
|
||||
|
||||
// Create new frame
|
||||
FramePtr converted = Frame::Create();
|
||||
converted->set_video_params(params);
|
||||
converted->set_timestamp(timestamp_);
|
||||
converted->allocate();
|
||||
// Create new frame
|
||||
FramePtr converted = Frame::Create();
|
||||
converted->set_video_params(params);
|
||||
converted->set_timestamp(timestamp_);
|
||||
converted->allocate();
|
||||
|
||||
// Do the conversion through OIIO for convenience
|
||||
OIIO::ImageBuf src(OIIO::ImageSpec(width(), height(),
|
||||
channel_count(),
|
||||
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
|
||||
// Do the conversion through OIIO for convenience
|
||||
OIIO::ImageBuf src(
|
||||
OIIO::ImageSpec(width(), height(), channel_count(),
|
||||
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
|
||||
|
||||
OIIOUtils::FrameToBuffer(this, &src);
|
||||
OIIOUtils::FrameToBuffer(this, &src);
|
||||
|
||||
OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(),
|
||||
channel_count(),
|
||||
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
|
||||
OIIO::ImageBuf dst(OIIO::ImageSpec(
|
||||
converted->width(), converted->height(), channel_count(),
|
||||
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
|
||||
|
||||
if (dst.copy_pixels(src)) {
|
||||
OIIOUtils::BufferToFrame(&dst, converted.get());
|
||||
return converted;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
if (dst.copy_pixels(src)) {
|
||||
OIIOUtils::BufferToFrame(&dst, converted.get());
|
||||
return converted;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+79
-79
@@ -28,7 +28,8 @@
|
||||
#include "common/define.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class Frame;
|
||||
using FramePtr = std::shared_ptr<Frame>;
|
||||
@@ -36,135 +37,134 @@ using FramePtr = std::shared_ptr<Frame>;
|
||||
/**
|
||||
* @brief Video frame data or audio sample data from a Decoder
|
||||
*/
|
||||
class Frame
|
||||
{
|
||||
class Frame {
|
||||
public:
|
||||
Frame();
|
||||
Frame();
|
||||
|
||||
~Frame();
|
||||
~Frame();
|
||||
|
||||
DISABLE_COPY_MOVE(Frame)
|
||||
DISABLE_COPY_MOVE(Frame)
|
||||
|
||||
static FramePtr Create();
|
||||
static FramePtr Create();
|
||||
|
||||
const VideoParams& video_params() const;
|
||||
void set_video_params(const VideoParams& params);
|
||||
const VideoParams &video_params() const;
|
||||
void set_video_params(const VideoParams ¶ms);
|
||||
|
||||
static FramePtr Interlace(FramePtr top, FramePtr bottom);
|
||||
static FramePtr Interlace(FramePtr top, FramePtr bottom);
|
||||
|
||||
static int generate_linesize_bytes(int width, PixelFormat format, int channel_count);
|
||||
static int generate_linesize_bytes(int width, PixelFormat format,
|
||||
int channel_count);
|
||||
|
||||
int linesize_pixels() const
|
||||
{
|
||||
return linesize_pixels_;
|
||||
}
|
||||
int linesize_pixels() const
|
||||
{
|
||||
return linesize_pixels_;
|
||||
}
|
||||
|
||||
int linesize_bytes() const
|
||||
{
|
||||
return linesize_;
|
||||
}
|
||||
int linesize_bytes() const
|
||||
{
|
||||
return linesize_;
|
||||
}
|
||||
|
||||
int width() const
|
||||
{
|
||||
return params_.effective_width();
|
||||
}
|
||||
int width() const
|
||||
{
|
||||
return params_.effective_width();
|
||||
}
|
||||
|
||||
int height() const
|
||||
{
|
||||
return params_.effective_height();
|
||||
}
|
||||
int height() const
|
||||
{
|
||||
return params_.effective_height();
|
||||
}
|
||||
|
||||
PixelFormat format() const
|
||||
{
|
||||
return params_.format();
|
||||
}
|
||||
PixelFormat format() const
|
||||
{
|
||||
return params_.format();
|
||||
}
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return params_.channel_count();
|
||||
}
|
||||
int channel_count() const
|
||||
{
|
||||
return params_.channel_count();
|
||||
}
|
||||
|
||||
Color get_pixel(int x, int y) const;
|
||||
bool contains_pixel(int x, int y) const;
|
||||
void set_pixel(int x, int y, const Color& c);
|
||||
Color get_pixel(int x, int y) const;
|
||||
bool contains_pixel(int x, int y) const;
|
||||
void set_pixel(int x, int y, const Color &c);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get frame's timestamp.
|
||||
*
|
||||
* This timestamp is always a rational that will equate to the time in seconds.
|
||||
*/
|
||||
const rational& timestamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
const rational ×tamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
void set_timestamp(const rational& timestamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
void set_timestamp(const rational ×tamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get the data buffer of this frame
|
||||
*/
|
||||
char* data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
char *data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get the const data buffer of this frame
|
||||
*/
|
||||
const char* const_data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
const char *const_data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Allocate memory buffer to store data based on parameters
|
||||
*
|
||||
* For video frames, the width(), height(), and format() must be set for this function to work.
|
||||
*
|
||||
* If a memory buffer has been previously allocated without destroying, this function will destroy it.
|
||||
*/
|
||||
bool allocate();
|
||||
bool allocate();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Return whether the frame is allocated or not
|
||||
*/
|
||||
bool is_allocated() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
bool is_allocated() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Destroy a memory buffer allocated with allocate()
|
||||
*/
|
||||
void destroy();
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the size of the array returned in data() in bytes
|
||||
*
|
||||
* Returns 0 if nothing is allocated.
|
||||
*/
|
||||
int allocated_size() const
|
||||
{
|
||||
return data_size_;
|
||||
}
|
||||
int allocated_size() const
|
||||
{
|
||||
return data_size_;
|
||||
}
|
||||
|
||||
FramePtr convert(PixelFormat format) const;
|
||||
FramePtr convert(PixelFormat format) const;
|
||||
|
||||
private:
|
||||
VideoParams params_;
|
||||
VideoParams params_;
|
||||
|
||||
char* data_;
|
||||
int data_size_;
|
||||
char *data_;
|
||||
int data_size_;
|
||||
|
||||
rational timestamp_;
|
||||
rational timestamp_;
|
||||
|
||||
int linesize_;
|
||||
|
||||
int linesize_pixels_;
|
||||
int linesize_;
|
||||
|
||||
int linesize_pixels_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+150
-133
@@ -31,217 +31,234 @@
|
||||
#include "core.h"
|
||||
#include "render/renderer.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
QStringList OIIODecoder::supported_formats_;
|
||||
|
||||
OIIODecoder::OIIODecoder() :
|
||||
image_(nullptr)
|
||||
OIIODecoder::OIIODecoder()
|
||||
: image_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
QString OIIODecoder::id() const
|
||||
{
|
||||
return QStringLiteral("oiio");
|
||||
return QStringLiteral("oiio");
|
||||
}
|
||||
|
||||
FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cancelled) const
|
||||
FootageDescription OIIODecoder::Probe(const QString &filename,
|
||||
CancelAtom *cancelled) const
|
||||
{
|
||||
Q_UNUSED(cancelled)
|
||||
Q_UNUSED(cancelled)
|
||||
|
||||
FootageDescription desc(id());
|
||||
FootageDescription desc(id());
|
||||
|
||||
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
|
||||
// to open a file that it can't if it's given one
|
||||
if (!FileTypeIsSupported(filename)) {
|
||||
return desc;
|
||||
}
|
||||
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
|
||||
// to open a file that it can't if it's given one
|
||||
if (!FileTypeIsSupported(filename)) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
std::string std_filename = filename.toStdString();
|
||||
std::string std_filename = filename.toStdString();
|
||||
|
||||
auto in = OIIO::ImageInput::open(std_filename);
|
||||
auto in = OIIO::ImageInput::open(std_filename);
|
||||
|
||||
if (!in) {
|
||||
return desc;
|
||||
}
|
||||
if (!in) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
|
||||
// it better
|
||||
if (!strcmp(in->format_name(), "FFmpeg movie")) {
|
||||
return desc;
|
||||
}
|
||||
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
|
||||
// it better
|
||||
if (!strcmp(in->format_name(), "FFmpeg movie")) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
bool stream_enabled = true;
|
||||
bool stream_enabled = true;
|
||||
|
||||
int i;
|
||||
for (i=0; in->seek_subimage(i, 0); i++) {
|
||||
OIIO::ImageSpec spec = in->spec();
|
||||
int i;
|
||||
for (i = 0; in->seek_subimage(i, 0); i++) {
|
||||
OIIO::ImageSpec spec = in->spec();
|
||||
|
||||
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
|
||||
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
|
||||
|
||||
video_params.set_stream_index(i);
|
||||
video_params.set_stream_index(i);
|
||||
|
||||
if (i > 1) {
|
||||
// This is a multilayer image and this image might have an offset
|
||||
OIIO::ImageSpec root_spec = in->spec(0);
|
||||
if (i > 1) {
|
||||
// This is a multilayer image and this image might have an offset
|
||||
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_y = spec.y + float(spec.height)*0.5f - float(root_spec.height)*0.5f;
|
||||
float norm_x = spec.x + float(spec.width) * 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_y(norm_y);
|
||||
}
|
||||
video_params.set_x(norm_x);
|
||||
video_params.set_y(norm_y);
|
||||
}
|
||||
|
||||
// By default, only enable the first subimage (presumably the combined image). Later we will
|
||||
// ask the user if they want to enable the layers instead.
|
||||
video_params.set_enabled(stream_enabled);
|
||||
stream_enabled = false;
|
||||
// By default, only enable the first subimage (presumably the combined image). Later we will
|
||||
// ask the user if they want to enable the layers instead.
|
||||
video_params.set_enabled(stream_enabled);
|
||||
stream_enabled = false;
|
||||
|
||||
// OIIO automatically premultiplies alpha
|
||||
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
|
||||
// likely reduces the fidelity?
|
||||
video_params.set_premultiplied_alpha(true);
|
||||
// OIIO automatically premultiplies alpha
|
||||
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
|
||||
// likely reduces the fidelity?
|
||||
video_params.set_premultiplied_alpha(true);
|
||||
|
||||
desc.AddVideoStream(video_params);
|
||||
}
|
||||
desc.AddVideoStream(video_params);
|
||||
}
|
||||
|
||||
desc.SetStreamCount(i);
|
||||
desc.SetStreamCount(i);
|
||||
|
||||
// If we're here, we have a successful image open
|
||||
in->close();
|
||||
// If we're here, we have a successful image open
|
||||
in->close();
|
||||
|
||||
return desc;
|
||||
return desc;
|
||||
}
|
||||
|
||||
bool OIIODecoder::OpenInternal()
|
||||
{
|
||||
// If we can open the filename provided, assume everything is working
|
||||
return OpenImageHandler(stream().filename(), stream().stream());
|
||||
// If we can open the filename provided, assume everything is working
|
||||
return OpenImageHandler(stream().filename(), stream().stream());
|
||||
}
|
||||
|
||||
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||
{
|
||||
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
|
||||
vp.set_divider(p.divider);
|
||||
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
|
||||
vp.set_divider(p.divider);
|
||||
|
||||
if (!buffer_.is_allocated()
|
||||
|| last_params_.divider != p.divider) {
|
||||
last_params_ = p;
|
||||
if (!buffer_.is_allocated() || last_params_.divider != p.divider) {
|
||||
last_params_ = p;
|
||||
|
||||
buffer_.destroy();
|
||||
buffer_.set_video_params(vp);
|
||||
buffer_.allocate();
|
||||
buffer_.destroy();
|
||||
buffer_.set_video_params(vp);
|
||||
buffer_.allocate();
|
||||
|
||||
if (p.divider == 1) {
|
||||
// Just upload straight to the buffer
|
||||
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
|
||||
} else {
|
||||
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());
|
||||
if (p.divider == 1) {
|
||||
// Just upload straight to the buffer
|
||||
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
|
||||
} else {
|
||||
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());
|
||||
|
||||
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
|
||||
int px_sz = vp.GetBytesPerPixel();
|
||||
for (int dst_y=0; dst_y<buffer_.height(); dst_y++) {
|
||||
int src_y = dst_y * buf.spec().height / buffer_.height();
|
||||
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
|
||||
int px_sz = vp.GetBytesPerPixel();
|
||||
for (int dst_y = 0; dst_y < buffer_.height(); dst_y++) {
|
||||
int src_y = dst_y * buf.spec().height / buffer_.height();
|
||||
|
||||
for (int dst_x=0; dst_x<buffer_.width(); dst_x++) {
|
||||
int src_x = dst_x * buf.spec().width / buffer_.width();
|
||||
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x,
|
||||
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x,
|
||||
px_sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) {
|
||||
int src_x = dst_x * buf.spec().width / buffer_.width();
|
||||
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y +
|
||||
px_sz * dst_x,
|
||||
static_cast<uint8_t *>(buf.localpixels()) +
|
||||
buf.scanline_stride() * src_y + px_sz * src_x,
|
||||
px_sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
|
||||
return p.renderer->CreateTexture(vp, buffer_.data(),
|
||||
buffer_.linesize_pixels());
|
||||
}
|
||||
|
||||
void OIIODecoder::CloseInternal()
|
||||
{
|
||||
CloseImageHandle();
|
||||
CloseImageHandle();
|
||||
}
|
||||
|
||||
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
|
||||
bool OIIODecoder::FileTypeIsSupported(const QString &fn)
|
||||
{
|
||||
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
|
||||
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
|
||||
// "extension_list" attribute and match it with the extension of the file.
|
||||
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
|
||||
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
|
||||
// "extension_list" attribute and match it with the extension of the file.
|
||||
|
||||
// Check if we've created the supported formats list, create it if not
|
||||
if (supported_formats_.isEmpty()) {
|
||||
QStringList extension_list = QString::fromStdString(OIIO::get_string_attribute("extension_list")).split(';');
|
||||
// Check if we've created the supported formats list, create it if not
|
||||
if (supported_formats_.isEmpty()) {
|
||||
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
|
||||
foreach (const QString& ext, extension_list) {
|
||||
QStringList format_and_ext = ext.split(':');
|
||||
// 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) {
|
||||
QStringList format_and_ext = ext.split(':');
|
||||
|
||||
supported_formats_.append(format_and_ext.at(1).split(','));
|
||||
}
|
||||
}
|
||||
supported_formats_.append(format_and_ext.at(1).split(','));
|
||||
}
|
||||
}
|
||||
|
||||
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
if (!supported_formats_.contains(QFileInfo(fn).suffix(),
|
||||
Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
|
||||
{
|
||||
image_ = OIIO::ImageInput::open(fn.toStdString());
|
||||
image_ = OIIO::ImageInput::open(fn.toStdString());
|
||||
|
||||
if (!image_) {
|
||||
return false;
|
||||
}
|
||||
if (!image_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!image_->seek_subimage(subimage, 0)) {
|
||||
return false;
|
||||
}
|
||||
if (!image_->seek_subimage(subimage, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we can work with this pixel format
|
||||
const OIIO::ImageSpec& spec = image_->spec();
|
||||
// Check if we can work with this pixel format
|
||||
const OIIO::ImageSpec &spec = image_->spec();
|
||||
|
||||
// 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));
|
||||
// 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));
|
||||
|
||||
if (pix_fmt_ == PixelFormat::INVALID) {
|
||||
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
|
||||
return false;
|
||||
}
|
||||
if (pix_fmt_ == PixelFormat::INVALID) {
|
||||
qWarning()
|
||||
<< "Failed to convert OIIO::ImageDesc to native pixel format";
|
||||
return false;
|
||||
}
|
||||
|
||||
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
|
||||
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
|
||||
|
||||
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
|
||||
qCritical() << "Failed to determine appropriate OIIO basetype from native format";
|
||||
return false;
|
||||
}
|
||||
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
|
||||
qCritical()
|
||||
<< "Failed to determine appropriate OIIO basetype from native format";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OIIODecoder::CloseImageHandle()
|
||||
{
|
||||
if (image_) {
|
||||
image_->close();
|
||||
image_ = nullptr;
|
||||
}
|
||||
if (image_) {
|
||||
image_->close();
|
||||
image_ = nullptr;
|
||||
}
|
||||
|
||||
buffer_.destroy();
|
||||
buffer_.destroy();
|
||||
}
|
||||
|
||||
VideoParams OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
|
||||
VideoParams
|
||||
OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
|
||||
{
|
||||
VideoParams video_params;
|
||||
VideoParams video_params;
|
||||
|
||||
video_params.set_width(spec.width);
|
||||
video_params.set_height(spec.height);
|
||||
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
|
||||
video_params.set_channel_count(spec.nchannels);
|
||||
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
|
||||
video_params.set_video_type(VideoParams::kVideoTypeStill);
|
||||
video_params.set_width(spec.width);
|
||||
video_params.set_height(spec.height);
|
||||
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(
|
||||
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
|
||||
video_params.set_channel_count(spec.nchannels);
|
||||
video_params.set_pixel_aspect_ratio(
|
||||
OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
|
||||
video_params.set_video_type(VideoParams::kVideoTypeStill);
|
||||
|
||||
return video_params;
|
||||
return video_params;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,46 +26,50 @@
|
||||
|
||||
#include "codec/decoder.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class OIIODecoder : public Decoder
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class OIIODecoder : public Decoder {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OIIODecoder();
|
||||
OIIODecoder();
|
||||
|
||||
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
|
||||
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
|
||||
|
||||
virtual QString id() const override;
|
||||
virtual QString id() const override;
|
||||
|
||||
virtual bool SupportsVideo() override{return true;}
|
||||
virtual bool SupportsVideo() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const override;
|
||||
virtual FootageDescription Probe(const QString &filename,
|
||||
CancelAtom *cancelled) const override;
|
||||
|
||||
protected:
|
||||
virtual bool OpenInternal() override;
|
||||
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
|
||||
virtual void CloseInternal() override;
|
||||
virtual bool OpenInternal() override;
|
||||
virtual TexturePtr
|
||||
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<OIIO::ImageInput> image_;
|
||||
std::unique_ptr<OIIO::ImageInput> image_;
|
||||
|
||||
static bool FileTypeIsSupported(const QString& fn);
|
||||
static bool FileTypeIsSupported(const QString &fn);
|
||||
|
||||
bool OpenImageHandler(const QString& fn, int subimage);
|
||||
bool OpenImageHandler(const QString &fn, int subimage);
|
||||
|
||||
void CloseImageHandle();
|
||||
void CloseImageHandle();
|
||||
|
||||
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
|
||||
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
|
||||
|
||||
PixelFormat pix_fmt_;
|
||||
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
|
||||
PixelFormat pix_fmt_;
|
||||
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
|
||||
|
||||
Frame buffer_;
|
||||
RetrieveVideoParams last_params_;
|
||||
|
||||
static QStringList supported_formats_;
|
||||
Frame buffer_;
|
||||
RetrieveVideoParams last_params_;
|
||||
|
||||
static QStringList supported_formats_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -22,60 +22,62 @@
|
||||
|
||||
#include "common/oiioutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
OIIOEncoder::OIIOEncoder(const EncodingParams ¶ms) :
|
||||
Encoder(params)
|
||||
namespace olive
|
||||
{
|
||||
|
||||
OIIOEncoder::OIIOEncoder(const EncodingParams ¶ms)
|
||||
: Encoder(params)
|
||||
{
|
||||
}
|
||||
|
||||
bool OIIOEncoder::Open()
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
|
||||
{
|
||||
std::string filename = GetFilenameForFrame(time).toStdString();
|
||||
std::string filename = GetFilenameForFrame(time).toStdString();
|
||||
|
||||
auto output = OIIO::ImageOutput::create(filename);
|
||||
if (!output) {
|
||||
return false;
|
||||
}
|
||||
auto output = OIIO::ImageOutput::create(filename);
|
||||
if (!output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
|
||||
OIIO::ImageSpec spec(frame->width(), frame->height(), frame->channel_count(), type);
|
||||
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
|
||||
OIIO::ImageSpec spec(frame->width(), frame->height(),
|
||||
frame->channel_count(), type);
|
||||
|
||||
if (!output->open(filename, spec)) {
|
||||
return false;
|
||||
}
|
||||
if (!output->open(filename, spec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!output->write_image(type, frame->data(), OIIO::AutoStride, frame->linesize_bytes())) {
|
||||
return false;
|
||||
}
|
||||
if (!output->write_image(type, frame->data(), OIIO::AutoStride,
|
||||
frame->linesize_bytes())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!output->close()) {
|
||||
return false;
|
||||
}
|
||||
if (!output->close()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
|
||||
{
|
||||
// Do nothing
|
||||
return false;
|
||||
// Do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void OIIOEncoder::Close()
|
||||
{
|
||||
// Do nothing
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,23 +23,23 @@
|
||||
|
||||
#include "codec/encoder.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class OIIOEncoder : public Encoder
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class OIIOEncoder : public Encoder {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OIIOEncoder(const EncodingParams ¶ms);
|
||||
OIIOEncoder(const EncodingParams ¶ms);
|
||||
|
||||
public slots:
|
||||
virtual bool Open() override;
|
||||
virtual bool Open() override;
|
||||
|
||||
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override;
|
||||
virtual bool WriteAudio(const SampleBuffer &audio) override;
|
||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||
|
||||
virtual void Close() override;
|
||||
virtual bool WriteFrame(olive::FramePtr frame,
|
||||
olive::core::rational time) override;
|
||||
virtual bool WriteAudio(const SampleBuffer &audio) override;
|
||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||
|
||||
virtual void Close() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,100 +20,103 @@
|
||||
|
||||
#include "planarfiledevice.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
PlanarFileDevice::PlanarFileDevice(QObject *parent) :
|
||||
QObject(parent)
|
||||
namespace olive
|
||||
{
|
||||
|
||||
PlanarFileDevice::PlanarFileDevice(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
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()) {
|
||||
// Already open
|
||||
return false;
|
||||
}
|
||||
if (isOpen()) {
|
||||
// Already open
|
||||
return false;
|
||||
}
|
||||
|
||||
files_.resize(filenames.size());
|
||||
files_.fill(nullptr);
|
||||
files_.resize(filenames.size());
|
||||
files_.fill(nullptr);
|
||||
|
||||
for (int i=0; i<files_.size(); i++) {
|
||||
files_[i] = new QFile(filenames.at(i));
|
||||
if (!files_[i]->open(mode)) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < files_.size(); i++) {
|
||||
files_[i] = new QFile(filenames.at(i));
|
||||
if (!files_[i]->open(mode)) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offset)
|
||||
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel,
|
||||
qint64 offset)
|
||||
{
|
||||
qint64 ret = -1;
|
||||
qint64 ret = -1;
|
||||
|
||||
if (isOpen()) {
|
||||
for (int i=0; i<files_.size(); i++) {
|
||||
// Kind of clunky but should be largely fine
|
||||
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
for (int i = 0; i < files_.size(); i++) {
|
||||
// Kind of clunky but should be largely fine
|
||||
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel, qint64 offset)
|
||||
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel,
|
||||
qint64 offset)
|
||||
{
|
||||
qint64 ret = -1;
|
||||
qint64 ret = -1;
|
||||
|
||||
if (isOpen()) {
|
||||
for (int i=0; i<files_.size(); i++) {
|
||||
// Kind of clunky but should be largely fine
|
||||
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
for (int i = 0; i < files_.size(); i++) {
|
||||
// Kind of clunky but should be largely fine
|
||||
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
qint64 PlanarFileDevice::size() const
|
||||
{
|
||||
if (isOpen()) {
|
||||
return files_.first()->size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if (isOpen()) {
|
||||
return files_.first()->size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool PlanarFileDevice::seek(qint64 pos)
|
||||
{
|
||||
bool ret = true;
|
||||
bool ret = true;
|
||||
|
||||
for (int i=0; i<files_.size(); i++) {
|
||||
ret = files_[i]->seek(pos) & ret;
|
||||
}
|
||||
for (int i = 0; i < files_.size(); i++) {
|
||||
ret = files_[i]->seek(pos) & ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PlanarFileDevice::close()
|
||||
{
|
||||
for (int i=0; i<files_.size(); i++) {
|
||||
QFile *f = files_.at(i);
|
||||
if (f) {
|
||||
if (f->isOpen()) {
|
||||
f->close();
|
||||
}
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
files_.clear();
|
||||
for (int i = 0; i < files_.size(); i++) {
|
||||
QFile *f = files_.at(i);
|
||||
if (f) {
|
||||
if (f->isOpen()) {
|
||||
f->close();
|
||||
}
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
files_.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,38 +25,38 @@
|
||||
#include <QFile>
|
||||
#include <QObject>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
class PlanarFileDevice : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class PlanarFileDevice : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PlanarFileDevice(QObject *parent = nullptr);
|
||||
PlanarFileDevice(QObject *parent = nullptr);
|
||||
|
||||
virtual ~PlanarFileDevice() override;
|
||||
virtual ~PlanarFileDevice() override;
|
||||
|
||||
bool isOpen() const
|
||||
{
|
||||
return !files_.isEmpty();
|
||||
}
|
||||
bool isOpen() const
|
||||
{
|
||||
return !files_.isEmpty();
|
||||
}
|
||||
|
||||
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
|
||||
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
|
||||
|
||||
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
|
||||
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
|
||||
|
||||
qint64 write(const char **data, qint64 bytes_per_channel, qint64 offset = 0);
|
||||
qint64 write(const char **data, qint64 bytes_per_channel,
|
||||
qint64 offset = 0);
|
||||
|
||||
qint64 size() const;
|
||||
qint64 size() const;
|
||||
|
||||
bool seek(qint64 pos);
|
||||
bool seek(qint64 pos);
|
||||
|
||||
void close();
|
||||
void close();
|
||||
|
||||
private:
|
||||
QVector<QFile*> files_;
|
||||
|
||||
QVector<QFile *> files_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -23,15 +23,12 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class AutoScroll {
|
||||
public:
|
||||
enum Method {
|
||||
kNone,
|
||||
kPage,
|
||||
kSmooth
|
||||
};
|
||||
enum Method { kNone, kPage, kSmooth };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -24,36 +24,38 @@
|
||||
#include "common/define.h"
|
||||
#include "render/cancelatom.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CancelableObject {
|
||||
public:
|
||||
CancelableObject()
|
||||
{
|
||||
}
|
||||
CancelableObject()
|
||||
{
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
{
|
||||
cancel_.Cancel();
|
||||
CancelEvent();
|
||||
}
|
||||
void Cancel()
|
||||
{
|
||||
cancel_.Cancel();
|
||||
CancelEvent();
|
||||
}
|
||||
|
||||
CancelAtom *GetCancelAtom()
|
||||
{
|
||||
return &cancel_;
|
||||
}
|
||||
CancelAtom *GetCancelAtom()
|
||||
{
|
||||
return &cancel_;
|
||||
}
|
||||
|
||||
bool IsCancelled()
|
||||
{
|
||||
return cancel_.IsCancelled();
|
||||
}
|
||||
bool IsCancelled()
|
||||
{
|
||||
return cancel_.IsCancelled();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void CancelEvent(){}
|
||||
virtual void CancelEvent()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
CancelAtom cancel_;
|
||||
|
||||
CancelAtom cancel_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,146 +25,154 @@
|
||||
|
||||
CommandLineParser::~CommandLineParser()
|
||||
{
|
||||
foreach (const KnownOption& o, options_) {
|
||||
delete o.option;
|
||||
}
|
||||
foreach (const KnownOption &o, options_) {
|
||||
delete o.option;
|
||||
}
|
||||
|
||||
foreach (const KnownPositionalArgument& a, positional_args_) {
|
||||
delete a.option;
|
||||
}
|
||||
foreach (const KnownPositionalArgument &a, positional_args_) {
|
||||
delete a.option;
|
||||
}
|
||||
}
|
||||
|
||||
const CommandLineParser::Option *CommandLineParser::AddOption(const QStringList &strings, const QString &description, bool takes_arg, const QString &arg_placeholder, bool hidden)
|
||||
const CommandLineParser::Option *
|
||||
CommandLineParser::AddOption(const QStringList &strings,
|
||||
const QString &description, bool takes_arg,
|
||||
const QString &arg_placeholder, bool hidden)
|
||||
{
|
||||
Option* o = new Option();
|
||||
Option *o = new Option();
|
||||
|
||||
options_.append({strings, description, o, takes_arg, arg_placeholder, hidden});
|
||||
options_.append(
|
||||
{ strings, description, o, takes_arg, arg_placeholder, hidden });
|
||||
|
||||
return o;
|
||||
return o;
|
||||
}
|
||||
|
||||
const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArgument(const QString &name, const QString &description, bool required)
|
||||
const CommandLineParser::PositionalArgument *
|
||||
CommandLineParser::AddPositionalArgument(const QString &name,
|
||||
const QString &description,
|
||||
bool required)
|
||||
{
|
||||
PositionalArgument* a = new PositionalArgument();
|
||||
PositionalArgument *a = new PositionalArgument();
|
||||
|
||||
positional_args_.append({name, description, a, required});
|
||||
positional_args_.append({ name, description, a, required });
|
||||
|
||||
return a;
|
||||
return a;
|
||||
}
|
||||
|
||||
void CommandLineParser::Process(const QVector<QString> &argv)
|
||||
{
|
||||
int positional_index = 0;
|
||||
int positional_index = 0;
|
||||
|
||||
for (int i=1; i<argv.size(); i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
// Must be an option
|
||||
for (int i = 1; i < argv.size(); i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
// Must be an option
|
||||
|
||||
// Skip past first dashes
|
||||
QString arg_basename = argv[i].mid(1);
|
||||
// Skip past first dashes
|
||||
QString arg_basename = argv[i].mid(1);
|
||||
|
||||
bool matched_known = false;
|
||||
bool matched_known = false;
|
||||
|
||||
for (int j=0; j<options_.size(); j++) {
|
||||
KnownOption& o = options_[j];
|
||||
for (int j = 0; j < options_.size(); j++) {
|
||||
KnownOption &o = options_[j];
|
||||
|
||||
foreach (const QString& s, o.args) {
|
||||
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
|
||||
// Flag discovered!
|
||||
o.option->Set();
|
||||
foreach (const QString &s, o.args) {
|
||||
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
|
||||
// Flag discovered!
|
||||
o.option->Set();
|
||||
|
||||
if (o.takes_arg && i+1 < argv.size()) {
|
||||
o.option->SetSetting(argv[i+1]);
|
||||
i++;
|
||||
}
|
||||
if (o.takes_arg && i + 1 < argv.size()) {
|
||||
o.option->SetSetting(argv[i + 1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
matched_known = true;
|
||||
goto found_flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
matched_known = true;
|
||||
goto found_flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_flag:
|
||||
if (!matched_known) {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
if (!matched_known) {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
|
||||
} else {
|
||||
// Must be a positional flag
|
||||
if (positional_index < positional_args_.size()) {
|
||||
positional_args_[positional_index].option->SetSetting(argv[i]);
|
||||
positional_index++;
|
||||
} else {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Must be a positional flag
|
||||
if (positional_index < positional_args_.size()) {
|
||||
positional_args_[positional_index].option->SetSetting(argv[i]);
|
||||
positional_index++;
|
||||
} else {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLineParser::PrintHelp(const char* filename)
|
||||
void CommandLineParser::PrintHelp(const char *filename)
|
||||
{
|
||||
printf("%s %s\n",
|
||||
QCoreApplication::applicationName().toUtf8().constData(),
|
||||
QCoreApplication::applicationVersion().toUtf8().constData());
|
||||
printf("%s %s\n", QCoreApplication::applicationName().toUtf8().constData(),
|
||||
QCoreApplication::applicationVersion().toUtf8().constData());
|
||||
|
||||
printf("Copyright (C) 2018-2022 Olive Team\n");
|
||||
printf("Copyright (C) 2018-2022 Olive Team\n");
|
||||
|
||||
QString positional_args;
|
||||
for (int i=0; i<positional_args_.size(); i++) {
|
||||
if (i > 0) {
|
||||
positional_args.append(' ');
|
||||
}
|
||||
QString positional_args;
|
||||
for (int i = 0; i < positional_args_.size(); i++) {
|
||||
if (i > 0) {
|
||||
positional_args.append(' ');
|
||||
}
|
||||
|
||||
positional_args.append('[');
|
||||
positional_args.append(positional_args_.at(i).name);
|
||||
positional_args.append(']');
|
||||
}
|
||||
positional_args.append('[');
|
||||
positional_args.append(positional_args_.at(i).name);
|
||||
positional_args.append(']');
|
||||
}
|
||||
|
||||
const char* basename;
|
||||
const char *basename;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
basename = strrchr(filename, '\\');
|
||||
if (!basename) {
|
||||
basename = strrchr(filename, '/');
|
||||
}
|
||||
basename = strrchr(filename, '\\');
|
||||
if (!basename) {
|
||||
basename = strrchr(filename, '/');
|
||||
}
|
||||
#else
|
||||
basename = strrchr(filename, '/');
|
||||
basename = strrchr(filename, '/');
|
||||
#endif
|
||||
|
||||
if (basename) {
|
||||
// Slash found, increment pointer to avoid showing the slash itself
|
||||
basename++;
|
||||
} else {
|
||||
// If no slashes are found, assume string is already a basename
|
||||
basename = filename;
|
||||
}
|
||||
if (basename) {
|
||||
// Slash found, increment pointer to avoid showing the slash itself
|
||||
basename++;
|
||||
} else {
|
||||
// If no slashes are found, assume string is already a basename
|
||||
basename = filename;
|
||||
}
|
||||
|
||||
printf("Usage: %s [options] %s\n\n", basename, positional_args.toUtf8().constData());
|
||||
foreach (const KnownOption& o, options_) {
|
||||
if (o.hidden) {
|
||||
continue;
|
||||
}
|
||||
printf("Usage: %s [options] %s\n\n", basename,
|
||||
positional_args.toUtf8().constData());
|
||||
foreach (const KnownOption &o, options_) {
|
||||
if (o.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString all_args;
|
||||
QString all_args;
|
||||
|
||||
for (int i=0; i<o.args.size(); i++) {
|
||||
if (i > 0) {
|
||||
all_args.append(QStringLiteral(", "));
|
||||
}
|
||||
for (int i = 0; i < o.args.size(); i++) {
|
||||
if (i > 0) {
|
||||
all_args.append(QStringLiteral(", "));
|
||||
}
|
||||
|
||||
const QString& this_arg = o.args.at(i);
|
||||
const QString &this_arg = o.args.at(i);
|
||||
|
||||
all_args.append('-');
|
||||
all_args.append(this_arg);
|
||||
}
|
||||
all_args.append('-');
|
||||
all_args.append(this_arg);
|
||||
}
|
||||
|
||||
if (o.arg_placeholder.isEmpty()) {
|
||||
printf(" %s\n", all_args.toUtf8().constData());
|
||||
} else {
|
||||
printf(" %s <%s>\n", all_args.toUtf8().constData(), o.arg_placeholder.toUtf8().constData());
|
||||
}
|
||||
if (o.arg_placeholder.isEmpty()) {
|
||||
printf(" %s\n", all_args.toUtf8().constData());
|
||||
} else {
|
||||
printf(" %s <%s>\n", all_args.toUtf8().constData(),
|
||||
o.arg_placeholder.toUtf8().constData());
|
||||
}
|
||||
|
||||
printf(" %s\n\n", o.description.toUtf8().constData());
|
||||
}
|
||||
printf(" %s\n\n", o.description.toUtf8().constData());
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@@ -36,86 +36,86 @@
|
||||
* the user as a command line argument. Therefore we needed a custom implementation that could
|
||||
* parse arguments without the need for a Q(Core)Application to be present already.
|
||||
*/
|
||||
class CommandLineParser
|
||||
{
|
||||
class CommandLineParser {
|
||||
public:
|
||||
~CommandLineParser();
|
||||
~CommandLineParser();
|
||||
|
||||
DISABLE_COPY_MOVE(CommandLineParser)
|
||||
DISABLE_COPY_MOVE(CommandLineParser)
|
||||
|
||||
class PositionalArgument
|
||||
{
|
||||
public:
|
||||
PositionalArgument() = default;
|
||||
class PositionalArgument {
|
||||
public:
|
||||
PositionalArgument() = default;
|
||||
|
||||
const QString& GetSetting() const
|
||||
{
|
||||
return setting_;
|
||||
}
|
||||
const QString &GetSetting() const
|
||||
{
|
||||
return setting_;
|
||||
}
|
||||
|
||||
void SetSetting(const QString& s)
|
||||
{
|
||||
setting_ = s;
|
||||
}
|
||||
void SetSetting(const QString &s)
|
||||
{
|
||||
setting_ = s;
|
||||
}
|
||||
|
||||
private:
|
||||
QString setting_;
|
||||
private:
|
||||
QString setting_;
|
||||
};
|
||||
|
||||
};
|
||||
class Option : public PositionalArgument {
|
||||
public:
|
||||
Option()
|
||||
{
|
||||
is_set_ = false;
|
||||
}
|
||||
|
||||
class Option : public PositionalArgument {
|
||||
public:
|
||||
Option()
|
||||
{
|
||||
is_set_ = false;
|
||||
}
|
||||
bool IsSet() const
|
||||
{
|
||||
return is_set_;
|
||||
}
|
||||
|
||||
bool IsSet() const
|
||||
{
|
||||
return is_set_;
|
||||
}
|
||||
void Set()
|
||||
{
|
||||
is_set_ = true;
|
||||
}
|
||||
|
||||
void Set()
|
||||
{
|
||||
is_set_ = true;
|
||||
}
|
||||
private:
|
||||
bool is_set_;
|
||||
};
|
||||
|
||||
private:
|
||||
bool is_set_;
|
||||
CommandLineParser() = default;
|
||||
|
||||
};
|
||||
const Option *AddOption(const QStringList &strings,
|
||||
const QString &description, bool takes_arg = false,
|
||||
const QString &arg_placeholder = QString(),
|
||||
bool hidden = false);
|
||||
|
||||
CommandLineParser() = default;
|
||||
const PositionalArgument *AddPositionalArgument(const QString &name,
|
||||
const QString &description,
|
||||
bool required = false);
|
||||
|
||||
const Option* AddOption(const QStringList& strings, const QString& description, bool takes_arg = false, const QString& arg_placeholder = QString(), bool hidden = false);
|
||||
void Process(const QVector<QString> &argv);
|
||||
|
||||
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false);
|
||||
|
||||
void Process(const QVector<QString> &argv);
|
||||
|
||||
void PrintHelp(const char* filename);
|
||||
void PrintHelp(const char *filename);
|
||||
|
||||
private:
|
||||
struct KnownOption {
|
||||
QStringList args;
|
||||
QString description;
|
||||
Option* option;
|
||||
bool takes_arg;
|
||||
QString arg_placeholder;
|
||||
bool hidden;
|
||||
};
|
||||
struct KnownOption {
|
||||
QStringList args;
|
||||
QString description;
|
||||
Option *option;
|
||||
bool takes_arg;
|
||||
QString arg_placeholder;
|
||||
bool hidden;
|
||||
};
|
||||
|
||||
struct KnownPositionalArgument {
|
||||
QString name;
|
||||
QString description;
|
||||
PositionalArgument* option;
|
||||
bool required;
|
||||
};
|
||||
struct KnownPositionalArgument {
|
||||
QString name;
|
||||
QString description;
|
||||
PositionalArgument *option;
|
||||
bool required;
|
||||
};
|
||||
|
||||
QVector<KnownOption> options_;
|
||||
|
||||
QVector<KnownPositionalArgument> positional_args_;
|
||||
QVector<KnownOption> options_;
|
||||
|
||||
QVector<KnownPositionalArgument> positional_args_;
|
||||
};
|
||||
|
||||
#endif // COMMANDLINEPARSER_H
|
||||
|
||||
@@ -40,52 +40,63 @@ crashpad::CrashpadClient *client;
|
||||
|
||||
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
|
||||
QString handler_abs_path = QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
|
||||
// Generate absolute path
|
||||
QString handler_abs_path =
|
||||
QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
|
||||
|
||||
bool status = false;
|
||||
bool status = false;
|
||||
|
||||
if (QFileInfo::exists(handler_abs_path)) {
|
||||
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
|
||||
if (QFileInfo::exists(handler_abs_path)) {
|
||||
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
|
||||
|
||||
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
|
||||
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
|
||||
|
||||
base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics"))));
|
||||
base::FilePath metrics_dir(
|
||||
QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath())
|
||||
.filePath(QStringLiteral("metrics"))));
|
||||
|
||||
// Metadata that will be posted to the server with the crash report map
|
||||
std::map<std::string, std::string> annotations;
|
||||
// Metadata that will be posted to the server with the crash report map
|
||||
std::map<std::string, std::string> annotations;
|
||||
|
||||
// Disable crashpad rate limiting so that all crashes have dmp files
|
||||
std::vector<std::string> arguments;
|
||||
arguments.push_back("--no-rate-limit");
|
||||
arguments.push_back("--no-upload-gzip");
|
||||
// Disable crashpad rate limiting so that all crashes have dmp files
|
||||
std::vector<std::string> arguments;
|
||||
arguments.push_back("--no-rate-limit");
|
||||
arguments.push_back("--no-upload-gzip");
|
||||
|
||||
// Initialize Crashpad database
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database = crashpad::CrashReportDatabase::Initialize(reports_dir);
|
||||
if (database == NULL) return false;
|
||||
// Initialize Crashpad database
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database =
|
||||
crashpad::CrashReportDatabase::Initialize(reports_dir);
|
||||
if (database == NULL)
|
||||
return false;
|
||||
|
||||
// Disable automated crash uploads
|
||||
crashpad::Settings *settings = database->GetSettings();
|
||||
if (settings == NULL) return false;
|
||||
settings->SetUploadsEnabled(false);
|
||||
// Disable automated crash uploads
|
||||
crashpad::Settings *settings = database->GetSettings();
|
||||
if (settings == NULL)
|
||||
return false;
|
||||
settings->SetUploadsEnabled(false);
|
||||
|
||||
// Start crash handler
|
||||
client = new crashpad::CrashpadClient();
|
||||
status = client->StartHandler(handler, reports_dir, metrics_dir,
|
||||
"https://olivevideoeditor.org/crashpad/report.php",
|
||||
annotations, arguments, true, true);
|
||||
}
|
||||
// Start crash handler
|
||||
client = new crashpad::CrashpadClient();
|
||||
status = client->StartHandler(
|
||||
handler, reports_dir, metrics_dir,
|
||||
"https://olivevideoeditor.org/crashpad/report.php", annotations,
|
||||
arguments, true, true);
|
||||
}
|
||||
|
||||
// Override Crashpad exception filter with our own
|
||||
if (!status) {
|
||||
qWarning() << "Failed to start Crashpad, automatic crash reporting will be disabled";
|
||||
}
|
||||
// Override Crashpad exception filter with our own
|
||||
if (!status) {
|
||||
qWarning()
|
||||
<< "Failed to start Crashpad, automatic crash reporting will be disabled";
|
||||
}
|
||||
|
||||
return status;
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif // USE_CRASHPAD
|
||||
|
||||
@@ -35,6 +35,6 @@
|
||||
// arrays encoded in UTF-16.
|
||||
#define QSTRING_TO_BASE_STRING(x) x.toStdWString()
|
||||
#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x)
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
#endif // CRASHPADUTILS_H
|
||||
|
||||
+29
-27
@@ -20,38 +20,40 @@
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
namespace olive
|
||||
{
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
|
||||
const char* msg_type = "UNKNOWN";
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
msg_type = "DEBUG";
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
msg_type = "INFO";
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
msg_type = "WARNING";
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
msg_type = "ERROR";
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
msg_type = "FATAL";
|
||||
break;
|
||||
}
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
|
||||
const QString &msg)
|
||||
{
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
|
||||
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
|
||||
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
|
||||
const char *msg_type = "UNKNOWN";
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
msg_type = "DEBUG";
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
msg_type = "INFO";
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
msg_type = "WARNING";
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
msg_type = "ERROR";
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
msg_type = "FATAL";
|
||||
break;
|
||||
}
|
||||
|
||||
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
|
||||
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
|
||||
// is flushed
|
||||
fflush(stderr);
|
||||
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
|
||||
// is flushed
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -25,9 +25,11 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
|
||||
const QString &msg);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+54
-55
@@ -26,77 +26,76 @@
|
||||
|
||||
//#define ALLOW_RETURNING_INFINITY
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Decibel
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class Decibel {
|
||||
public:
|
||||
// In basically all circumstances, this should calculate to 0.0 linear
|
||||
static constexpr double MINIMUM = -200.0;
|
||||
// In basically all circumstances, this should calculate to 0.0 linear
|
||||
static constexpr double MINIMUM = -200.0;
|
||||
|
||||
static double fromLinear(double linear)
|
||||
{
|
||||
double v = double(20.0) * std::log10(linear);
|
||||
static double fromLinear(double linear)
|
||||
{
|
||||
double v = double(20.0) * std::log10(linear);
|
||||
#ifndef ALLOW_RETURNING_INFINITY
|
||||
if (std::isinf(v)) {
|
||||
return MINIMUM;
|
||||
}
|
||||
if (std::isinf(v)) {
|
||||
return MINIMUM;
|
||||
}
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static double toLinear(double decibel)
|
||||
{
|
||||
double to_linear = std::pow(double(10.0), decibel / double(20.0));
|
||||
static double toLinear(double decibel)
|
||||
{
|
||||
double to_linear = std::pow(double(10.0), decibel / double(20.0));
|
||||
|
||||
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
|
||||
if (to_linear < 0.000001) {
|
||||
return 0;
|
||||
} else {
|
||||
return to_linear;
|
||||
}
|
||||
}
|
||||
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
|
||||
if (to_linear < 0.000001) {
|
||||
return 0;
|
||||
} else {
|
||||
return to_linear;
|
||||
}
|
||||
}
|
||||
|
||||
static double fromLogarithmic(double logarithmic)
|
||||
{
|
||||
if (logarithmic < 0.001)
|
||||
static double fromLogarithmic(double logarithmic)
|
||||
{
|
||||
if (logarithmic < 0.001)
|
||||
#ifdef ALLOW_RETURNING_INFINITY
|
||||
return std::numeric_limits<double>::infinity();
|
||||
return std::numeric_limits<double>::infinity();
|
||||
#else
|
||||
return MINIMUM;
|
||||
return MINIMUM;
|
||||
#endif
|
||||
else if (logarithmic > 0.99)
|
||||
return 0;
|
||||
else
|
||||
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
|
||||
}
|
||||
else if (logarithmic > 0.99)
|
||||
return 0;
|
||||
else
|
||||
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
|
||||
}
|
||||
|
||||
static double toLogarithmic(double decibel)
|
||||
{
|
||||
if (qFuzzyIsNull(decibel)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
|
||||
}
|
||||
}
|
||||
static double toLogarithmic(double decibel)
|
||||
{
|
||||
if (qFuzzyIsNull(decibel)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
|
||||
}
|
||||
}
|
||||
|
||||
static double LinearToLogarithmic(double linear)
|
||||
{
|
||||
return 1 - std::exp(-linear * LOG100);
|
||||
}
|
||||
static double LinearToLogarithmic(double linear)
|
||||
{
|
||||
return 1 - std::exp(-linear * LOG100);
|
||||
}
|
||||
|
||||
static double LogarithmicToLinear(double logarithmic)
|
||||
{
|
||||
if (logarithmic > 0.99) {
|
||||
return 1;
|
||||
} else {
|
||||
return -std::log(1 - logarithmic) / LOG100;
|
||||
}
|
||||
}
|
||||
static double LogarithmicToLinear(double logarithmic)
|
||||
{
|
||||
if (logarithmic > 0.99) {
|
||||
return 1;
|
||||
} else {
|
||||
return -std::log(1 - logarithmic) / LOG100;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double LOG100 = 4.60517018599;
|
||||
|
||||
static constexpr double LOG100 = 4.60517018599;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+16
-12
@@ -21,7 +21,8 @@
|
||||
#ifndef OLIVECOMMONDEFINE_H
|
||||
#define OLIVECOMMONDEFINE_H
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/// The minimum size an icon in ProjectExplorer can be
|
||||
const int kProjectIconSizeMinimum = 16;
|
||||
@@ -39,25 +40,28 @@ const int kBytesInGigabyte = 1073741824;
|
||||
#define MACRO_NAME_AS_STR(s) #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_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)
|
||||
#define OLIVE_NS_CONST_ARG(x, y) \
|
||||
QArgument<const olive::x>("const " 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
|
||||
* use our own functions for portability.
|
||||
*/
|
||||
|
||||
#define DISABLE_COPY(Class) \
|
||||
Class(const Class &) = delete;\
|
||||
Class &operator=(const Class &) = delete;
|
||||
#define DISABLE_COPY(Class) \
|
||||
Class(const Class &) = delete; \
|
||||
Class &operator=(const Class &) = delete;
|
||||
|
||||
#define DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
#define DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define DISABLE_COPY_MOVE(Class) \
|
||||
DISABLE_COPY(Class) \
|
||||
DISABLE_MOVE(Class)
|
||||
DISABLE_COPY(Class) \
|
||||
DISABLE_MOVE(Class)
|
||||
|
||||
#endif // OLIVECOMMONDEFINE_H
|
||||
|
||||
+10
-9
@@ -23,21 +23,22 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
inline int64_t GetDigitCount(int64_t input)
|
||||
{
|
||||
input = std::abs(input);
|
||||
input = std::abs(input);
|
||||
|
||||
int64_t lim = 10;
|
||||
int64_t digit = 1;
|
||||
int64_t lim = 10;
|
||||
int64_t digit = 1;
|
||||
|
||||
while (input >= lim) {
|
||||
lim *= 10;
|
||||
digit++;
|
||||
}
|
||||
while (input >= lim) {
|
||||
lim *= 10;
|
||||
digit++;
|
||||
}
|
||||
|
||||
return digit;
|
||||
return digit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+148
-141
@@ -20,180 +20,187 @@
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum)
|
||||
namespace olive
|
||||
{
|
||||
AVPixelFormat possible_pix_fmts[3];
|
||||
|
||||
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
|
||||
AVPixelFormat
|
||||
FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
|
||||
PixelFormat maximum)
|
||||
{
|
||||
AVPixelFormat possible_pix_fmts[3];
|
||||
|
||||
if (maximum == PixelFormat::U8) {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
|
||||
} else {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
|
||||
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
||||
}
|
||||
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
|
||||
|
||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
|
||||
pix_fmt,
|
||||
1,
|
||||
nullptr);
|
||||
if (maximum == PixelFormat::U8) {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
|
||||
} else {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
|
||||
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt, 1,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return SampleFormat::U8;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return SampleFormat::S16;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return SampleFormat::S32;
|
||||
case AV_SAMPLE_FMT_S64:
|
||||
return SampleFormat::S64;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return SampleFormat::F32;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return SampleFormat::F64;
|
||||
case AV_SAMPLE_FMT_U8P :
|
||||
return SampleFormat::U8P;
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
return SampleFormat::S16P;
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
return SampleFormat::S32P;
|
||||
case AV_SAMPLE_FMT_S64P:
|
||||
return SampleFormat::S64P;
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
return SampleFormat::F32P;
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
return SampleFormat::F64P;
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
case AV_SAMPLE_FMT_NB:
|
||||
break;
|
||||
}
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return SampleFormat::U8;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return SampleFormat::S16;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return SampleFormat::S32;
|
||||
case AV_SAMPLE_FMT_S64:
|
||||
return SampleFormat::S64;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return SampleFormat::F32;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return SampleFormat::F64;
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
return SampleFormat::U8P;
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
return SampleFormat::S16P;
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
return SampleFormat::S32P;
|
||||
case AV_SAMPLE_FMT_S64P:
|
||||
return SampleFormat::S64P;
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
return SampleFormat::F32P;
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
return SampleFormat::F64P;
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
case AV_SAMPLE_FMT_NB:
|
||||
break;
|
||||
}
|
||||
|
||||
return SampleFormat::INVALID;
|
||||
return SampleFormat::INVALID;
|
||||
}
|
||||
|
||||
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const SampleFormat &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case SampleFormat::U8:
|
||||
return AV_SAMPLE_FMT_U8;
|
||||
case SampleFormat::S16:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case SampleFormat::S32:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case SampleFormat::S64:
|
||||
return AV_SAMPLE_FMT_S64;
|
||||
case SampleFormat::F32:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
case SampleFormat::F64:
|
||||
return AV_SAMPLE_FMT_DBL;
|
||||
case SampleFormat::U8P:
|
||||
return AV_SAMPLE_FMT_U8P;
|
||||
case SampleFormat::S16P:
|
||||
return AV_SAMPLE_FMT_S16P;
|
||||
case SampleFormat::S32P:
|
||||
return AV_SAMPLE_FMT_S32P;
|
||||
case SampleFormat::S64P:
|
||||
return AV_SAMPLE_FMT_S64P;
|
||||
case SampleFormat::F32P:
|
||||
return AV_SAMPLE_FMT_FLTP;
|
||||
case SampleFormat::F64P:
|
||||
return AV_SAMPLE_FMT_DBLP;
|
||||
case SampleFormat::INVALID:
|
||||
case SampleFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
switch (smp_fmt) {
|
||||
case SampleFormat::U8:
|
||||
return AV_SAMPLE_FMT_U8;
|
||||
case SampleFormat::S16:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case SampleFormat::S32:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case SampleFormat::S64:
|
||||
return AV_SAMPLE_FMT_S64;
|
||||
case SampleFormat::F32:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
case SampleFormat::F64:
|
||||
return AV_SAMPLE_FMT_DBL;
|
||||
case SampleFormat::U8P:
|
||||
return AV_SAMPLE_FMT_U8P;
|
||||
case SampleFormat::S16P:
|
||||
return AV_SAMPLE_FMT_S16P;
|
||||
case SampleFormat::S32P:
|
||||
return AV_SAMPLE_FMT_S32P;
|
||||
case SampleFormat::S64P:
|
||||
return AV_SAMPLE_FMT_S64P;
|
||||
case SampleFormat::F32P:
|
||||
return AV_SAMPLE_FMT_FLTP;
|
||||
case SampleFormat::F64P:
|
||||
return AV_SAMPLE_FMT_DBLP;
|
||||
case SampleFormat::INVALID:
|
||||
case SampleFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return AV_SAMPLE_FMT_NONE;
|
||||
return AV_SAMPLE_FMT_NONE;
|
||||
}
|
||||
|
||||
int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
|
||||
{
|
||||
switch (cs) {
|
||||
case AVCOL_SPC_BT709:
|
||||
return SWS_CS_ITU709;
|
||||
case AVCOL_SPC_FCC:
|
||||
return SWS_CS_FCC;
|
||||
case AVCOL_SPC_BT470BG:
|
||||
return SWS_CS_ITU624;
|
||||
case AVCOL_SPC_SMPTE170M:
|
||||
return SWS_CS_SMPTE170M;
|
||||
case AVCOL_SPC_SMPTE240M:
|
||||
return SWS_CS_SMPTE240M;
|
||||
case AVCOL_SPC_BT2020_NCL:
|
||||
return SWS_CS_BT2020;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (cs) {
|
||||
case AVCOL_SPC_BT709:
|
||||
return SWS_CS_ITU709;
|
||||
case AVCOL_SPC_FCC:
|
||||
return SWS_CS_FCC;
|
||||
case AVCOL_SPC_BT470BG:
|
||||
return SWS_CS_ITU624;
|
||||
case AVCOL_SPC_SMPTE170M:
|
||||
return SWS_CS_SMPTE170M;
|
||||
case AVCOL_SPC_SMPTE240M:
|
||||
return SWS_CS_SMPTE240M;
|
||||
case AVCOL_SPC_BT2020_NCL:
|
||||
return SWS_CS_BT2020;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SWS_CS_DEFAULT;
|
||||
return SWS_CS_DEFAULT;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
|
||||
{
|
||||
switch (f) {
|
||||
case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P;
|
||||
case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P;
|
||||
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:
|
||||
break;
|
||||
}
|
||||
switch (f) {
|
||||
case AV_PIX_FMT_YUVJ420P:
|
||||
return AV_PIX_FMT_YUV420P;
|
||||
case AV_PIX_FMT_YUVJ422P:
|
||||
return AV_PIX_FMT_YUV422P;
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
||||
return f;
|
||||
return f;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt, int channel_layout)
|
||||
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
|
||||
int channel_layout)
|
||||
{
|
||||
if (channel_layout == VideoParams::kRGBChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGB24;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGB48;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGBA;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGBA64;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (channel_layout == VideoParams::kRGBChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGB24;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGB48;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGBA;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGBA64;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return AV_PIX_FMT_NONE;
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
PixelFormat FFmpegUtils::GetCompatiblePixelFormat(const PixelFormat &pix_fmt)
|
||||
{
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return PixelFormat::U8;
|
||||
case PixelFormat::U16:
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
return PixelFormat::U16;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return PixelFormat::U8;
|
||||
case PixelFormat::U16:
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
return PixelFormat::U16;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return PixelFormat::INVALID;
|
||||
return PixelFormat::INVALID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-17
@@ -31,63 +31,67 @@ extern "C" {
|
||||
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
class FFmpegUtils {
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
static PixelFormat GetCompatiblePixelFormat(const PixelFormat& pix_fmt);
|
||||
static PixelFormat GetCompatiblePixelFormat(const PixelFormat &pix_fmt);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an FFmpeg pixel format for a given native pixel format
|
||||
*/
|
||||
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
|
||||
*/
|
||||
static SampleFormat GetNativeSampleFormat(const AVSampleFormat& smp_fmt);
|
||||
static SampleFormat GetNativeSampleFormat(const AVSampleFormat &smp_fmt);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an FFmpeg sample format type for a given native type
|
||||
*/
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an SWS_CS_* macro from an AVColorSpace enum member
|
||||
*
|
||||
* Why aren't these the same thing anyway? And for that matter, why doesn't FFmpeg provide a
|
||||
* convenience function to do this conversion for us? Who knows, but here we are.
|
||||
*/
|
||||
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
||||
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart
|
||||
*
|
||||
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
|
||||
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
|
||||
* aware), we use this function.
|
||||
*/
|
||||
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
|
||||
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
|
||||
};
|
||||
|
||||
using AVFramePtr = std::shared_ptr<AVFrame>;
|
||||
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
|
||||
{
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); });
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
|
||||
}
|
||||
inline AVFramePtr CreateAVFramePtr()
|
||||
{
|
||||
return CreateAVFramePtr(av_frame_alloc());
|
||||
return CreateAVFramePtr(av_frame_alloc());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+142
-125
@@ -29,206 +29,223 @@
|
||||
|
||||
#include "config/config.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
|
||||
{
|
||||
QFileInfo info(filename);
|
||||
QFileInfo info(filename);
|
||||
|
||||
if (!info.exists()) {
|
||||
return QString();
|
||||
}
|
||||
if (!info.exists()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
|
||||
hash.addData(info.absoluteFilePath().toUtf8());
|
||||
hash.addData(info.absoluteFilePath().toUtf8());
|
||||
|
||||
hash.addData(QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
|
||||
hash.addData(
|
||||
QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
|
||||
|
||||
QByteArray result = hash.result();
|
||||
QByteArray result = hash.result();
|
||||
|
||||
return QString(result.toHex());
|
||||
return QString(result.toHex());
|
||||
}
|
||||
|
||||
QString FileFunctions::GetConfigurationLocation()
|
||||
{
|
||||
if (IsPortable()) {
|
||||
return GetApplicationPath();
|
||||
} else {
|
||||
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
QDir(s).mkpath(".");
|
||||
return s;
|
||||
}
|
||||
if (IsPortable()) {
|
||||
return GetApplicationPath();
|
||||
} else {
|
||||
QString s =
|
||||
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
QDir(s).mkpath(".");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFunctions::IsPortable()
|
||||
{
|
||||
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
|
||||
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
|
||||
}
|
||||
|
||||
QString FileFunctions::GetApplicationPath()
|
||||
{
|
||||
return QCoreApplication::applicationDirPath();
|
||||
return QCoreApplication::applicationDirPath();
|
||||
}
|
||||
|
||||
QString FileFunctions::GetTempFilePath()
|
||||
{
|
||||
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
||||
.filePath(QCoreApplication::organizationName()))
|
||||
.filePath(QCoreApplication::applicationName());
|
||||
QString temp_path =
|
||||
QDir(
|
||||
QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
||||
.filePath(QCoreApplication::organizationName()))
|
||||
.filePath(QCoreApplication::applicationName());
|
||||
|
||||
// Ensure it exists
|
||||
QDir(temp_path).mkpath(".");
|
||||
// Ensure it exists
|
||||
QDir(temp_path).mkpath(".");
|
||||
|
||||
return temp_path;
|
||||
return temp_path;
|
||||
}
|
||||
|
||||
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest)
|
||||
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString &source,
|
||||
const QString &dest)
|
||||
{
|
||||
QFileInfoList info_list = QDir(source).entryInfoList();
|
||||
QFileInfoList info_list = QDir(source).entryInfoList();
|
||||
|
||||
foreach (const QFileInfo& info, info_list) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
foreach (const QFileInfo &info, info_list) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") ||
|
||||
info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString dest_equivalent = QDir(dest).filePath(info.fileName());
|
||||
QString dest_equivalent = QDir(dest).filePath(info.fileName());
|
||||
|
||||
if (info.isDir()) {
|
||||
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(), dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
} else if (QFileInfo::exists(dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (info.isDir()) {
|
||||
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(),
|
||||
dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
} else if (QFileInfo::exists(dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bool overwrite)
|
||||
void FileFunctions::CopyDirectory(const QString &source, const QString &dest,
|
||||
bool overwrite)
|
||||
{
|
||||
QDir d(source);
|
||||
QDir d(source);
|
||||
|
||||
if (!d.exists()) {
|
||||
qCritical() << "Failed to copy directory, source" << source << "didn't exist";
|
||||
return;
|
||||
}
|
||||
if (!d.exists()) {
|
||||
qCritical()
|
||||
<< "Failed to copy directory, source" << source << "didn't exist";
|
||||
return;
|
||||
}
|
||||
|
||||
QDir dest_dir(dest);
|
||||
QDir dest_dir(dest);
|
||||
|
||||
if (!dest_dir.mkpath(QStringLiteral("."))) {
|
||||
qCritical() << "Failed to create destination directory" << dest;
|
||||
return;
|
||||
}
|
||||
if (!dest_dir.mkpath(QStringLiteral("."))) {
|
||||
qCritical() << "Failed to create destination directory" << dest;
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfoList l = d.entryInfoList();
|
||||
QFileInfoList l = d.entryInfoList();
|
||||
|
||||
foreach (const QFileInfo& info, l) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
foreach (const QFileInfo &info, l) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") ||
|
||||
info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString dest_file_path = dest_dir.filePath(info.fileName());
|
||||
QString dest_file_path = dest_dir.filePath(info.fileName());
|
||||
|
||||
if (info.isDir()) {
|
||||
// Copy dir
|
||||
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
|
||||
} else {
|
||||
// Copy file
|
||||
if (overwrite && QFile::exists(dest_file_path)) {
|
||||
QFile file(dest_file_path);
|
||||
file.setPermissions(file.permissions() | QFileDevice::WriteOwner | QFileDevice::WriteUser |
|
||||
QFileDevice::WriteGroup | QFileDevice::WriteOther);
|
||||
file.remove();
|
||||
}
|
||||
if (info.isDir()) {
|
||||
// Copy dir
|
||||
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
|
||||
} else {
|
||||
// Copy file
|
||||
if (overwrite && QFile::exists(dest_file_path)) {
|
||||
QFile file(dest_file_path);
|
||||
file.setPermissions(
|
||||
file.permissions() | QFileDevice::WriteOwner |
|
||||
QFileDevice::WriteUser | QFileDevice::WriteGroup |
|
||||
QFileDevice::WriteOther);
|
||||
file.remove();
|
||||
}
|
||||
|
||||
QFile::copy(info.absoluteFilePath(), dest_file_path);
|
||||
}
|
||||
}
|
||||
QFile::copy(info.absoluteFilePath(), dest_file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFunctions::DirectoryIsValid(const QDir &d, bool try_to_create_if_not_exists)
|
||||
bool FileFunctions::DirectoryIsValid(const QDir &d,
|
||||
bool try_to_create_if_not_exists)
|
||||
{
|
||||
// Return whether the directory exists, or whether it could be created if it doesn't
|
||||
return d.exists() || d.mkpath(QStringLiteral("."));
|
||||
// Return whether the directory exists, or whether it could be created if it doesn't
|
||||
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
|
||||
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||
QString extension_with_dot;
|
||||
// No-op if either input is empty
|
||||
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||
QString extension_with_dot;
|
||||
|
||||
extension_with_dot.append('.');
|
||||
extension_with_dot.append(extension);
|
||||
extension_with_dot.append('.');
|
||||
extension_with_dot.append(extension);
|
||||
|
||||
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||
fn.append(extension_with_dot);
|
||||
}
|
||||
}
|
||||
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||
fn.append(extension_with_dot);
|
||||
}
|
||||
}
|
||||
|
||||
return fn;
|
||||
return fn;
|
||||
}
|
||||
|
||||
QString FileFunctions::ReadFileAsString(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
}
|
||||
|
||||
QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
|
||||
{
|
||||
int counter = 0;
|
||||
int counter = 0;
|
||||
|
||||
QFileInfo original_info(original);
|
||||
QString basename = original_info.baseName();
|
||||
QString complete_suffix = original_info.completeSuffix();
|
||||
QFileInfo original_info(original);
|
||||
QString basename = original_info.baseName();
|
||||
QString complete_suffix = original_info.completeSuffix();
|
||||
|
||||
// If we have a complete suffix, make sure there's a period in it
|
||||
if (!complete_suffix.isEmpty()) {
|
||||
complete_suffix.prepend('.');
|
||||
}
|
||||
// If we have a complete suffix, make sure there's a period in it
|
||||
if (!complete_suffix.isEmpty()) {
|
||||
complete_suffix.prepend('.');
|
||||
}
|
||||
|
||||
QString temp_abs_path;
|
||||
do {
|
||||
temp_abs_path = original_info.dir().filePath(
|
||||
QStringLiteral("%1.tmp%2%3").arg(basename,
|
||||
QString::number(counter),
|
||||
complete_suffix));
|
||||
counter++;
|
||||
} while (QFileInfo::exists(temp_abs_path));
|
||||
QString temp_abs_path;
|
||||
do {
|
||||
temp_abs_path = original_info.dir().filePath(
|
||||
QStringLiteral("%1.tmp%2%3")
|
||||
.arg(basename, QString::number(counter), complete_suffix));
|
||||
counter++;
|
||||
} 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)) {
|
||||
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
|
||||
return false;
|
||||
}
|
||||
if (QFileInfo::exists(to) && !QFile::remove(to)) {
|
||||
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
|
||||
return false;
|
||||
}
|
||||
|
||||
// By this point, we can assume `to` either never existed or has now been deleted
|
||||
if (!QFile::rename(from, to)) {
|
||||
qCritical() << "Failed to rename file" << from << "to" << to;
|
||||
return false;
|
||||
}
|
||||
// By this point, we can assume `to` either never existed or has now been deleted
|
||||
if (!QFile::rename(from, to)) {
|
||||
qCritical() << "Failed to rename file" << from << "to" << to;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
QString FileFunctions::GetAutoRecoveryRoot()
|
||||
{
|
||||
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery"));
|
||||
return QDir(QStandardPaths::writableLocation(
|
||||
QStandardPaths::AppLocalDataLocation))
|
||||
.filePath(QStringLiteral("autorecovery"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+29
-26
@@ -26,36 +26,40 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A collection of static file and directory functions
|
||||
*/
|
||||
class FileFunctions {
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief Returns true if the application is running in portable mode
|
||||
*
|
||||
* In portable mode, any persistent configuration files should be made in a path relative to the application rather
|
||||
* than in the user's home folder.
|
||||
*/
|
||||
static bool IsPortable();
|
||||
static bool IsPortable();
|
||||
|
||||
static QString GetUniqueFileIdentifier(const QString& filename);
|
||||
static QString GetUniqueFileIdentifier(const QString &filename);
|
||||
|
||||
static QString GetConfigurationLocation();
|
||||
static QString GetConfigurationLocation();
|
||||
|
||||
static QString GetApplicationPath();
|
||||
static QString GetApplicationPath();
|
||||
|
||||
static QString GetTempFilePath();
|
||||
static QString GetTempFilePath();
|
||||
|
||||
static bool CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest);
|
||||
static bool CanCopyDirectoryWithoutOverwriting(const QString &source,
|
||||
const QString &dest);
|
||||
|
||||
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false);
|
||||
static void CopyDirectory(const QString &source, const QString &dest,
|
||||
bool overwrite = false);
|
||||
|
||||
static bool DirectoryIsValid(const QDir& dir, bool try_to_create_if_not_exists = true);
|
||||
static bool DirectoryIsValid(const QDir &dir,
|
||||
bool try_to_create_if_not_exists = true);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Ensures a given filename has a certain extension
|
||||
*
|
||||
* Checks if the filename has the extension provided and appends it if not. The extension is
|
||||
@@ -64,11 +68,12 @@ public:
|
||||
|
||||
* @return The filename provided either untouched or with the extension appended to it.
|
||||
*/
|
||||
static QString EnsureFilenameExtension(QString fn, const QString& extension);
|
||||
static QString EnsureFilenameExtension(QString fn,
|
||||
const QString &extension);
|
||||
|
||||
static QString ReadFileAsString(const QString& filename);
|
||||
static QString ReadFileAsString(const QString &filename);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns a temporary filename that can be used while writing rather than the original
|
||||
*
|
||||
* If overwriting a file, it's safest to write to a new file first and then only replace it at
|
||||
@@ -78,28 +83,26 @@ public:
|
||||
* This function returns a slight variant of the filename provided that's guaranteed to not exist
|
||||
* and therefore won't overwrite anything important.
|
||||
*/
|
||||
static QString GetSafeTemporaryFilename(const QString& original);
|
||||
static QString GetSafeTemporaryFilename(const QString &original);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
|
||||
*/
|
||||
static bool RenameFileAllowOverwrite(const QString& from, const QString& to);
|
||||
static bool RenameFileAllowOverwrite(const QString &from,
|
||||
const QString &to);
|
||||
|
||||
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
|
||||
{
|
||||
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
unformatted.append(QStringLiteral(".exe"));
|
||||
unformatted.append(QStringLiteral(".exe"));
|
||||
#endif
|
||||
|
||||
return unformatted;
|
||||
}
|
||||
|
||||
static QString GetAutoRecoveryRoot();
|
||||
return unformatted;
|
||||
}
|
||||
|
||||
static QString GetAutoRecoveryRoot();
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FILEFUNCTIONS_H
|
||||
|
||||
+349
-323
@@ -5,446 +5,472 @@
|
||||
|
||||
#include "xmlutils.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QVector<QString> Html::kBlockTags = {
|
||||
QStringLiteral("p"),
|
||||
QStringLiteral("div")
|
||||
};
|
||||
const QVector<QString> Html::kBlockTags = { QStringLiteral("p"),
|
||||
QStringLiteral("div") };
|
||||
|
||||
inline bool StrEquals(const QStringView &a, const QStringView &b)
|
||||
{
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
QString Html::DocToHtml(const QTextDocument *doc)
|
||||
{
|
||||
QString html;
|
||||
QXmlStreamWriter writer(&html);
|
||||
QString html;
|
||||
QXmlStreamWriter writer(&html);
|
||||
|
||||
//writer.setAutoFormatting(true);
|
||||
//writer.setAutoFormatting(true);
|
||||
|
||||
for (auto it=doc->begin(); it!=doc->end(); it=it.next()) {
|
||||
WriteBlock(&writer, it);
|
||||
}
|
||||
for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
|
||||
WriteBlock(&writer, it);
|
||||
}
|
||||
|
||||
return html;
|
||||
return html;
|
||||
}
|
||||
|
||||
struct HtmlNode {
|
||||
QString tag;
|
||||
QTextCharFormat format;
|
||||
QString tag;
|
||||
QTextCharFormat format;
|
||||
};
|
||||
|
||||
QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
|
||||
{
|
||||
QTextCharFormat f;
|
||||
QTextCharFormat f;
|
||||
|
||||
for (int i=0; i<stack.size(); i++) {
|
||||
f.merge(stack.at(i).format);
|
||||
}
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
f.merge(stack.at(i).format);
|
||||
}
|
||||
|
||||
return f;
|
||||
return f;
|
||||
}
|
||||
|
||||
void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
||||
{
|
||||
// Empty doc
|
||||
doc->clear();
|
||||
bool inside_block = true;
|
||||
// Empty doc
|
||||
doc->clear();
|
||||
bool inside_block = true;
|
||||
|
||||
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||
QTextCursor c(doc);
|
||||
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||
QTextCursor c(doc);
|
||||
|
||||
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||
QXmlStreamReader reader(wrapped);
|
||||
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||
QXmlStreamReader reader(wrapped);
|
||||
|
||||
QVector<HtmlNode> fmt_stack;
|
||||
QVector<HtmlNode> fmt_stack;
|
||||
|
||||
QTextCharFormat default_fmt;
|
||||
default_fmt.setFontWeight(QFont::Normal);
|
||||
fmt_stack.append({QStringLiteral("html"), default_fmt});
|
||||
QTextCharFormat default_fmt;
|
||||
default_fmt.setFontWeight(QFont::Normal);
|
||||
fmt_stack.append({ QStringLiteral("html"), default_fmt });
|
||||
|
||||
QTextCharFormat current_fmt;
|
||||
QTextCharFormat current_fmt;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
QString tag = reader.name().toString().toLower();
|
||||
fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) });
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
fmt_stack.append({tag, ReadCharFormat(reader.attributes())});
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
if (kBlockTags.contains(tag)) {
|
||||
QTextBlockFormat block_fmt =
|
||||
ReadBlockFormat(reader.attributes());
|
||||
if (inside_block) {
|
||||
c.setBlockFormat(block_fmt);
|
||||
c.setBlockCharFormat(current_fmt);
|
||||
} else {
|
||||
c.insertBlock(block_fmt, current_fmt);
|
||||
inside_block = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (kBlockTags.contains(tag)) {
|
||||
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes());
|
||||
if (inside_block) {
|
||||
c.setBlockFormat(block_fmt);
|
||||
c.setBlockCharFormat(current_fmt);
|
||||
} else {
|
||||
c.insertBlock(block_fmt, current_fmt);
|
||||
inside_block = true;
|
||||
}
|
||||
}
|
||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||
QString characters = reader.text().toString();
|
||||
c.insertText(characters, current_fmt);
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
QString characters = reader.text().toString();
|
||||
c.insertText(characters, current_fmt);
|
||||
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
|
||||
if (fmt_stack.at(i).tag == tag) {
|
||||
fmt_stack.removeAt(i);
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||
if (kBlockTags.contains(tag)) {
|
||||
inside_block = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
for (int i=fmt_stack.size()-1; i>=0; i--) {
|
||||
if (fmt_stack.at(i).tag == tag) {
|
||||
fmt_stack.removeAt(i);
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
if (kBlockTags.contains(tag)) {
|
||||
inside_block = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.error()) {
|
||||
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||
}
|
||||
if (reader.error()) {
|
||||
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||
}
|
||||
}
|
||||
|
||||
void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("p"));
|
||||
writer->writeStartElement(QStringLiteral("p"));
|
||||
|
||||
const QTextBlockFormat &fmt = block.blockFormat();
|
||||
const QTextBlockFormat &fmt = block.blockFormat();
|
||||
|
||||
// Write block alignment
|
||||
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||
if (fmt.alignment() & Qt::AlignRight) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("right"));
|
||||
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center"));
|
||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify"));
|
||||
}
|
||||
}
|
||||
// Write block alignment
|
||||
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||
if (fmt.alignment() & Qt::AlignRight) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("right"));
|
||||
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("center"));
|
||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("justify"));
|
||||
}
|
||||
}
|
||||
|
||||
// RTL support
|
||||
if (block.textDirection() == Qt::RightToLeft) {
|
||||
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||
}
|
||||
// RTL support
|
||||
if (block.textDirection() == Qt::RightToLeft) {
|
||||
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||
}
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||
WriteCSSProperty(&style, QStringLiteral("line-height"), QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||
}
|
||||
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||
WriteCSSProperty(&style, QStringLiteral("line-height"),
|
||||
QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||
}
|
||||
|
||||
WriteCharFormat(&style, block.charFormat());
|
||||
WriteCharFormat(&style, block.charFormat());
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
auto it = block.begin();
|
||||
auto it = block.begin();
|
||||
|
||||
if (it != block.end()) {
|
||||
for (; it!=block.end(); it++) {
|
||||
WriteFragment(writer, it.fragment());
|
||||
}
|
||||
}
|
||||
if (it != block.end()) {
|
||||
for (; it != block.end(); it++) {
|
||||
WriteFragment(writer, it.fragment());
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // p
|
||||
writer->writeEndElement(); // p
|
||||
}
|
||||
|
||||
void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment)
|
||||
void Html::WriteFragment(QXmlStreamWriter *writer,
|
||||
const QTextFragment &fragment)
|
||||
{
|
||||
const QTextCharFormat &fmt = fragment.charFormat();
|
||||
const QTextCharFormat &fmt = fragment.charFormat();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("span"));
|
||||
writer->writeStartElement(QStringLiteral("span"));
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
WriteCharFormat(&style, fmt);
|
||||
WriteCharFormat(&style, fmt);
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||
bool first_line = true;
|
||||
foreach (const QString &l, lines) {
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
} else {
|
||||
writer->writeEmptyElement(QStringLiteral("br"));
|
||||
}
|
||||
writer->writeCharacters(l);
|
||||
}
|
||||
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||
bool first_line = true;
|
||||
foreach (const QString &l, lines) {
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
} else {
|
||||
writer->writeEmptyElement(QStringLiteral("br"));
|
||||
}
|
||||
writer->writeCharacters(l);
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // span
|
||||
writer->writeEndElement(); // span
|
||||
}
|
||||
|
||||
void Html::WriteCSSProperty(QString *style, const QString &key, const QStringList &values)
|
||||
void Html::WriteCSSProperty(QString *style, const QString &key,
|
||||
const QStringList &values)
|
||||
{
|
||||
QString value;
|
||||
foreach (QString v, values) {
|
||||
if (v.contains(' ')) {
|
||||
v = QStringLiteral("'%1'").arg(v);
|
||||
}
|
||||
QString value;
|
||||
foreach (QString v, values) {
|
||||
if (v.contains(' ')) {
|
||||
v = QStringLiteral("'%1'").arg(v);
|
||||
}
|
||||
|
||||
AppendStringAutoSpace(&value, v);
|
||||
}
|
||||
AppendStringAutoSpace(&value, v);
|
||||
}
|
||||
|
||||
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||
}
|
||||
|
||||
void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
|
||||
{
|
||||
QStringList families = fmt.fontFamilies().toStringList();
|
||||
if (!families.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-family"), families.first());
|
||||
}
|
||||
QStringList families = fmt.fontFamilies().toStringList();
|
||||
if (!families.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-family"),
|
||||
families.first());
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-size"), QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||
WriteCSSProperty(
|
||||
style, QStringLiteral("font-size"),
|
||||
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-weight"), QString::number(fmt.fontWeight() * 8));
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-weight"),
|
||||
QString::number(fmt.fontWeight() * 8));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-style"), fmt.fontItalic() ? QStringLiteral("italic") : QStringLiteral("normal"));
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-style"),
|
||||
fmt.fontItalic() ? QStringLiteral("italic") :
|
||||
QStringLiteral("normal"));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||
WriteCSSProperty(style, QStringLiteral("-ove-font-style"), fmt.fontStyleName().toString());
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||
WriteCSSProperty(style, QStringLiteral("-ove-font-style"),
|
||||
fmt.fontStyleName().toString());
|
||||
}
|
||||
|
||||
QStringList deco;
|
||||
QStringList deco;
|
||||
|
||||
if (fmt.fontUnderline()) {
|
||||
deco.append(QStringLiteral("underline"));
|
||||
}
|
||||
if (fmt.fontUnderline()) {
|
||||
deco.append(QStringLiteral("underline"));
|
||||
}
|
||||
|
||||
if (fmt.fontStrikeOut()) {
|
||||
deco.append(QStringLiteral("line-through"));
|
||||
}
|
||||
if (fmt.fontStrikeOut()) {
|
||||
deco.append(QStringLiteral("line-through"));
|
||||
}
|
||||
|
||||
if (fmt.fontOverline()) {
|
||||
deco.append(QStringLiteral("overline"));
|
||||
}
|
||||
if (fmt.fontOverline()) {
|
||||
deco.append(QStringLiteral("overline"));
|
||||
}
|
||||
|
||||
if (!deco.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
|
||||
}
|
||||
if (!deco.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
|
||||
}
|
||||
|
||||
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||
const QColor &color = fmt.foreground().color();
|
||||
QString cs;
|
||||
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||
const QColor &color = fmt.foreground().color();
|
||||
QString cs;
|
||||
|
||||
if (color.alpha() == 255) {
|
||||
cs = color.name();
|
||||
} 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()));
|
||||
}
|
||||
if (color.alpha() == 255) {
|
||||
cs = color.name();
|
||||
} 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()));
|
||||
}
|
||||
|
||||
WriteCSSProperty(style, QStringLiteral("color"), cs);
|
||||
}
|
||||
WriteCSSProperty(style, QStringLiteral("color"), cs);
|
||||
}
|
||||
|
||||
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-variant"), QStringLiteral("small-caps"));
|
||||
// TODO: Add others
|
||||
}
|
||||
}
|
||||
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-variant"),
|
||||
QStringLiteral("small-caps"));
|
||||
// TODO: Add others
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt.fontLetterSpacing() != 0.0) {
|
||||
WriteCSSProperty(style, QStringLiteral("letter-spacing"), QStringLiteral("%1%").arg(QString::number(fmt.fontLetterSpacing())));
|
||||
}
|
||||
if (fmt.fontLetterSpacing() != 0.0) {
|
||||
WriteCSSProperty(style, QStringLiteral("letter-spacing"),
|
||||
QStringLiteral("%1%").arg(
|
||||
QString::number(fmt.fontLetterSpacing())));
|
||||
}
|
||||
|
||||
if (fmt.fontStretch() != 0) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-stretch"), QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||
}
|
||||
if (fmt.fontStretch() != 0) {
|
||||
WriteCSSProperty(
|
||||
style, QStringLiteral("font-stretch"),
|
||||
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||
}
|
||||
}
|
||||
|
||||
QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextCharFormat fmt;
|
||||
QTextCharFormat fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
|
||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
||||
const QString &first_val = it.value().first();
|
||||
for (auto it = css.begin(); it != css.end(); it++) {
|
||||
const QString &first_val = it.value().first();
|
||||
|
||||
if (it.key() == QStringLiteral("font-family")) {
|
||||
fmt.setFontFamilies({first_val});
|
||||
} else if (it.key() == QStringLiteral("font-size")) {
|
||||
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) {
|
||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||
fmt.setFontWeight(first_val.toInt()/8);
|
||||
} else if (it.key() == QStringLiteral("font-style")) {
|
||||
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic")));
|
||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||
foreach (const QString &v, it.value()) {
|
||||
if (StrEquals(v, QStringLiteral("underline"))) {
|
||||
fmt.setFontUnderline(true);
|
||||
} else if (StrEquals(v, QStringLiteral("line-through"))) {
|
||||
fmt.setFontStrikeOut(true);
|
||||
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
||||
fmt.setFontOverline(true);
|
||||
}
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("color")) {
|
||||
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) {
|
||||
QString vals_only = first_val;
|
||||
vals_only.remove(QStringLiteral("rgba"));
|
||||
vals_only.remove(QStringLiteral("("));
|
||||
vals_only.remove(QStringLiteral(")"));
|
||||
QStringList rgba = vals_only.split(',');
|
||||
if (rgba.size() == 4) {
|
||||
QColor c;
|
||||
c.setRedF(rgba.at(0).toDouble());
|
||||
c.setGreenF(rgba.at(1).toDouble());
|
||||
c.setBlueF(rgba.at(2).toDouble());
|
||||
c.setAlphaF(rgba.at(3).toDouble());
|
||||
fmt.setForeground(c);
|
||||
}
|
||||
} else {
|
||||
fmt.setForeground(QColor(first_val));
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
|
||||
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||
fmt.setFontStyleName(first_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
if (it.key() == QStringLiteral("font-family")) {
|
||||
fmt.setFontFamilies({ first_val });
|
||||
} else if (it.key() == QStringLiteral("font-size")) {
|
||||
if (first_val.endsWith(QStringLiteral("pt"),
|
||||
Qt::CaseInsensitive)) {
|
||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||
fmt.setFontWeight(first_val.toInt() / 8);
|
||||
} else if (it.key() == QStringLiteral("font-style")) {
|
||||
fmt.setFontItalic(
|
||||
StrEquals(first_val, QStringLiteral("italic")));
|
||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||
foreach (const QString &v, it.value()) {
|
||||
if (StrEquals(v, QStringLiteral("underline"))) {
|
||||
fmt.setFontUnderline(true);
|
||||
} else if (StrEquals(v,
|
||||
QStringLiteral("line-through"))) {
|
||||
fmt.setFontStrikeOut(true);
|
||||
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
||||
fmt.setFontOverline(true);
|
||||
}
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("color")) {
|
||||
if (first_val.startsWith(QStringLiteral("rgba"),
|
||||
Qt::CaseInsensitive)) {
|
||||
QString vals_only = first_val;
|
||||
vals_only.remove(QStringLiteral("rgba"));
|
||||
vals_only.remove(QStringLiteral("("));
|
||||
vals_only.remove(QStringLiteral(")"));
|
||||
QStringList rgba = vals_only.split(',');
|
||||
if (rgba.size() == 4) {
|
||||
QColor c;
|
||||
c.setRedF(rgba.at(0).toDouble());
|
||||
c.setGreenF(rgba.at(1).toDouble());
|
||||
c.setBlueF(rgba.at(2).toDouble());
|
||||
c.setAlphaF(rgba.at(3).toDouble());
|
||||
fmt.setForeground(c);
|
||||
}
|
||||
} else {
|
||||
fmt.setForeground(QColor(first_val));
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
|
||||
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontLetterSpacing(
|
||||
first_val.chopped(1).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||
fmt.setFontStyleName(first_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextBlockFormat block_fmt;
|
||||
QTextBlockFormat block_fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("align"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("right"))) {
|
||||
block_fmt.setAlignment(Qt::AlignRight);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
|
||||
block_fmt.setAlignment(Qt::AlignHCenter);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
|
||||
block_fmt.setAlignment(Qt::AlignJustify);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
|
||||
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("align"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("right"))) {
|
||||
block_fmt.setAlignment(Qt::AlignRight);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
|
||||
block_fmt.setAlignment(Qt::AlignHCenter);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
|
||||
block_fmt.setAlignment(Qt::AlignJustify);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
|
||||
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
|
||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
||||
if (it.key() == QStringLiteral("line-height")) {
|
||||
const QString &first_val = it.value().constFirst();
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = css.begin(); it != css.end(); it++) {
|
||||
if (it.key() == QStringLiteral("line-height")) {
|
||||
const QString &first_val = it.value().constFirst();
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
block_fmt.setLineHeight(
|
||||
first_val.chopped(1).toDouble(),
|
||||
QTextBlockFormat::ProportionalHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return block_fmt;
|
||||
return block_fmt;
|
||||
}
|
||||
|
||||
void Html::AppendStringAutoSpace(QString *s, const QString &append)
|
||||
{
|
||||
if (!s->isEmpty()) {
|
||||
s->append(QChar(' '));
|
||||
}
|
||||
if (!s->isEmpty()) {
|
||||
s->append(QChar(' '));
|
||||
}
|
||||
|
||||
s->append(append);
|
||||
s->append(append);
|
||||
}
|
||||
|
||||
QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
|
||||
{
|
||||
QMap<QString, QStringList> map;
|
||||
QMap<QString, QStringList> map;
|
||||
|
||||
QStringList list = s.split(QChar(';'));
|
||||
QStringList list = s.split(QChar(';'));
|
||||
|
||||
foreach (const QString &a, list) {
|
||||
QStringList kv = a.split(QChar(':'));
|
||||
foreach (const QString &a, list) {
|
||||
QStringList kv = a.split(QChar(':'));
|
||||
|
||||
if (kv.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
if (kv.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||
// match. Also commas should be filtered out.
|
||||
QStringList values;
|
||||
const QString &val = kv.at(1);
|
||||
QChar in_quote(0);
|
||||
QString current_str;
|
||||
for (int i=0; i<val.size(); i++) {
|
||||
const QChar ¤t_char = val.at(i);
|
||||
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||
// match. Also commas should be filtered out.
|
||||
QStringList values;
|
||||
const QString &val = kv.at(1);
|
||||
QChar in_quote(0);
|
||||
QString current_str;
|
||||
for (int i = 0; i < val.size(); i++) {
|
||||
const QChar ¤t_char = val.at(i);
|
||||
|
||||
if (!in_quote.isNull()) {
|
||||
// If inside quotes and character isn't quote, indiscriminately append char
|
||||
if (current_char == in_quote) {
|
||||
in_quote = QChar(0);
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||
// Dump current
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
current_str.clear();
|
||||
}
|
||||
} else if (in_quote.isNull() && (current_char == QChar('\'') || current_char == QChar('"'))) {
|
||||
in_quote = current_char;
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
}
|
||||
if (!in_quote.isNull()) {
|
||||
// If inside quotes and character isn't quote, indiscriminately append char
|
||||
if (current_char == in_quote) {
|
||||
in_quote = QChar(0);
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||
// Dump current
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
current_str.clear();
|
||||
}
|
||||
} else if (in_quote.isNull() && (current_char == QChar('\'') ||
|
||||
current_char == QChar('"'))) {
|
||||
in_quote = current_char;
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
}
|
||||
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
}
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
}
|
||||
|
||||
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||
if (values.isEmpty()) {
|
||||
values.append(QString());
|
||||
}
|
||||
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||
if (values.isEmpty()) {
|
||||
values.append(QString());
|
||||
}
|
||||
|
||||
map[kv.at(0).trimmed().toLower()] = values;
|
||||
}
|
||||
map[kv.at(0).trimmed().toLower()] = values;
|
||||
}
|
||||
|
||||
return map;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-17
@@ -6,7 +6,8 @@
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Functions for converting HTML to QTextDocument and vice versa
|
||||
@@ -26,33 +27,37 @@ namespace olive {
|
||||
*/
|
||||
class Html {
|
||||
public:
|
||||
static QString DocToHtml(const QTextDocument *doc);
|
||||
static QString DocToHtml(const QTextDocument *doc);
|
||||
|
||||
static void HtmlToDoc(QTextDocument *doc, const QString &html);
|
||||
static void HtmlToDoc(QTextDocument *doc, const QString &html);
|
||||
|
||||
private:
|
||||
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, const QString &value)
|
||||
{
|
||||
WriteCSSProperty(style, key, QStringList({value}));
|
||||
}
|
||||
static void WriteCSSProperty(QString *style, const QString &key,
|
||||
const QStringList &value);
|
||||
static void WriteCSSProperty(QString *style, const QString &key,
|
||||
const QString &value)
|
||||
{
|
||||
WriteCSSProperty(style, key, QStringList({ value }));
|
||||
}
|
||||
|
||||
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
|
||||
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
|
||||
|
||||
static QTextCharFormat ReadCharFormat(const QXmlStreamAttributes &attributes);
|
||||
static QTextCharFormat
|
||||
ReadCharFormat(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static QTextBlockFormat ReadBlockFormat(const QXmlStreamAttributes &attributes);
|
||||
static QTextBlockFormat
|
||||
ReadBlockFormat(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static void AppendStringAutoSpace(QString *s, const QString &append);
|
||||
static void AppendStringAutoSpace(QString *s, const QString &append);
|
||||
|
||||
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
|
||||
|
||||
static const QVector<QString> kBlockTags;
|
||||
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
|
||||
|
||||
static const QVector<QString> kBlockTags;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,29 +2,30 @@
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
uint64_t job_time_index = 0;
|
||||
QMutex job_time_mutex;
|
||||
|
||||
JobTime::JobTime()
|
||||
{
|
||||
Acquire();
|
||||
Acquire();
|
||||
}
|
||||
|
||||
void JobTime::Acquire()
|
||||
{
|
||||
job_time_mutex.lock();
|
||||
job_time_mutex.lock();
|
||||
|
||||
value_ = job_time_index;
|
||||
job_time_index++;
|
||||
value_ = job_time_index;
|
||||
job_time_index++;
|
||||
|
||||
job_time_mutex.unlock();
|
||||
job_time_mutex.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime& r)
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime &r)
|
||||
{
|
||||
return debug.space() << r.value();
|
||||
return debug.space() << r.value();
|
||||
}
|
||||
|
||||
+35
-36
@@ -4,58 +4,57 @@
|
||||
#include <QDebug>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace olive {
|
||||
|
||||
class JobTime
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class JobTime {
|
||||
public:
|
||||
JobTime();
|
||||
JobTime();
|
||||
|
||||
void Acquire();
|
||||
void Acquire();
|
||||
|
||||
uint64_t value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
uint64_t value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
bool operator==(const JobTime &rhs) const
|
||||
{
|
||||
return value_ == rhs.value_;
|
||||
}
|
||||
bool operator==(const JobTime &rhs) const
|
||||
{
|
||||
return value_ == rhs.value_;
|
||||
}
|
||||
|
||||
bool operator!=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ != rhs.value_;
|
||||
}
|
||||
bool operator!=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ != rhs.value_;
|
||||
}
|
||||
|
||||
bool operator<(const JobTime &rhs) const
|
||||
{
|
||||
return value_ < rhs.value_;
|
||||
}
|
||||
bool operator<(const JobTime &rhs) const
|
||||
{
|
||||
return value_ < rhs.value_;
|
||||
}
|
||||
|
||||
bool operator>(const JobTime &rhs) const
|
||||
{
|
||||
return value_ > rhs.value_;
|
||||
}
|
||||
bool operator>(const JobTime &rhs) const
|
||||
{
|
||||
return value_ > rhs.value_;
|
||||
}
|
||||
|
||||
bool operator<=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ <= rhs.value_;
|
||||
}
|
||||
bool operator<=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ <= rhs.value_;
|
||||
}
|
||||
|
||||
bool operator>=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ >= rhs.value_;
|
||||
}
|
||||
bool operator>=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ >= rhs.value_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t value_;
|
||||
|
||||
uint64_t value_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime& r);
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime &r);
|
||||
|
||||
Q_DECLARE_METATYPE(olive::JobTime)
|
||||
|
||||
|
||||
+7
-6
@@ -21,20 +21,21 @@
|
||||
#ifndef LERP_H
|
||||
#define LERP_H
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
/**
|
||||
* @brief Linearly interpolate a value between a and b using t
|
||||
*
|
||||
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
|
||||
* in between a and b at that point linearly.
|
||||
*/
|
||||
T lerp(T a, T b, double t) {
|
||||
return (a * (1.0 - t)) + (b * t);
|
||||
T lerp(T a, T b, double t)
|
||||
{
|
||||
return (a * (1.0 - t)) + (b * t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T lerp(T a, T b, float t) {
|
||||
return (a * (1.0f - t)) + (b * t);
|
||||
template <typename T> T lerp(T a, T b, float t)
|
||||
{
|
||||
return (a * (1.0f - t)) + (b * t);
|
||||
}
|
||||
|
||||
#endif // LERP_H
|
||||
|
||||
+249
-245
@@ -31,7 +31,8 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief MemoryPool base class
|
||||
@@ -46,386 +47,389 @@ namespace olive {
|
||||
* `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
|
||||
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
|
||||
*/
|
||||
class MemoryPool : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class MemoryPool : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param element_count
|
||||
*
|
||||
* Number of elements per arena
|
||||
*/
|
||||
MemoryPool(int element_count)
|
||||
{
|
||||
element_count_ = element_count;
|
||||
MemoryPool(int element_count)
|
||||
{
|
||||
element_count_ = element_count;
|
||||
|
||||
clear_timer_ = new QTimer();
|
||||
clear_timer_->setInterval(kMaxEmptyArenaLife);
|
||||
clear_timer_->moveToThread(qApp->thread());
|
||||
connect(clear_timer_, &QTimer::timeout, this, &MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
|
||||
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
||||
}
|
||||
clear_timer_ = new QTimer();
|
||||
clear_timer_->setInterval(kMaxEmptyArenaLife);
|
||||
clear_timer_->moveToThread(qApp->thread());
|
||||
connect(clear_timer_, &QTimer::timeout, this,
|
||||
&MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
|
||||
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Destructor
|
||||
*
|
||||
* Deletes all arenas.
|
||||
*/
|
||||
virtual ~MemoryPool()
|
||||
{
|
||||
Clear();
|
||||
clear_timer_->deleteLater();
|
||||
}
|
||||
virtual ~MemoryPool()
|
||||
{
|
||||
Clear();
|
||||
clear_timer_->deleteLater();
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(MemoryPool)
|
||||
DISABLE_COPY_MOVE(MemoryPool)
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Clears all arenas, freeing all of their memory
|
||||
*
|
||||
* Note that this function is not safe, any elements that are still out there will be invalid
|
||||
* and accessing them will cause a crash. You'll need to make sure all elements are already
|
||||
* relinquished before then.
|
||||
*/
|
||||
void Clear()
|
||||
{
|
||||
qDeleteAll(arenas_);
|
||||
arenas_.clear();
|
||||
}
|
||||
void Clear()
|
||||
{
|
||||
qDeleteAll(arenas_);
|
||||
arenas_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns whether any arenas are successfully allocated
|
||||
*/
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return !arenas_.empty();
|
||||
}
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return !arenas_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns current number of allocated arenas
|
||||
*/
|
||||
inline int GetArenaCount() const
|
||||
{
|
||||
return arenas_.size();
|
||||
}
|
||||
inline int GetArenaCount() const
|
||||
{
|
||||
return arenas_.size();
|
||||
}
|
||||
|
||||
class Arena;
|
||||
class Arena;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief A handle for a chunk of memory in an arena
|
||||
*
|
||||
* Calling Get() on the pool or arena will return a shared pointer to an element which will contain a pointer to
|
||||
* the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the memory
|
||||
* is released back into the pool so it can be used by another class.
|
||||
*/
|
||||
class Element {
|
||||
public:
|
||||
/**
|
||||
class Element {
|
||||
public:
|
||||
/**
|
||||
* @brief Element Constructor
|
||||
*
|
||||
* There is no need to use this outside of the memory pool's internal functions.
|
||||
*/
|
||||
Element(Arena* parent, uint8_t* data)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = data;
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
Element(Arena *parent, uint8_t *data)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = data;
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Element Destructor
|
||||
*
|
||||
* Automatically releases this element's memory back to the arena it was retrieved from.
|
||||
*/
|
||||
~Element()
|
||||
{
|
||||
release();
|
||||
}
|
||||
~Element()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(Element)
|
||||
DISABLE_COPY_MOVE(Element)
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Access data represented in the pool
|
||||
*/
|
||||
inline uint8_t* data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
inline uint8_t *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline const int64_t& timestamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
inline const int64_t ×tamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
inline void set_timestamp(const int64_t& timestamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
inline void set_timestamp(const int64_t ×tamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Register that this element has been accessed
|
||||
*
|
||||
* \see last_accessed()
|
||||
*/
|
||||
inline void access()
|
||||
{
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
inline void access()
|
||||
{
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the last time `access()` was called on this function
|
||||
*
|
||||
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of
|
||||
* time, it can probably be freed back into the pool). This requires all usages to call `access()`.
|
||||
*/
|
||||
inline const int64_t& last_accessed() const
|
||||
{
|
||||
return accessed_;
|
||||
}
|
||||
inline const int64_t &last_accessed() const
|
||||
{
|
||||
return accessed_;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (data_) {
|
||||
parent_->Release(this);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
void release()
|
||||
{
|
||||
if (data_) {
|
||||
parent_->Release(this);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Arena* parent_;
|
||||
private:
|
||||
Arena *parent_;
|
||||
|
||||
uint8_t* data_;
|
||||
uint8_t *data_;
|
||||
|
||||
int64_t timestamp_;
|
||||
int64_t timestamp_;
|
||||
|
||||
int64_t accessed_;
|
||||
int64_t accessed_;
|
||||
};
|
||||
|
||||
};
|
||||
using ElementPtr = std::shared_ptr<Element>;
|
||||
|
||||
using ElementPtr = std::shared_ptr<Element>;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief A memory pool arena - a subsection of memory
|
||||
*
|
||||
* The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of
|
||||
* an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and continue
|
||||
* providing memory (and freeing arenas when they're no longer in use).
|
||||
*/
|
||||
class Arena {
|
||||
public:
|
||||
Arena(MemoryPool* parent)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = nullptr;
|
||||
allocated_sz_ = 0;
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
class Arena {
|
||||
public:
|
||||
Arena(MemoryPool *parent)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = nullptr;
|
||||
allocated_sz_ = 0;
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
~Arena()
|
||||
{
|
||||
std::list<Element*> copy = lent_elements_;
|
||||
foreach (Element* e, copy) {
|
||||
e->release();
|
||||
}
|
||||
~Arena()
|
||||
{
|
||||
std::list<Element *> copy = lent_elements_;
|
||||
foreach (Element *e, copy) {
|
||||
e->release();
|
||||
}
|
||||
|
||||
delete [] data_;
|
||||
}
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(Arena)
|
||||
DISABLE_COPY_MOVE(Arena)
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an element if there is free memory to do so
|
||||
*/
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
for (int i=0;i<available_.size();i++) {
|
||||
if (available_.at(i)) {
|
||||
// This buffer is available
|
||||
available_.replace(i, false);
|
||||
for (int i = 0; i < available_.size(); i++) {
|
||||
if (available_.at(i)) {
|
||||
// This buffer is available
|
||||
available_.replace(i, false);
|
||||
|
||||
ElementPtr e = std::make_shared<Element>(this,
|
||||
reinterpret_cast<uint8_t*>(data_ + i * element_sz_));
|
||||
lent_elements_.push_back(e.get());
|
||||
ElementPtr e = std::make_shared<Element>(
|
||||
this,
|
||||
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
|
||||
lent_elements_.push_back(e.get());
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Releases an element back into the pool for use elsewhere
|
||||
*/
|
||||
void Release(Element* e)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_);
|
||||
void Release(Element *e)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
quintptr diff = reinterpret_cast<quintptr>(e->data()) -
|
||||
reinterpret_cast<quintptr>(data_);
|
||||
|
||||
int index = diff / element_sz_;
|
||||
int index = diff / element_sz_;
|
||||
|
||||
available_.replace(index, true);
|
||||
available_.replace(index, true);
|
||||
|
||||
lent_elements_.remove(e);
|
||||
lent_elements_.remove(e);
|
||||
|
||||
if (lent_elements_.empty()) {
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
if (lent_elements_.empty()) {
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
|
||||
int GetUsageCount()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return lent_elements_.size();
|
||||
}
|
||||
int GetUsageCount()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return lent_elements_.size();
|
||||
}
|
||||
|
||||
bool Allocate(size_t ele_sz, size_t nb_elements)
|
||||
{
|
||||
if (IsAllocated()) {
|
||||
return true;
|
||||
}
|
||||
bool Allocate(size_t ele_sz, size_t nb_elements)
|
||||
{
|
||||
if (IsAllocated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
element_sz_ = ele_sz;
|
||||
element_sz_ = ele_sz;
|
||||
|
||||
allocated_sz_ = element_sz_ * nb_elements;
|
||||
allocated_sz_ = element_sz_ * nb_elements;
|
||||
|
||||
if ((data_ = new uint8_t[allocated_sz_])) {
|
||||
available_.resize(nb_elements);
|
||||
available_.fill(true);
|
||||
if ((data_ = new uint8_t[allocated_sz_])) {
|
||||
available_.resize(nb_elements);
|
||||
available_.fill(true);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
available_.clear();
|
||||
return true;
|
||||
} else {
|
||||
available_.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline int GetElementCount() const
|
||||
{
|
||||
return available_.size();
|
||||
}
|
||||
inline int GetElementCount() const
|
||||
{
|
||||
return available_.size();
|
||||
}
|
||||
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline qint64 GetTimeArenaWasMadeEmpty()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return empty_time_;
|
||||
}
|
||||
inline qint64 GetTimeArenaWasMadeEmpty()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return empty_time_;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* parent_;
|
||||
private:
|
||||
MemoryPool *parent_;
|
||||
|
||||
uint8_t* data_;
|
||||
uint8_t *data_;
|
||||
|
||||
size_t allocated_sz_;
|
||||
size_t allocated_sz_;
|
||||
|
||||
QVector<bool> available_;
|
||||
QVector<bool> available_;
|
||||
|
||||
QMutex lock_;
|
||||
QMutex lock_;
|
||||
|
||||
size_t element_sz_;
|
||||
size_t element_sz_;
|
||||
|
||||
std::list<Element*> lent_elements_;
|
||||
std::list<Element *> lent_elements_;
|
||||
|
||||
qint64 empty_time_;
|
||||
qint64 empty_time_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves an element from an available arena
|
||||
*/
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
// Attempt to get an element from an arena
|
||||
foreach (Arena* a, arenas_) {
|
||||
ElementPtr e = a->Get();
|
||||
// Attempt to get an element from an arena
|
||||
foreach (Arena *a, arenas_) {
|
||||
ElementPtr e = a->Get();
|
||||
|
||||
if (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
if (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
// All arenas were empty, we'll need to create a new one
|
||||
if (arenas_.empty()) {
|
||||
qDebug() << "No arenas, creating new...";
|
||||
} else {
|
||||
qDebug() << "All arenas are full, creating new...";
|
||||
}
|
||||
// All arenas were empty, we'll need to create a new one
|
||||
if (arenas_.empty()) {
|
||||
qDebug() << "No arenas, creating new...";
|
||||
} else {
|
||||
qDebug() << "All arenas are full, creating new...";
|
||||
}
|
||||
|
||||
size_t ele_sz = GetElementSize();
|
||||
size_t ele_sz = GetElementSize();
|
||||
|
||||
if (!ele_sz) {
|
||||
qCritical() << "Failed to create arena, element size was 0";
|
||||
return nullptr;
|
||||
}
|
||||
if (!ele_sz) {
|
||||
qCritical() << "Failed to create arena, element size was 0";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (element_count_ <= 0) {
|
||||
qCritical() << "Failed to create arena, element count was invalid:" << element_count_;
|
||||
return nullptr;
|
||||
}
|
||||
if (element_count_ <= 0) {
|
||||
qCritical() << "Failed to create arena, element count was invalid:"
|
||||
<< element_count_;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Arena* a = new Arena(this);
|
||||
if (!a->Allocate(ele_sz, element_count_)) {
|
||||
qCritical() << "Failed to create arena, allocation failed. Out of memory?";
|
||||
delete a;
|
||||
return nullptr;
|
||||
}
|
||||
Arena *a = new Arena(this);
|
||||
if (!a->Allocate(ele_sz, element_count_)) {
|
||||
qCritical()
|
||||
<< "Failed to create arena, allocation failed. Out of memory?";
|
||||
delete a;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
arenas_.push_back(a);
|
||||
return a->Get();
|
||||
}
|
||||
arenas_.push_back(a);
|
||||
return a->Get();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
/**
|
||||
* @brief The size of each element
|
||||
*
|
||||
* Override this to use a custom size (e.g. a char array where T = char but the element size is > 1)
|
||||
*/
|
||||
virtual size_t GetElementSize()
|
||||
{
|
||||
return sizeof(uint8_t);
|
||||
}
|
||||
virtual size_t GetElementSize()
|
||||
{
|
||||
return sizeof(uint8_t);
|
||||
}
|
||||
|
||||
private:
|
||||
int element_count_;
|
||||
int element_count_;
|
||||
|
||||
std::list<Arena*> arenas_;
|
||||
std::list<Arena *> arenas_;
|
||||
|
||||
QMutex lock_;
|
||||
QMutex lock_;
|
||||
|
||||
QTimer *clear_timer_;
|
||||
QTimer *clear_timer_;
|
||||
|
||||
static const qint64 kMaxEmptyArenaLife = 5000;
|
||||
static const qint64 kMaxEmptyArenaLife = 5000;
|
||||
|
||||
private slots:
|
||||
void ClearEmptyArenas()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
void ClearEmptyArenas()
|
||||
{
|
||||
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(); ) {
|
||||
Arena* arena = (*it);
|
||||
|
||||
if (arena->GetUsageCount() == 0 && arena->GetTimeArenaWasMadeEmpty() <= min_time) {
|
||||
qDebug() << "Removing an empty arena";
|
||||
delete arena;
|
||||
it = arenas_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = arenas_.begin(); it != arenas_.end();) {
|
||||
Arena *arena = (*it);
|
||||
|
||||
if (arena->GetUsageCount() == 0 &&
|
||||
arena->GetTimeArenaWasMadeEmpty() <= min_time) {
|
||||
qDebug() << "Removing an empty arena";
|
||||
delete arena;
|
||||
it = arenas_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+19
-18
@@ -20,28 +20,29 @@
|
||||
|
||||
#include "ocioutils.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OCIO::BIT_DEPTH_UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OCIO::BIT_DEPTH_UINT16;
|
||||
break;
|
||||
case PixelFormat::F16:
|
||||
return OCIO::BIT_DEPTH_F16;
|
||||
break;
|
||||
case PixelFormat::F32:
|
||||
return OCIO::BIT_DEPTH_F32;
|
||||
break;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OCIO::BIT_DEPTH_UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OCIO::BIT_DEPTH_UINT16;
|
||||
break;
|
||||
case PixelFormat::F16:
|
||||
return OCIO::BIT_DEPTH_F16;
|
||||
break;
|
||||
case PixelFormat::F32:
|
||||
return OCIO::BIT_DEPTH_F32;
|
||||
break;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return OCIO::BIT_DEPTH_UNKNOWN;
|
||||
return OCIO::BIT_DEPTH_UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ namespace OCIO = OCIO_NAMESPACE;
|
||||
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class OCIOUtils
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OCIOUtils {
|
||||
public:
|
||||
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
|
||||
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+37
-41
@@ -22,62 +22,58 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace olive {
|
||||
|
||||
void OIIOUtils::FrameToBuffer(const Frame* frame, OIIO::ImageBuf *buf)
|
||||
namespace olive
|
||||
{
|
||||
buf->set_pixels(OIIO::ROI(),
|
||||
buf->spec().format,
|
||||
frame->const_data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
|
||||
void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
|
||||
{
|
||||
buf->set_pixels(OIIO::ROI(), buf->spec().format, 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->spec().format,
|
||||
frame->data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
buf->get_pixels(OIIO::ROI(), buf->spec().format, frame->data(),
|
||||
OIIO::AutoStride, frame->linesize_bytes());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
switch (type) {
|
||||
case OIIO::TypeDesc::UNKNOWN:
|
||||
case OIIO::TypeDesc::NONE:
|
||||
break;
|
||||
switch (type) {
|
||||
case OIIO::TypeDesc::UNKNOWN:
|
||||
case OIIO::TypeDesc::NONE:
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::INT8:
|
||||
case OIIO::TypeDesc::INT16:
|
||||
case OIIO::TypeDesc::INT32:
|
||||
case OIIO::TypeDesc::UINT32:
|
||||
case OIIO::TypeDesc::INT64:
|
||||
case OIIO::TypeDesc::UINT64:
|
||||
case OIIO::TypeDesc::STRING:
|
||||
case OIIO::TypeDesc::PTR:
|
||||
case OIIO::TypeDesc::LASTBASE:
|
||||
case OIIO::TypeDesc::DOUBLE:
|
||||
qDebug() << "Tried to use unknown OIIO base type";
|
||||
break;
|
||||
case OIIO::TypeDesc::INT8:
|
||||
case OIIO::TypeDesc::INT16:
|
||||
case OIIO::TypeDesc::INT32:
|
||||
case OIIO::TypeDesc::UINT32:
|
||||
case OIIO::TypeDesc::INT64:
|
||||
case OIIO::TypeDesc::UINT64:
|
||||
case OIIO::TypeDesc::STRING:
|
||||
case OIIO::TypeDesc::PTR:
|
||||
case OIIO::TypeDesc::LASTBASE:
|
||||
case OIIO::TypeDesc::DOUBLE:
|
||||
qDebug() << "Tried to use unknown OIIO base type";
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::UINT8:
|
||||
return PixelFormat::U8;
|
||||
case OIIO::TypeDesc::UINT16:
|
||||
return PixelFormat::U16;
|
||||
case OIIO::TypeDesc::HALF:
|
||||
return PixelFormat::F16;
|
||||
case OIIO::TypeDesc::FLOAT:
|
||||
return PixelFormat::F32;
|
||||
}
|
||||
case OIIO::TypeDesc::UINT8:
|
||||
return PixelFormat::U8;
|
||||
case OIIO::TypeDesc::UINT16:
|
||||
return PixelFormat::U16;
|
||||
case OIIO::TypeDesc::HALF:
|
||||
return PixelFormat::F16;
|
||||
case OIIO::TypeDesc::FLOAT:
|
||||
return PixelFormat::F32;
|
||||
}
|
||||
|
||||
return PixelFormat::INVALID;
|
||||
return PixelFormat::INVALID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
-23
@@ -27,37 +27,38 @@
|
||||
#include "codec/frame.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OIIOUtils {
|
||||
public:
|
||||
static OIIO::TypeDesc::BASETYPE GetOIIOBaseTypeFromFormat(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OIIO::TypeDesc::UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OIIO::TypeDesc::UINT16;
|
||||
case PixelFormat::F16:
|
||||
return OIIO::TypeDesc::HALF;
|
||||
case PixelFormat::F32:
|
||||
return OIIO::TypeDesc::FLOAT;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
static OIIO::TypeDesc::BASETYPE
|
||||
GetOIIOBaseTypeFromFormat(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OIIO::TypeDesc::UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OIIO::TypeDesc::UINT16;
|
||||
case PixelFormat::F16:
|
||||
return OIIO::TypeDesc::HALF;
|
||||
case PixelFormat::F32:
|
||||
return OIIO::TypeDesc::FLOAT;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return OIIO::TypeDesc::UNKNOWN;
|
||||
}
|
||||
return OIIO::TypeDesc::UNKNOWN;
|
||||
}
|
||||
|
||||
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf* buf);
|
||||
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf);
|
||||
|
||||
static void BufferToFrame(OIIO::ImageBuf* buf, Frame* frame);
|
||||
static void BufferToFrame(OIIO::ImageBuf *buf, Frame *frame);
|
||||
|
||||
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
|
||||
|
||||
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
|
||||
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
|
||||
|
||||
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+16
-15
@@ -25,30 +25,31 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
uint32_t ceil_to_power_of_2(uint32_t v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
|
||||
return v;
|
||||
return v;
|
||||
}
|
||||
|
||||
uint32_t floor_to_power_of_2(uint32_t x)
|
||||
{
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
|
||||
return x - (x >> 1);
|
||||
return x - (x >> 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+125
-114
@@ -22,192 +22,203 @@
|
||||
|
||||
#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)
|
||||
return fm.width(s);
|
||||
return fm.width(s);
|
||||
#else
|
||||
return fm.horizontalAdvance(s);
|
||||
return fm.horizontalAdvance(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
QFrame *QtUtils::CreateHorizontalLine()
|
||||
{
|
||||
QFrame* horizontal_line = new QFrame();
|
||||
horizontal_line->setFrameShape(QFrame::HLine);
|
||||
horizontal_line->setFrameShadow(QFrame::Sunken);
|
||||
return horizontal_line;
|
||||
QFrame *horizontal_line = new QFrame();
|
||||
horizontal_line->setFrameShape(QFrame::HLine);
|
||||
horizontal_line->setFrameShadow(QFrame::Sunken);
|
||||
return horizontal_line;
|
||||
}
|
||||
|
||||
QFrame *QtUtils::CreateVerticalLine()
|
||||
{
|
||||
QFrame *l = CreateHorizontalLine();
|
||||
l->setFrameShape(QFrame::VLine);
|
||||
return l;
|
||||
QFrame *l = CreateHorizontalLine();
|
||||
l->setFrameShape(QFrame::VLine);
|
||||
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);
|
||||
b.setIcon(icon);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(title);
|
||||
b.setText(message);
|
||||
QMessageBox b(parent);
|
||||
b.setIcon(icon);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(title);
|
||||
b.setText(message);
|
||||
|
||||
uint mask = QMessageBox::FirstButton;
|
||||
while (mask <= QMessageBox::LastButton) {
|
||||
uint sb = buttons & mask;
|
||||
if (sb) {
|
||||
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
uint mask = QMessageBox::FirstButton;
|
||||
while (mask <= QMessageBox::LastButton) {
|
||||
uint sb = buttons & mask;
|
||||
if (sb) {
|
||||
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
return b.exec();
|
||||
return b.exec();
|
||||
}
|
||||
|
||||
QDateTime QtUtils::GetCreationDate(const QFileInfo &info)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
return info.created();
|
||||
return info.created();
|
||||
#else
|
||||
QDateTime t = info.birthTime();
|
||||
if (!t.isValid()) {
|
||||
t = info.metadataChangeTime();
|
||||
}
|
||||
return t;
|
||||
QDateTime t = info.birthTime();
|
||||
if (!t.isValid()) {
|
||||
t = info.metadataChangeTime();
|
||||
}
|
||||
return t;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
|
||||
{
|
||||
return dt.toString(Qt::TextDate);
|
||||
return dt.toString(Qt::TextDate);
|
||||
}
|
||||
|
||||
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width)
|
||||
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm,
|
||||
int bounding_width)
|
||||
{
|
||||
QStringList list;
|
||||
QStringList list;
|
||||
|
||||
QStringList lines = s.split('\n');
|
||||
QStringList lines = s.split('\n');
|
||||
|
||||
// Iterate every line
|
||||
for (int i=0; i<lines.size(); i++) {
|
||||
QString this_line = lines.at(i);
|
||||
// Iterate every line
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
QString this_line = lines.at(i);
|
||||
|
||||
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) {
|
||||
int old_size = this_line.size();
|
||||
int hard_break = -1;
|
||||
while (this_line.size() > 1 &&
|
||||
QFontMetricsWidth(fm, this_line) >= bounding_width) {
|
||||
int old_size = this_line.size();
|
||||
int hard_break = -1;
|
||||
|
||||
for (int j=this_line.size()-1; j>=0; j--) {
|
||||
const QChar &char_test = this_line.at(j);
|
||||
for (int j = this_line.size() - 1; j >= 0; j--) {
|
||||
const QChar &char_test = this_line.at(j);
|
||||
|
||||
if (char_test.isSpace()
|
||||
|| char_test == '-') {
|
||||
if (QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
|
||||
if (!char_test.isSpace()) {
|
||||
j++;
|
||||
}
|
||||
if (char_test.isSpace() || char_test == '-') {
|
||||
if (QFontMetricsWidth(fm, this_line.left(j)) <
|
||||
bounding_width) {
|
||||
if (!char_test.isSpace()) {
|
||||
j++;
|
||||
}
|
||||
|
||||
QString chopped = this_line.left(j);
|
||||
QString chopped = this_line.left(j);
|
||||
|
||||
list.append(chopped);
|
||||
list.append(chopped);
|
||||
|
||||
while (j < this_line.size() && this_line.at(j).isSpace()) {
|
||||
j++;
|
||||
}
|
||||
this_line.remove(0, j);
|
||||
break;
|
||||
}
|
||||
} else if (hard_break == -1 && QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
|
||||
// In case we can't find a better place to split, split at the earliest time the line
|
||||
// goes under the width limit
|
||||
hard_break = j;
|
||||
}
|
||||
}
|
||||
while (j < this_line.size() &&
|
||||
this_line.at(j).isSpace()) {
|
||||
j++;
|
||||
}
|
||||
this_line.remove(0, j);
|
||||
break;
|
||||
}
|
||||
} else if (hard_break == -1 &&
|
||||
QFontMetricsWidth(fm, this_line.left(j)) <
|
||||
bounding_width) {
|
||||
// In case we can't find a better place to split, split at the earliest time the line
|
||||
// goes under the width limit
|
||||
hard_break = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_size == this_line.size()) {
|
||||
if (hard_break != -1) {
|
||||
list.append(this_line.left(hard_break));
|
||||
this_line.remove(0, hard_break);
|
||||
} else {
|
||||
qWarning() << "Failed to find anywhere to wrap. Returning full line.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (old_size == this_line.size()) {
|
||||
if (hard_break != -1) {
|
||||
list.append(this_line.left(hard_break));
|
||||
this_line.remove(0, hard_break);
|
||||
} else {
|
||||
qWarning()
|
||||
<< "Failed to find anywhere to wrap. Returning full line.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this_line.isEmpty()) {
|
||||
list.append(this_line);
|
||||
}
|
||||
}
|
||||
if (!this_line.isEmpty()) {
|
||||
list.append(this_line);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
return list;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
|
||||
Qt::KeyboardModifiers
|
||||
QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
|
||||
{
|
||||
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
|
||||
return e;
|
||||
}
|
||||
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
|
||||
return e;
|
||||
}
|
||||
|
||||
if (e & Qt::ShiftModifier) {
|
||||
e |= Qt::ControlModifier;
|
||||
e &= ~Qt::ShiftModifier;
|
||||
} else if (e & Qt::ControlModifier) {
|
||||
e |= Qt::ShiftModifier;
|
||||
e &= ~Qt::ControlModifier;
|
||||
}
|
||||
if (e & Qt::ShiftModifier) {
|
||||
e |= Qt::ControlModifier;
|
||||
e &= ~Qt::ShiftModifier;
|
||||
} else if (e & Qt::ControlModifier) {
|
||||
e |= Qt::ShiftModifier;
|
||||
e &= ~Qt::ControlModifier;
|
||||
}
|
||||
|
||||
return e;
|
||||
return e;
|
||||
}
|
||||
|
||||
void QtUtils::SetComboBoxData(QComboBox *cb, int data)
|
||||
{
|
||||
for (int i=0; i<cb->count(); i++) {
|
||||
if (cb->itemData(i).toInt() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cb->count(); i++) {
|
||||
if (cb->itemData(i).toInt() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data)
|
||||
{
|
||||
for (int i=0; i<cb->count(); i++) {
|
||||
if (cb->itemData(i).toString() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cb->count(); i++) {
|
||||
if (cb->itemData(i).toString() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QColor QtUtils::toQColor(const core::Color &i)
|
||||
{
|
||||
QColor c;
|
||||
QColor c;
|
||||
|
||||
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
||||
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
||||
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
||||
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
||||
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
||||
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
||||
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
||||
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
||||
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
||||
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
||||
|
||||
return c;
|
||||
return c;
|
||||
}
|
||||
|
||||
namespace core {
|
||||
namespace core
|
||||
{
|
||||
|
||||
uint qHash(const core::rational &r, uint seed)
|
||||
{
|
||||
return ::qHash(r.toDouble(), seed);
|
||||
return ::qHash(r.toDouble(), seed);
|
||||
}
|
||||
|
||||
uint qHash(const core::TimeRange &r, uint seed)
|
||||
{
|
||||
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
|
||||
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+43
-40
@@ -29,76 +29,79 @@
|
||||
#include <QFrame>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class QtUtils {
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves the width of a string according to certain QFontMetrics
|
||||
*
|
||||
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the
|
||||
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
|
||||
* earlier.
|
||||
*/
|
||||
static int QFontMetricsWidth(QFontMetrics fm, const QString& s);
|
||||
static int QFontMetricsWidth(QFontMetrics fm, const QString &s);
|
||||
|
||||
static QFrame* CreateHorizontalLine();
|
||||
static QFrame *CreateHorizontalLine();
|
||||
|
||||
static QFrame* CreateVerticalLine();
|
||||
static QFrame *CreateVerticalLine();
|
||||
|
||||
static int MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
static int MsgBox(QWidget *parent, QMessageBox::Icon icon,
|
||||
const QString &title, const QString &message,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||
|
||||
static QString GetFormattedDateTime(const QDateTime &dt);
|
||||
static QString GetFormattedDateTime(const QDateTime &dt);
|
||||
|
||||
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width);
|
||||
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm,
|
||||
int bounding_width);
|
||||
|
||||
static Qt::KeyboardModifiers FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
|
||||
static Qt::KeyboardModifiers
|
||||
FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
|
||||
|
||||
static void SetComboBoxData(QComboBox *cb, int data);
|
||||
static void SetComboBoxData(QComboBox *cb, const QString &data);
|
||||
static void SetComboBoxData(QComboBox *cb, int data);
|
||||
static void SetComboBoxData(QComboBox *cb, const QString &data);
|
||||
|
||||
template <typename T>
|
||||
static T *GetParentOfType(const QObject *child)
|
||||
{
|
||||
QObject *t = child->parent();
|
||||
template <typename T> static T *GetParentOfType(const QObject *child)
|
||||
{
|
||||
QObject *t = child->parent();
|
||||
|
||||
while (t) {
|
||||
if (T *p = dynamic_cast<T*>(t)) {
|
||||
return p;
|
||||
}
|
||||
t = t->parent();
|
||||
}
|
||||
while (t) {
|
||||
if (T *p = dynamic_cast<T *>(t)) {
|
||||
return p;
|
||||
}
|
||||
t = t->parent();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static QColor toQColor(const core::Color &c);
|
||||
static QColor toQColor(const core::Color &c);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Convert a pointer to a value that can be sent between NodeParams
|
||||
*/
|
||||
static QVariant PtrToValue(void* ptr)
|
||||
{
|
||||
return reinterpret_cast<quintptr>(ptr);
|
||||
}
|
||||
static QVariant PtrToValue(void *ptr)
|
||||
{
|
||||
return reinterpret_cast<quintptr>(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Convert a NodeParam value to a pointer of any kind
|
||||
*/
|
||||
template<class T>
|
||||
static T* ValueToPtr(const QVariant &ptr)
|
||||
{
|
||||
return reinterpret_cast<T*>(ptr.value<quintptr>());
|
||||
}
|
||||
|
||||
template <class T> static T *ValueToPtr(const QVariant &ptr)
|
||||
{
|
||||
return reinterpret_cast<T *>(ptr.value<quintptr>());
|
||||
}
|
||||
};
|
||||
|
||||
namespace core {
|
||||
namespace core
|
||||
{
|
||||
|
||||
uint qHash(const core::rational& r, uint seed = 0);
|
||||
uint qHash(const core::TimeRange& r, uint seed = 0);
|
||||
uint qHash(const core::rational &r, uint seed = 0);
|
||||
uint qHash(const core::TimeRange &r, uint seed = 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -21,10 +21,9 @@
|
||||
#ifndef RANGE_H
|
||||
#define RANGE_H
|
||||
|
||||
template <typename T>
|
||||
bool InRange(T a, T b, T range)
|
||||
template <typename T> bool InRange(T a, T b, T range)
|
||||
{
|
||||
return (a >= b - range && a <= b + range);
|
||||
return (a >= b - range && a <= b + range);
|
||||
}
|
||||
|
||||
#endif // RANGE_H
|
||||
|
||||
+54
-52
@@ -24,69 +24,71 @@
|
||||
#include <QMessageBox>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace olive {
|
||||
|
||||
double GetFloatRatioFromUser(QWidget* parent,
|
||||
const QString& title,
|
||||
bool* ok_in)
|
||||
namespace olive
|
||||
{
|
||||
QString s;
|
||||
|
||||
forever {
|
||||
bool ok;
|
||||
double GetFloatRatioFromUser(QWidget *parent, const QString &title, bool *ok_in)
|
||||
{
|
||||
QString s;
|
||||
|
||||
s = QInputDialog::getText(parent,
|
||||
title,
|
||||
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||
QLineEdit::Normal,
|
||||
s,
|
||||
&ok);
|
||||
forever
|
||||
{
|
||||
bool ok;
|
||||
|
||||
if (!ok) {
|
||||
// User cancelled dialog, do nothing
|
||||
if (ok_in) {
|
||||
*ok_in = false;
|
||||
}
|
||||
return qSNaN();
|
||||
}
|
||||
s = QInputDialog::getText(
|
||||
parent, title,
|
||||
QCoreApplication::translate(
|
||||
"RatioDialog",
|
||||
"Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||
QLineEdit::Normal, s, &ok);
|
||||
|
||||
QStringList ratio_components = s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
||||
if (!ok) {
|
||||
// User cancelled dialog, do nothing
|
||||
if (ok_in) {
|
||||
*ok_in = false;
|
||||
}
|
||||
return qSNaN();
|
||||
}
|
||||
|
||||
if (ratio_components.size() == 1) {
|
||||
bool float_ok;
|
||||
QStringList ratio_components =
|
||||
s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
||||
|
||||
double flt = ratio_components.at(0).toDouble(&float_ok);
|
||||
if (ratio_components.size() == 1) {
|
||||
bool float_ok;
|
||||
|
||||
if (float_ok && flt > 0) {
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return flt;
|
||||
}
|
||||
} else if (ratio_components.size() == 2) {
|
||||
bool numer_ok, denom_ok;
|
||||
double flt = ratio_components.at(0).toDouble(&float_ok);
|
||||
|
||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||
if (float_ok && flt > 0) {
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return flt;
|
||||
}
|
||||
} else if (ratio_components.size() == 2) {
|
||||
bool numer_ok, denom_ok;
|
||||
|
||||
if (numer_ok
|
||||
&& denom_ok
|
||||
&& num > 0
|
||||
&& den > 0) {
|
||||
// Exit loop and set this ratio
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
}
|
||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||
|
||||
QMessageBox::warning(parent,
|
||||
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
||||
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||
"rational fraction with a ':' or a '/' separator.").arg(s),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
if (numer_ok && denom_ok && num > 0 && den > 0) {
|
||||
// Exit loop and set this ratio
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
}
|
||||
|
||||
QMessageBox::warning(
|
||||
parent,
|
||||
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
||||
QCoreApplication::translate(
|
||||
"RatioDialog",
|
||||
"Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||
"rational fraction with a ':' or a '/' separator.")
|
||||
.arg(s),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
|
||||
#include <QInputDialog>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
double GetFloatRatioFromUser(QWidget* parent,
|
||||
const QString& title,
|
||||
bool* ok_in);
|
||||
double GetFloatRatioFromUser(QWidget *parent, const QString &title,
|
||||
bool *ok_in);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -4,24 +4,21 @@
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
|
||||
template <typename K, typename V>
|
||||
class ThreadSafeMap
|
||||
{
|
||||
template <typename K, typename V> class ThreadSafeMap {
|
||||
public:
|
||||
ThreadSafeMap() = default;
|
||||
ThreadSafeMap() = default;
|
||||
|
||||
void insert(K key, V value)
|
||||
{
|
||||
mutex_.lock();
|
||||
map_.insert(key, value);
|
||||
mutex_.unlock();
|
||||
}
|
||||
void insert(K key, V value)
|
||||
{
|
||||
mutex_.lock();
|
||||
map_.insert(key, value);
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
QMutex mutex_;
|
||||
|
||||
QMap<K, V> map_;
|
||||
QMutex mutex_;
|
||||
|
||||
QMap<K, V> map_;
|
||||
};
|
||||
|
||||
#endif // THREADSAFEMAP_H
|
||||
|
||||
+5
-3
@@ -6,10 +6,12 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
inline QString ToHex(quint64 t) {
|
||||
return QStringLiteral("%1").arg(t, 0, 16);
|
||||
inline QString ToHex(quint64 t)
|
||||
{
|
||||
return QStringLiteral("%1").arg(t, 0, 16);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -21,10 +21,9 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
template <typename T>
|
||||
inline T mid(T a, T b)
|
||||
template <typename T> inline T mid(T a, T b)
|
||||
{
|
||||
return (a + b) * 0.5;
|
||||
return (a + b) * 0.5;
|
||||
}
|
||||
|
||||
#endif // UTIL_H
|
||||
|
||||
+13
-12
@@ -23,23 +23,24 @@
|
||||
#include "node/block/block.h"
|
||||
#include "node/factory.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
|
||||
{
|
||||
QXmlStreamReader::TokenType token;
|
||||
QXmlStreamReader::TokenType token;
|
||||
|
||||
while ((token = reader->readNext()) != QXmlStreamReader::Invalid
|
||||
&& token != QXmlStreamReader::EndDocument
|
||||
&& (!cancel_atom || !cancel_atom->IsCancelled())) {
|
||||
if (reader->isEndElement()) {
|
||||
return false;
|
||||
} else if (reader->isStartElement()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
|
||||
token != QXmlStreamReader::EndDocument &&
|
||||
(!cancel_atom || !cancel_atom->IsCancelled())) {
|
||||
if (reader->isEndElement()) {
|
||||
return false;
|
||||
} else if (reader->isStartElement()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
#include "render/cancelatom.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class Block;
|
||||
class Node;
|
||||
@@ -35,7 +36,7 @@ class NodeInput;
|
||||
class NodeGroup;
|
||||
|
||||
#define XMLAttributeLoop(reader, item) \
|
||||
foreach (const QXmlStreamAttribute& item, reader->attributes())
|
||||
foreach (const QXmlStreamAttribute &item, reader->attributes())
|
||||
|
||||
/**
|
||||
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
|
||||
@@ -46,7 +47,8 @@ class NodeGroup;
|
||||
*
|
||||
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
|
||||
*/
|
||||
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr);
|
||||
bool XMLReadNextStartElement(QXmlStreamReader *reader,
|
||||
CancelAtom *cancel_atom = nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+297
-189
@@ -37,278 +37,386 @@
|
||||
#include "ui/style/style.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
Config Config::current_config_;
|
||||
|
||||
Config::Config()
|
||||
{
|
||||
SetDefaults();
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data)
|
||||
void Config::SetEntryInternal(const QString &key, NodeValue::Type type,
|
||||
const QVariant &data)
|
||||
{
|
||||
config_map_[key] = {type, data};
|
||||
config_map_[key] = { type, data };
|
||||
}
|
||||
|
||||
QString Config::GetConfigFilePath()
|
||||
{
|
||||
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("config.xml"));
|
||||
return QDir(FileFunctions::GetConfigurationLocation())
|
||||
.filePath(QStringLiteral("config.xml"));
|
||||
}
|
||||
|
||||
Config &Config::Current()
|
||||
{
|
||||
return current_config_;
|
||||
return current_config_;
|
||||
}
|
||||
|
||||
void Config::SetDefaults()
|
||||
{
|
||||
config_map_.clear();
|
||||
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle);
|
||||
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame);
|
||||
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2)));
|
||||
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true);
|
||||
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("ScrollZooms"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), 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("SeekAlsoSelects"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage);
|
||||
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk);
|
||||
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
|
||||
config_map_.clear();
|
||||
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText,
|
||||
StyleManager::kDefaultStyle);
|
||||
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt,
|
||||
Timecode::kTimecodeDropFrame);
|
||||
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational,
|
||||
QVariant::fromValue(rational(2)));
|
||||
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean,
|
||||
true);
|
||||
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("ScrollZooms"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean,
|
||||
false);
|
||||
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean,
|
||||
false);
|
||||
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"),
|
||||
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("SeekAlsoSelects"), NodeValue::kBoolean,
|
||||
false);
|
||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
|
||||
false);
|
||||
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"),
|
||||
NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"),
|
||||
NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean,
|
||||
false);
|
||||
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt,
|
||||
AutoScroll::kPage);
|
||||
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
|
||||
false);
|
||||
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"),
|
||||
NodeValue::kInt, ImportTool::kDWSAsk);
|
||||
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean,
|
||||
true);
|
||||
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"),
|
||||
NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"),
|
||||
NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"),
|
||||
NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean,
|
||||
false);
|
||||
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"),
|
||||
NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
|
||||
|
||||
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailInOut);
|
||||
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled);
|
||||
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt,
|
||||
Timeline::kThumbnailInOut);
|
||||
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt,
|
||||
Timeline::kWaveformsEnabled);
|
||||
|
||||
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, 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("DefaultVideoTransition"), NodeValue::kText,
|
||||
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("DefaultSubtitleFamily"), NodeValue::kText, QString());
|
||||
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt, QFont::Bold);
|
||||
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean, true);
|
||||
SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt,
|
||||
48);
|
||||
SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText,
|
||||
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("CatColor1"), NodeValue::kInt, ColorCoding::kMaroon);
|
||||
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt, ColorCoding::kOrange);
|
||||
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt, ColorCoding::kBrown);
|
||||
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt, ColorCoding::kYellow);
|
||||
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt, 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("CatColor0"), NodeValue::kInt,
|
||||
ColorCoding::kRed);
|
||||
SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt,
|
||||
ColorCoding::kMaroon);
|
||||
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt,
|
||||
ColorCoding::kOrange);
|
||||
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt,
|
||||
ColorCoding::kBrown);
|
||||
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt,
|
||||
ColorCoding::kYellow);
|
||||
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt,
|
||||
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("AudioInput"), NodeValue::kText, QString());
|
||||
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText,
|
||||
QString());
|
||||
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
|
||||
|
||||
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000);
|
||||
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
|
||||
SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
|
||||
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt,
|
||||
48000);
|
||||
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("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), 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("AudioRecordingFormat"), NodeValue::kInt,
|
||||
ExportFormat::kFormatWAV);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt,
|
||||
ExportCodec::kCodecPCM);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"),
|
||||
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("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60)));
|
||||
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational,
|
||||
QVariant::fromValue(rational(0)));
|
||||
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational,
|
||||
QVariant::fromValue(rational(60)));
|
||||
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920);
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080);
|
||||
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1)));
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), 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)));
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt,
|
||||
1920);
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt,
|
||||
1080);
|
||||
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"),
|
||||
NodeValue::kRational, QVariant::fromValue(rational(1)));
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"),
|
||||
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
|
||||
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, PixelFormat::F32);
|
||||
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, PixelFormat::F16);
|
||||
// Online/offline settings
|
||||
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt,
|
||||
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()
|
||||
{
|
||||
QFile config_file(GetConfigFilePath());
|
||||
QFile config_file(GetConfigFilePath());
|
||||
|
||||
if (!config_file.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!config_file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config_file.open(QFile::ReadOnly)) {
|
||||
qWarning() << "Failed to load application settings. This session will use defaults.";
|
||||
return;
|
||||
}
|
||||
if (!config_file.open(QFile::ReadOnly)) {
|
||||
qWarning()
|
||||
<< "Failed to load application settings. This session will use defaults.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset to defaults
|
||||
current_config_.SetDefaults();
|
||||
// Reset to defaults
|
||||
current_config_.SetDefaults();
|
||||
|
||||
QXmlStreamReader reader(&config_file);
|
||||
QXmlStreamReader reader(&config_file);
|
||||
|
||||
QString config_version;
|
||||
QString config_version;
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("Configuration")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
QString key = reader.name().toString();
|
||||
QString value = reader.readElementText();
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("Configuration")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
QString key = reader.name().toString();
|
||||
QString value = reader.readElementText();
|
||||
|
||||
if (key == QStringLiteral("Version")) {
|
||||
config_version = value;
|
||||
if (key == QStringLiteral("Version")) {
|
||||
config_version = value;
|
||||
|
||||
if (!value.contains(".")) {
|
||||
qDebug() << "CONFIG: This is a 0.1.x config file, upconvert";
|
||||
}
|
||||
} else if (key == QStringLiteral("DefaultSequenceFrameRate") && !config_version.contains('.')) {
|
||||
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
|
||||
// supported rational
|
||||
qDebug() << " CONFIG: Finding closest match to" << value;
|
||||
if (!value.contains(".")) {
|
||||
qDebug()
|
||||
<< "CONFIG: This is a 0.1.x config file, upconvert";
|
||||
}
|
||||
} else if (key == QStringLiteral("DefaultSequenceFrameRate") &&
|
||||
!config_version.contains('.')) {
|
||||
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
|
||||
// supported rational
|
||||
qDebug() << " CONFIG: Finding closest match to" << value;
|
||||
|
||||
double config_fr = value.toDouble();
|
||||
double config_fr = value.toDouble();
|
||||
|
||||
const QVector<rational>& supported_frame_rates = VideoParams::kSupportedFrameRates;
|
||||
const QVector<rational> &supported_frame_rates =
|
||||
VideoParams::kSupportedFrameRates;
|
||||
|
||||
rational match = supported_frame_rates.first();
|
||||
double match_diff = qAbs(match.toDouble() - config_fr);
|
||||
rational match = supported_frame_rates.first();
|
||||
double match_diff = qAbs(match.toDouble() - config_fr);
|
||||
|
||||
for (int i=1;i<supported_frame_rates.size();i++) {
|
||||
double diff = qAbs(supported_frame_rates.at(i).toDouble() - config_fr);
|
||||
for (int i = 1; i < supported_frame_rates.size(); i++) {
|
||||
double diff = qAbs(
|
||||
supported_frame_rates.at(i).toDouble() - config_fr);
|
||||
|
||||
if (diff < match_diff) {
|
||||
match = supported_frame_rates.at(i);
|
||||
match_diff = diff;
|
||||
}
|
||||
}
|
||||
if (diff < match_diff) {
|
||||
match = supported_frame_rates.at(i);
|
||||
match_diff = diff;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << " CONFIG: Closest match was" << match.toDouble();
|
||||
qDebug()
|
||||
<< " CONFIG: Closest match was" << match.toDouble();
|
||||
|
||||
current_config_[key] = QVariant::fromValue(match.flipped());
|
||||
} else {
|
||||
current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false);
|
||||
}
|
||||
}
|
||||
current_config_[key] = QVariant::fromValue(match.flipped());
|
||||
} else {
|
||||
current_config_[key] = NodeValue::StringToValue(
|
||||
current_config_.GetConfigEntryType(key), value, false);
|
||||
}
|
||||
}
|
||||
|
||||
//reader.skipCurrentElement();
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
//reader.skipCurrentElement();
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.hasError()) {
|
||||
QMessageBox::critical(Core::instance()->main_window(),
|
||||
QCoreApplication::translate("Config", "Error loading settings"),
|
||||
QCoreApplication::translate("Config", "Failed to load application settings. This session will "
|
||||
"use defaults.\n\n%1").arg(reader.errorString()),
|
||||
QMessageBox::Ok);
|
||||
current_config_.SetDefaults();
|
||||
}
|
||||
if (reader.hasError()) {
|
||||
QMessageBox::critical(
|
||||
Core::instance()->main_window(),
|
||||
QCoreApplication::translate("Config", "Error loading settings"),
|
||||
QCoreApplication::translate(
|
||||
"Config",
|
||||
"Failed to load application settings. This session will "
|
||||
"use defaults.\n\n%1")
|
||||
.arg(reader.errorString()),
|
||||
QMessageBox::Ok);
|
||||
current_config_.SetDefaults();
|
||||
}
|
||||
|
||||
config_file.close();
|
||||
config_file.close();
|
||||
}
|
||||
|
||||
void Config::Save()
|
||||
{
|
||||
QString real_filename = GetConfigFilePath();
|
||||
QString temp_filename = FileFunctions::GetSafeTemporaryFilename(real_filename);
|
||||
QString real_filename = GetConfigFilePath();
|
||||
QString temp_filename =
|
||||
FileFunctions::GetSafeTemporaryFilename(real_filename);
|
||||
|
||||
QFile config_file(temp_filename);
|
||||
QFile config_file(temp_filename);
|
||||
|
||||
if (!config_file.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(Core::instance()->main_window(),
|
||||
QCoreApplication::translate("Config", "Error saving settings"),
|
||||
QCoreApplication::translate("Config", "Failed to save application settings. The application "
|
||||
"may lack write permissions for this location."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
if (!config_file.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(
|
||||
Core::instance()->main_window(),
|
||||
QCoreApplication::translate("Config", "Error saving settings"),
|
||||
QCoreApplication::translate(
|
||||
"Config",
|
||||
"Failed to save application settings. The application "
|
||||
"may lack write permissions for this location."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamWriter writer(&config_file);
|
||||
writer.setAutoFormatting(true);
|
||||
QXmlStreamWriter writer(&config_file);
|
||||
writer.setAutoFormatting(true);
|
||||
|
||||
writer.writeStartDocument();
|
||||
writer.writeStartDocument();
|
||||
|
||||
writer.writeStartElement("Configuration");
|
||||
writer.writeStartElement("Configuration");
|
||||
|
||||
// Anything after the hyphen is considered "unimportant" information
|
||||
writer.writeTextElement("Version", QCoreApplication::applicationVersion().split('-').first());
|
||||
// Anything after the hyphen is considered "unimportant" information
|
||||
writer.writeTextElement(
|
||||
"Version", QCoreApplication::applicationVersion().split('-').first());
|
||||
|
||||
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
|
||||
while (iterator.hasNext()) {
|
||||
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) {
|
||||
qWarning() << "Config key" << iterator.key() << "had null type and was discarded";
|
||||
} else {
|
||||
writer.writeTextElement(iterator.key(), value);
|
||||
}
|
||||
}
|
||||
if (iterator.value().type == NodeValue::kNone) {
|
||||
qWarning() << "Config key" << iterator.key()
|
||||
<< "had null type and was discarded";
|
||||
} else {
|
||||
writer.writeTextElement(iterator.key(), value);
|
||||
}
|
||||
}
|
||||
|
||||
writer.writeEndElement(); // Configuration
|
||||
writer.writeEndElement(); // Configuration
|
||||
|
||||
writer.writeEndDocument();
|
||||
writer.writeEndDocument();
|
||||
|
||||
config_file.close();
|
||||
config_file.close();
|
||||
|
||||
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename, real_filename)) {
|
||||
qWarning() << QStringLiteral("Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
|
||||
.arg(real_filename, temp_filename);
|
||||
}
|
||||
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename,
|
||||
real_filename)) {
|
||||
qWarning()
|
||||
<< QStringLiteral(
|
||||
"Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
|
||||
.arg(real_filename, temp_filename);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Config::operator[](const QString &key) const
|
||||
{
|
||||
return config_map_[key].data;
|
||||
return config_map_[key].data;
|
||||
}
|
||||
|
||||
QVariant &Config::operator[](const QString &key)
|
||||
{
|
||||
return config_map_[key].data;
|
||||
return config_map_[key].data;
|
||||
}
|
||||
|
||||
NodeValue::Type Config::GetConfigEntryType(const QString &key) const
|
||||
{
|
||||
return config_map_[key].type;
|
||||
return config_map_[key].type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+19
-17
@@ -27,42 +27,44 @@
|
||||
|
||||
#include "node/value.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
|
||||
#define OLIVE_CONFIG_STR(x) Config::Current()[x]
|
||||
|
||||
class Config {
|
||||
public:
|
||||
static Config& Current();
|
||||
static Config &Current();
|
||||
|
||||
void SetDefaults();
|
||||
void SetDefaults();
|
||||
|
||||
static void Load();
|
||||
static void Load();
|
||||
|
||||
static void Save();
|
||||
static void Save();
|
||||
|
||||
QVariant operator[](const QString&) const;
|
||||
QVariant operator[](const QString &) const;
|
||||
|
||||
QVariant& operator[](const QString&);
|
||||
QVariant &operator[](const QString &);
|
||||
|
||||
NodeValue::Type GetConfigEntryType(const QString& key) const;
|
||||
NodeValue::Type GetConfigEntryType(const QString &key) const;
|
||||
|
||||
private:
|
||||
Config();
|
||||
Config();
|
||||
|
||||
struct ConfigEntry {
|
||||
NodeValue::Type type;
|
||||
QVariant data;
|
||||
};
|
||||
struct ConfigEntry {
|
||||
NodeValue::Type type;
|
||||
QVariant data;
|
||||
};
|
||||
|
||||
void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data);
|
||||
void SetEntryInternal(const QString &key, NodeValue::Type type,
|
||||
const QVariant &data);
|
||||
|
||||
QMap<QString, ConfigEntry> config_map_;
|
||||
QMap<QString, ConfigEntry> config_map_;
|
||||
|
||||
static Config current_config_;
|
||||
static Config current_config_;
|
||||
|
||||
static QString GetConfigFilePath();
|
||||
static QString GetConfigFilePath();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+1042
-956
File diff suppressed because it is too large
Load Diff
+256
-255
@@ -35,7 +35,8 @@
|
||||
#include "undo/undostack.h"
|
||||
#include "widget/projectexplorer/projectviewmodel.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class MainWindow;
|
||||
|
||||
@@ -48,132 +49,125 @@ class MainWindow;
|
||||
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
||||
* opening the import dialog, etc.)
|
||||
*/
|
||||
class Core : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class Core : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class CoreParams
|
||||
{
|
||||
public:
|
||||
CoreParams();
|
||||
class CoreParams {
|
||||
public:
|
||||
CoreParams();
|
||||
|
||||
enum RunMode {
|
||||
kRunNormal,
|
||||
kHeadlessExport,
|
||||
kHeadlessPreCache
|
||||
};
|
||||
enum RunMode { kRunNormal, kHeadlessExport, kHeadlessPreCache };
|
||||
|
||||
bool fullscreen() const
|
||||
{
|
||||
return run_fullscreen_;
|
||||
}
|
||||
bool fullscreen() const
|
||||
{
|
||||
return run_fullscreen_;
|
||||
}
|
||||
|
||||
void set_fullscreen(bool e)
|
||||
{
|
||||
run_fullscreen_ = e;
|
||||
}
|
||||
void set_fullscreen(bool e)
|
||||
{
|
||||
run_fullscreen_ = e;
|
||||
}
|
||||
|
||||
RunMode run_mode() const
|
||||
{
|
||||
return mode_;
|
||||
}
|
||||
RunMode run_mode() const
|
||||
{
|
||||
return mode_;
|
||||
}
|
||||
|
||||
void set_run_mode(RunMode m)
|
||||
{
|
||||
mode_ = m;
|
||||
}
|
||||
void set_run_mode(RunMode m)
|
||||
{
|
||||
mode_ = m;
|
||||
}
|
||||
|
||||
const QString startup_project() const
|
||||
{
|
||||
return startup_project_;
|
||||
}
|
||||
const QString startup_project() const
|
||||
{
|
||||
return startup_project_;
|
||||
}
|
||||
|
||||
void set_startup_project(const QString& p)
|
||||
{
|
||||
startup_project_ = p;
|
||||
}
|
||||
void set_startup_project(const QString &p)
|
||||
{
|
||||
startup_project_ = p;
|
||||
}
|
||||
|
||||
const QString& startup_language() const
|
||||
{
|
||||
return startup_language_;
|
||||
}
|
||||
const QString &startup_language() const
|
||||
{
|
||||
return startup_language_;
|
||||
}
|
||||
|
||||
void set_startup_language(const QString& s)
|
||||
{
|
||||
startup_language_ = s;
|
||||
}
|
||||
void set_startup_language(const QString &s)
|
||||
{
|
||||
startup_language_ = s;
|
||||
}
|
||||
|
||||
bool crash_on_startup() const
|
||||
{
|
||||
return crash_;
|
||||
}
|
||||
bool crash_on_startup() const
|
||||
{
|
||||
return crash_;
|
||||
}
|
||||
|
||||
void set_crash_on_startup(bool e)
|
||||
{
|
||||
crash_ = true;
|
||||
}
|
||||
void set_crash_on_startup(bool e)
|
||||
{
|
||||
crash_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
RunMode mode_;
|
||||
private:
|
||||
RunMode mode_;
|
||||
|
||||
QString startup_project_;
|
||||
QString startup_project_;
|
||||
|
||||
QString startup_language_;
|
||||
QString startup_language_;
|
||||
|
||||
bool run_fullscreen_;
|
||||
bool run_fullscreen_;
|
||||
|
||||
bool crash_;
|
||||
bool crash_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Core Constructor
|
||||
*
|
||||
* Currently empty
|
||||
*/
|
||||
Core(const CoreParams& params);
|
||||
Core(const CoreParams ¶ms);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Core object accessible from anywhere in the code
|
||||
*
|
||||
* Use this to access Core functions.
|
||||
*/
|
||||
static Core* instance();
|
||||
static Core *instance();
|
||||
|
||||
const CoreParams& core_params() const
|
||||
{
|
||||
return core_params_;
|
||||
}
|
||||
const CoreParams &core_params() const
|
||||
{
|
||||
return core_params_;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Start Olive Core
|
||||
*
|
||||
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
|
||||
*/
|
||||
void Start();
|
||||
void Start();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Stop Olive Core
|
||||
*
|
||||
* Ends all threads and frees all memory ready for the application to exit.
|
||||
*/
|
||||
void Stop();
|
||||
void Stop();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieve main window instance
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
|
||||
*/
|
||||
MainWindow* main_window();
|
||||
MainWindow *main_window();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieve UndoStack object
|
||||
*/
|
||||
UndoStack* undo_stack();
|
||||
UndoStack *undo_stack();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Import a list of files
|
||||
*
|
||||
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
|
||||
@@ -181,34 +175,34 @@ public:
|
||||
*
|
||||
* @param urls
|
||||
*/
|
||||
void ImportFiles(const QStringList& urls, Folder *parent);
|
||||
void ImportFiles(const QStringList &urls, Folder *parent);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get the currently active tool
|
||||
*/
|
||||
const Tool::Item& tool() const;
|
||||
const Tool::Item &tool() const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get the currently selected object that the add tool should make (if the add tool is active)
|
||||
*/
|
||||
const Tool::AddableObject& GetSelectedAddableObject() const;
|
||||
const Tool::AddableObject &GetSelectedAddableObject() const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
|
||||
*/
|
||||
const QString& GetSelectedTransition() const;
|
||||
const QString &GetSelectedTransition() const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get current snapping value
|
||||
*/
|
||||
const bool& snapping() const;
|
||||
const bool &snapping() const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns a list of the most recently opened/saved projects
|
||||
*/
|
||||
const QStringList& GetRecentProjects() const;
|
||||
const QStringList &GetRecentProjects() const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get the currently active project
|
||||
*
|
||||
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
|
||||
@@ -218,402 +212,409 @@ public:
|
||||
*
|
||||
* The active Project file, or nullptr if the heuristic couldn't find one.
|
||||
*/
|
||||
Project* GetActiveProject() const;
|
||||
Folder* GetSelectedFolderInActiveProject() const;
|
||||
Project *GetActiveProject() const;
|
||||
Folder *GetSelectedFolderInActiveProject() const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Gets current timecode display mode
|
||||
*/
|
||||
Timecode::Display GetTimecodeDisplay() const;
|
||||
Timecode::Display GetTimecodeDisplay() const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Sets current timecode display mode
|
||||
*/
|
||||
void SetTimecodeDisplay(Timecode::Display d);
|
||||
void SetTimecodeDisplay(Timecode::Display d);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
|
||||
*/
|
||||
void SetAutorecoveryInterval(int minutes);
|
||||
void SetAutorecoveryInterval(int minutes);
|
||||
|
||||
static void CopyStringToClipboard(const QString& s);
|
||||
static void CopyStringToClipboard(const QString &s);
|
||||
|
||||
static QString PasteStringFromClipboard();
|
||||
static QString PasteStringFromClipboard();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Recursively count files in a file/directory list
|
||||
*/
|
||||
static int CountFilesInFileList(const QFileInfoList &filenames);
|
||||
static int CountFilesInFileList(const QFileInfoList &filenames);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Show a dialog to the user to rename a set of nodes
|
||||
*/
|
||||
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
|
||||
*/
|
||||
static Sequence* CreateNewSequenceForProject(const QString &format, Project *project);
|
||||
static Sequence* CreateNewSequenceForProject(Project *project)
|
||||
{
|
||||
return CreateNewSequenceForProject(tr("Sequence %1"), project);
|
||||
}
|
||||
static Sequence *CreateNewSequenceForProject(const QString &format,
|
||||
Project *project);
|
||||
static Sequence *CreateNewSequenceForProject(Project *project)
|
||||
{
|
||||
return CreateNewSequenceForProject(tr("Sequence %1"), project);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Opens a project from the recently opened list
|
||||
*/
|
||||
void OpenProjectFromRecentList(int index);
|
||||
void OpenProjectFromRecentList(int index);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Closes a project
|
||||
*/
|
||||
bool CloseProject(bool auto_open_new, bool ignore_modified = false);
|
||||
bool CloseProject(bool auto_open_new, bool ignore_modified = false);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Runs a modal cache task on the currently active sequence
|
||||
*/
|
||||
void CacheActiveSequence(bool in_out_only);
|
||||
void CacheActiveSequence(bool in_out_only);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Check each footage object for whether it still exists or has changed
|
||||
*/
|
||||
bool ValidateFootageInLoadedProject(Project* project, const QString &project_saved_url);
|
||||
bool ValidateFootageInLoadedProject(Project *project,
|
||||
const QString &project_saved_url);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Changes the current language
|
||||
*/
|
||||
bool SetLanguage(const QString& locale);
|
||||
bool SetLanguage(const QString &locale);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Show message in main window's status bar
|
||||
*
|
||||
* Shorthand for Core::instance()->main_window()->statusBar()->showMessage();
|
||||
*/
|
||||
void ShowStatusBarMessage(const QString& s, int timeout = 0);
|
||||
void ShowStatusBarMessage(const QString &s, int timeout = 0);
|
||||
|
||||
void ClearStatusBarMessage();
|
||||
void ClearStatusBarMessage();
|
||||
|
||||
void OpenRecoveryProject(const QString& filename);
|
||||
void OpenRecoveryProject(const QString &filename);
|
||||
|
||||
void OpenNodeInViewer(ViewerOutput* viewer);
|
||||
void OpenNodeInViewer(ViewerOutput *viewer);
|
||||
|
||||
void OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image);
|
||||
void OpenExportDialogForViewer(ViewerOutput *viewer,
|
||||
bool start_still_image);
|
||||
|
||||
bool IsMagicEnabled() const { return magic_; }
|
||||
bool IsMagicEnabled() const
|
||||
{
|
||||
return magic_;
|
||||
}
|
||||
|
||||
public slots:
|
||||
/**
|
||||
/**
|
||||
* @brief Starts an open file dialog to load a project from file
|
||||
*/
|
||||
void OpenProject();
|
||||
void OpenProject();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Saves the current project
|
||||
*/
|
||||
bool SaveProject();
|
||||
bool SaveProject();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Performs a "save as" on the current project
|
||||
*/
|
||||
bool SaveProjectAs();
|
||||
bool SaveProjectAs();
|
||||
|
||||
void RevertProject();
|
||||
void RevertProject();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set the current application-wide tool
|
||||
*
|
||||
* @param tool
|
||||
*/
|
||||
void SetTool(const Tool::Item& tool);
|
||||
void SetTool(const Tool::Item &tool);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set the current snapping setting
|
||||
*/
|
||||
void SetSnapping(const bool& b);
|
||||
void SetSnapping(const bool &b);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Show an About dialog
|
||||
*/
|
||||
void DialogAboutShow();
|
||||
void DialogAboutShow();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open the import footage dialog and import the files selected (runs ImportFiles())
|
||||
*/
|
||||
void DialogImportShow();
|
||||
void DialogImportShow();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Show Preferences dialog
|
||||
*/
|
||||
void DialogPreferencesShow();
|
||||
void DialogPreferencesShow();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Show Project Properties dialog
|
||||
*/
|
||||
void DialogProjectPropertiesShow();
|
||||
void DialogProjectPropertiesShow();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Show Export dialog
|
||||
*/
|
||||
void DialogExportShow();
|
||||
void DialogExportShow();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Show OTIO import dialog
|
||||
*/
|
||||
#ifdef USE_OTIO
|
||||
bool DialogImportOTIOShow(const QList<Sequence*>& sequences);
|
||||
bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
|
||||
#endif
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Create a new folder in the currently active project
|
||||
*/
|
||||
void CreateNewFolder();
|
||||
void CreateNewFolder();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Create a new sequence in the currently active project
|
||||
*/
|
||||
void CreateNewSequence();
|
||||
void CreateNewSequence();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set the currently selected object that the add tool should make
|
||||
*/
|
||||
void SetSelectedAddableObject(const Tool::AddableObject& obj);
|
||||
void SetSelectedAddableObject(const Tool::AddableObject &obj);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set the currently selected object that the add tool should make
|
||||
*/
|
||||
void SetSelectedTransitionObject(const QString& obj);
|
||||
void SetSelectedTransitionObject(const QString &obj);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Clears the list of recently opened/saved projects
|
||||
*/
|
||||
void ClearOpenRecentList();
|
||||
void ClearOpenRecentList();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Creates a new empty project and opens it
|
||||
*/
|
||||
void CreateNewProject();
|
||||
void CreateNewProject();
|
||||
|
||||
void CheckForAutoRecoveries();
|
||||
void CheckForAutoRecoveries();
|
||||
|
||||
void BrowseAutoRecoveries();
|
||||
void BrowseAutoRecoveries();
|
||||
|
||||
void RequestPixelSamplingInViewers(bool e);
|
||||
void RequestPixelSamplingInViewers(bool e);
|
||||
|
||||
void WarnCacheFull();
|
||||
void WarnCacheFull();
|
||||
|
||||
void SetMagic(bool e)
|
||||
{
|
||||
magic_ = e;
|
||||
}
|
||||
void SetMagic(bool e)
|
||||
{
|
||||
magic_ = e;
|
||||
}
|
||||
|
||||
signals:
|
||||
/**
|
||||
/**
|
||||
* @brief Signal emitted when the tool is changed from somewhere
|
||||
*/
|
||||
void ToolChanged(const Tool::Item& tool);
|
||||
void ToolChanged(const Tool::Item &tool);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Signal emitted when addable object changes through SetSelectedAddableObject
|
||||
*/
|
||||
void AddableObjectChanged(Tool::AddableObject o);
|
||||
void AddableObjectChanged(Tool::AddableObject o);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Signal emitted when the snapping setting is changed
|
||||
*/
|
||||
void SnappingChanged(const bool& b);
|
||||
void SnappingChanged(const bool &b);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Signal emitted when the default timecode display mode changed
|
||||
*/
|
||||
void TimecodeDisplayChanged(Timecode::Display d);
|
||||
void TimecodeDisplayChanged(Timecode::Display d);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Signal emitted when a change is made to the open recent list
|
||||
*/
|
||||
void OpenRecentListChanged();
|
||||
void OpenRecentListChanged();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Enable mouse color sampling functionality on all viewers
|
||||
*
|
||||
* This can be slow, so we only turn it on when we need it.
|
||||
*/
|
||||
void ColorPickerEnabled(bool e);
|
||||
void ColorPickerEnabled(bool e);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief A viewer with color picked enabled has emitted a color
|
||||
*/
|
||||
void ColorPickerColorEmitted(const Color &reference, const Color &display);
|
||||
void ColorPickerColorEmitted(const Color &reference, const Color &display);
|
||||
|
||||
private:
|
||||
/**
|
||||
/**
|
||||
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
||||
*/
|
||||
static QString GetProjectFilter(bool include_any_filter);
|
||||
static QString GetProjectFilter(bool include_any_filter);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the filename where the recently opened/saved projects should be stored
|
||||
*/
|
||||
static QString GetRecentProjectsFilePath();
|
||||
static QString GetRecentProjectsFilePath();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Called only on startup to set the locale
|
||||
*/
|
||||
void SetStartupLocale();
|
||||
void SetStartupLocale();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
|
||||
*/
|
||||
void PushRecentlyOpenedProject(const QString &s);
|
||||
void PushRecentlyOpenedProject(const QString &s);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Declare custom types/classes for Qt's signal/slot system
|
||||
*
|
||||
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
|
||||
* them all in a function here.
|
||||
*/
|
||||
void DeclareTypesForQt();
|
||||
void DeclareTypesForQt();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Start GUI portion of Olive
|
||||
*
|
||||
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
|
||||
* create an application instance that is completely valid minus the UI (e.g. for CLI modes).
|
||||
*/
|
||||
void StartGUI(bool full_screen);
|
||||
void StartGUI(bool full_screen);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal function for saving a project to a file
|
||||
*/
|
||||
void SaveProjectInternal(const QString &override_filename = QString());
|
||||
void SaveProjectInternal(const QString &override_filename = QString());
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves the currently most active sequence for exporting
|
||||
*/
|
||||
ViewerOutput *GetSequenceToExport();
|
||||
ViewerOutput *GetSequenceToExport();
|
||||
|
||||
static QString GetAutoRecoveryIndexFilename();
|
||||
static QString GetAutoRecoveryIndexFilename();
|
||||
|
||||
void SaveUnrecoveredList();
|
||||
void SaveUnrecoveredList();
|
||||
|
||||
bool RevertProjectInternal(bool by_opening_existing);
|
||||
bool RevertProjectInternal(bool by_opening_existing);
|
||||
|
||||
void SaveRecentProjectsList();
|
||||
void SaveRecentProjectsList();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Adds a project to the "open projects" list
|
||||
*/
|
||||
void AddOpenProject(olive::Project* p, bool add_to_recents = false);
|
||||
void AddOpenProject(olive::Project *p, bool add_to_recents = false);
|
||||
|
||||
bool AddOpenProjectFromTask(Task* task, bool add_to_recents);
|
||||
bool AddOpenProjectFromTask(Task *task, bool add_to_recents);
|
||||
|
||||
void SetActiveProject(Project *p);
|
||||
void SetActiveProject(Project *p);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal main window object
|
||||
*/
|
||||
MainWindow* main_window_;
|
||||
MainWindow *main_window_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief List of currently open projects
|
||||
*/
|
||||
Project *open_project_;
|
||||
Project *open_project_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Currently active tool
|
||||
*/
|
||||
Tool::Item tool_;
|
||||
Tool::Item tool_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Currently active addable object
|
||||
*/
|
||||
Tool::AddableObject addable_object_;
|
||||
Tool::AddableObject addable_object_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Currently selected transition
|
||||
*/
|
||||
QString selected_transition_;
|
||||
QString selected_transition_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Current snapping setting
|
||||
*/
|
||||
bool snapping_;
|
||||
bool snapping_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal timer for saving autorecovery files
|
||||
*/
|
||||
QTimer autorecovery_timer_;
|
||||
QTimer autorecovery_timer_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Application-wide undo stack instance
|
||||
*/
|
||||
UndoStack undo_stack_;
|
||||
UndoStack undo_stack_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief List of most recently opened/saved projects
|
||||
*/
|
||||
QStringList recent_projects_;
|
||||
QStringList recent_projects_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Parameters set up in main() determining how the program should run
|
||||
*/
|
||||
CoreParams core_params_;
|
||||
CoreParams core_params_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Static singleton core instance
|
||||
*/
|
||||
static Core* instance_;
|
||||
static Core *instance_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal translator
|
||||
*/
|
||||
QTranslator* translator_;
|
||||
QTranslator *translator_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief List of projects that are unsaved but have autorecovery projects
|
||||
*/
|
||||
QVector<QUuid> autorecovered_projects_;
|
||||
QVector<QUuid> autorecovered_projects_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Do something debug related
|
||||
*/
|
||||
bool magic_;
|
||||
bool magic_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief How many widgets currently need pixel sampling access
|
||||
*/
|
||||
int pixel_sampling_users_;
|
||||
int pixel_sampling_users_;
|
||||
|
||||
bool shown_cache_full_warning_;
|
||||
bool shown_cache_full_warning_;
|
||||
|
||||
private slots:
|
||||
void SaveAutorecovery();
|
||||
void SaveAutorecovery();
|
||||
|
||||
void ProjectSaveSucceeded(Task *task);
|
||||
void ProjectSaveSucceeded(Task *task);
|
||||
|
||||
bool AddOpenProjectFromTaskAndAddToRecents(Task* task)
|
||||
{
|
||||
return AddOpenProjectFromTask(task, true);
|
||||
}
|
||||
bool AddOpenProjectFromTaskAndAddToRecents(Task *task)
|
||||
{
|
||||
return AddOpenProjectFromTask(task, true);
|
||||
}
|
||||
|
||||
void ImportTaskComplete(Task *task);
|
||||
void ImportTaskComplete(Task *task);
|
||||
|
||||
bool ConfirmImageSequence(const QString &filename);
|
||||
bool ConfirmImageSequence(const QString &filename);
|
||||
|
||||
void ProjectWasModified(bool e);
|
||||
void ProjectWasModified(bool e);
|
||||
|
||||
bool StartHeadlessExport();
|
||||
bool StartHeadlessExport();
|
||||
|
||||
void OpenStartupProject();
|
||||
void OpenStartupProject();
|
||||
|
||||
void AddRecoveryProjectFromTask(Task* task);
|
||||
void AddRecoveryProjectFromTask(Task *task);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal project open
|
||||
*/
|
||||
void OpenProjectInternal(const QString& filename, bool recovery_project = false);
|
||||
|
||||
void ImportSingleFile(const QString &f);
|
||||
void OpenProjectInternal(const QString &filename,
|
||||
bool recovery_project = false);
|
||||
|
||||
void ImportSingleFile(const QString &f);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+259
-224
@@ -40,335 +40,370 @@
|
||||
#include "common/filefunctions.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
|
||||
namespace olive
|
||||
{
|
||||
setWindowTitle(tr("Olive"));
|
||||
setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||
|
||||
report_filename_ = report_path;
|
||||
waiting_for_upload_ = false;
|
||||
CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
|
||||
{
|
||||
setWindowTitle(tr("Olive"));
|
||||
setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
report_filename_ = report_path;
|
||||
waiting_for_upload_ = false;
|
||||
|
||||
layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by "
|
||||
"sending an error report.")));
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
QSplitter* splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
layout->addWidget(new QLabel(
|
||||
tr("We're sorry, Olive has crashed. Please help us fix it by "
|
||||
"sending an error report.")));
|
||||
|
||||
summary_edit_ = new QTextEdit();
|
||||
summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as "
|
||||
"possible. If you can, provide steps to reproduce this crash."));
|
||||
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
|
||||
splitter->addWidget(summary_edit_);
|
||||
summary_edit_ = new QTextEdit();
|
||||
summary_edit_->setPlaceholderText(
|
||||
tr("Describe what you were doing in as much detail as "
|
||||
"possible. If you can, provide steps to reproduce this crash."));
|
||||
|
||||
QWidget* crash_widget = new QWidget();
|
||||
QVBoxLayout* crash_widget_layout = new QVBoxLayout(crash_widget);
|
||||
crash_widget_layout->setMargin(0);
|
||||
splitter->addWidget(summary_edit_);
|
||||
|
||||
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
|
||||
QWidget *crash_widget = new QWidget();
|
||||
QVBoxLayout *crash_widget_layout = new QVBoxLayout(crash_widget);
|
||||
crash_widget_layout->setMargin(0);
|
||||
|
||||
crash_report_ = new QTextEdit();
|
||||
crash_report_->setReadOnly(true);
|
||||
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
crash_widget_layout->addWidget(crash_report_);
|
||||
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
|
||||
|
||||
splitter->addWidget(crash_widget);
|
||||
crash_report_ = new QTextEdit();
|
||||
crash_report_->setReadOnly(true);
|
||||
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
crash_widget_layout->addWidget(crash_report_);
|
||||
|
||||
QHBoxLayout* btn_layout = new QHBoxLayout();
|
||||
btn_layout->setMargin(0);
|
||||
btn_layout->addStretch();
|
||||
splitter->addWidget(crash_widget);
|
||||
|
||||
send_report_btn_ = new QPushButton(tr("Send Error Report"));
|
||||
connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport);
|
||||
btn_layout->addWidget(send_report_btn_);
|
||||
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||
btn_layout->setMargin(0);
|
||||
btn_layout->addStretch();
|
||||
|
||||
dont_send_btn_ = new QPushButton(tr("Don't Send"));
|
||||
connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject);
|
||||
btn_layout->addWidget(dont_send_btn_);
|
||||
send_report_btn_ = new QPushButton(tr("Send Error Report"));
|
||||
connect(send_report_btn_, &QPushButton::clicked, this,
|
||||
&CrashHandlerDialog::SendErrorReport);
|
||||
btn_layout->addWidget(send_report_btn_);
|
||||
|
||||
layout->addLayout(btn_layout);
|
||||
dont_send_btn_ = new QPushButton(tr("Don't Send"));
|
||||
connect(dont_send_btn_, &QPushButton::clicked, this,
|
||||
&CrashHandlerDialog::reject);
|
||||
btn_layout->addWidget(dont_send_btn_);
|
||||
|
||||
crash_report_->setEnabled(false);
|
||||
send_report_btn_->setEnabled(false);
|
||||
layout->addLayout(btn_layout);
|
||||
|
||||
crash_report_->setText(tr("Waiting for crash report to be generated..."));
|
||||
crash_report_->setEnabled(false);
|
||||
send_report_btn_->setEnabled(false);
|
||||
|
||||
GenerateReport();
|
||||
crash_report_->setText(tr("Waiting for crash report to be generated..."));
|
||||
|
||||
GenerateReport();
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::SetGUIObjectsEnabled(bool e)
|
||||
{
|
||||
summary_edit_->setEnabled(e);
|
||||
crash_report_->setEnabled(e);
|
||||
send_report_btn_->setEnabled(e);
|
||||
dont_send_btn_->setEnabled(e);
|
||||
summary_edit_->setEnabled(e);
|
||||
crash_report_->setEnabled(e);
|
||||
send_report_btn_->setEnabled(e);
|
||||
dont_send_btn_->setEnabled(e);
|
||||
}
|
||||
|
||||
QString CrashHandlerDialog::GetSymbolPath()
|
||||
{
|
||||
QDir app_path(qApp->applicationDirPath());
|
||||
QString symbols_path;
|
||||
QDir app_path(qApp->applicationDirPath());
|
||||
QString symbols_path;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
symbols_path = app_path.filePath(QStringLiteral("symbols"));
|
||||
symbols_path = app_path.filePath(QStringLiteral("symbols"));
|
||||
#elif BUILDFLAG(IS_LINUX)
|
||||
app_path.cdUp();
|
||||
symbols_path = app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
|
||||
app_path.cdUp();
|
||||
symbols_path =
|
||||
app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
|
||||
#elif BUILDFLAG(IS_APPLE)
|
||||
app_path.cdUp();
|
||||
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
|
||||
app_path.cdUp();
|
||||
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
|
||||
#endif
|
||||
|
||||
return symbols_path;
|
||||
return symbols_path;
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::GenerateReport()
|
||||
{
|
||||
QProcess* p = new QProcess();
|
||||
QProcess *p = new QProcess();
|
||||
|
||||
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &CrashHandlerDialog::ReadProcessFinished);
|
||||
connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData);
|
||||
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &CrashHandlerDialog::ReadProcessFinished);
|
||||
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);
|
||||
p->start(stackwalk_bin, {report_filename_, GetSymbolPath()});
|
||||
crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
|
||||
QString stackwalk_bin =
|
||||
QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
|
||||
p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
|
||||
crash_report_->setText(
|
||||
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
|
||||
void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
|
||||
{
|
||||
waiting_for_upload_ = false;
|
||||
waiting_for_upload_ = false;
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
// Close dialog
|
||||
QDialog::accept();
|
||||
} else {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Upload Failed"));
|
||||
b.setText(tr("Failed to send error report (%1). Please try again later.").arg(QString::number(reply->error())));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
// Close dialog
|
||||
QDialog::accept();
|
||||
} else {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Upload Failed"));
|
||||
b.setText(
|
||||
tr("Failed to send error report (%1). Please try again later.")
|
||||
.arg(QString::number(reply->error())));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
|
||||
SetGUIObjectsEnabled(true);
|
||||
}
|
||||
SetGUIObjectsEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &se)
|
||||
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply,
|
||||
const QList<QSslError> &se)
|
||||
{
|
||||
QStringList errors;
|
||||
for (const QSslError &err : se) {
|
||||
errors.append(err.errorString());
|
||||
}
|
||||
QStringList errors;
|
||||
for (const QSslError &err : se) {
|
||||
errors.append(err.errorString());
|
||||
}
|
||||
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("SSL Error"));
|
||||
b.setText(tr("Encountered the following SSL errors:\n\n%1").arg(errors.join('\n')));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("SSL Error"));
|
||||
b.setText(tr("Encountered the following SSL errors:\n\n%1")
|
||||
.arg(errors.join('\n')));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::AttemptToFindReport()
|
||||
{
|
||||
// If we found it, use it, otherwise wait a second and try again
|
||||
if (report_filename_.isEmpty()) {
|
||||
// Couldn't find report, try again in one second
|
||||
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
|
||||
} else {
|
||||
GenerateReport();
|
||||
}
|
||||
// If we found it, use it, otherwise wait a second and try again
|
||||
if (report_filename_.isEmpty()) {
|
||||
// Couldn't find report, try again in one second
|
||||
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
|
||||
} else {
|
||||
GenerateReport();
|
||||
}
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::ReadProcessHasData()
|
||||
{
|
||||
report_data_.append(static_cast<QProcess*>(sender())->readAllStandardOutput());
|
||||
report_data_.append(
|
||||
static_cast<QProcess *>(sender())->readAllStandardOutput());
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::ReadProcessFinished()
|
||||
{
|
||||
SetGUIObjectsEnabled(true);
|
||||
crash_report_->setText(report_data_);
|
||||
delete sender();
|
||||
SetGUIObjectsEnabled(true);
|
||||
crash_report_->setText(report_data_);
|
||||
delete sender();
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::SendErrorReport()
|
||||
{
|
||||
if (summary_edit_->document()->isEmpty()) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Question);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setText(tr("You must write a description to submit this crash report."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
if (summary_edit_->document()->isEmpty()) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Question);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setText(
|
||||
tr("You must write a description to submit this crash report."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkAccessManager* manager = new QNetworkAccessManager();
|
||||
connect(manager, &QNetworkAccessManager::finished, this, &CrashHandlerDialog::ReplyFinished);
|
||||
connect(manager, &QNetworkAccessManager::sslErrors, this, &CrashHandlerDialog::HandleSslErrors);
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
connect(manager, &QNetworkAccessManager::finished, this,
|
||||
&CrashHandlerDialog::ReplyFinished);
|
||||
connect(manager, &QNetworkAccessManager::sslErrors, this,
|
||||
&CrashHandlerDialog::HandleSslErrors);
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
||||
request.setUrl(QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
|
||||
QNetworkRequest request;
|
||||
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
||||
request.setUrl(
|
||||
QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
|
||||
|
||||
// Create HTTP form
|
||||
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
// Create HTTP form
|
||||
QHttpMultiPart *multipart =
|
||||
new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
// Create description section
|
||||
QHttpPart desc_part;
|
||||
desc_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
|
||||
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"description\""));
|
||||
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
|
||||
multipart->append(desc_part);
|
||||
// Create description section
|
||||
QHttpPart desc_part;
|
||||
desc_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("text/plain; charset=UTF-8"));
|
||||
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"description\""));
|
||||
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
|
||||
multipart->append(desc_part);
|
||||
|
||||
// Create report section
|
||||
QHttpPart report_part;
|
||||
report_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
|
||||
report_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"report\""));
|
||||
report_part.setBody(report_data_);
|
||||
multipart->append(report_part);
|
||||
// Create report section
|
||||
QHttpPart report_part;
|
||||
report_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("text/plain; charset=UTF-8"));
|
||||
report_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"report\""));
|
||||
report_part.setBody(report_data_);
|
||||
multipart->append(report_part);
|
||||
|
||||
// Create commit section
|
||||
QHttpPart commit_part;
|
||||
commit_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
|
||||
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"commit\""));
|
||||
commit_part.setBody(kAppVersionLong.toUtf8());
|
||||
multipart->append(commit_part);
|
||||
// Create commit section
|
||||
QHttpPart commit_part;
|
||||
commit_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("text/plain; charset=UTF-8"));
|
||||
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"commit\""));
|
||||
commit_part.setBody(kAppVersionLong.toUtf8());
|
||||
multipart->append(commit_part);
|
||||
|
||||
// Create dump section
|
||||
QHttpPart dump_part;
|
||||
dump_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream"));
|
||||
dump_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
|
||||
.arg(QFileInfo(report_filename_).fileName()));
|
||||
QFile* dump_file = new QFile(report_filename_);
|
||||
dump_file->open(QFile::ReadOnly);
|
||||
dump_part.setBodyDevice(dump_file);
|
||||
dump_file->setParent(multipart); // Delete file with multipart
|
||||
multipart->append(dump_part);
|
||||
// Create dump section
|
||||
QHttpPart dump_part;
|
||||
dump_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("application/octet-stream"));
|
||||
dump_part.setHeader(
|
||||
QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
|
||||
.arg(QFileInfo(report_filename_).fileName()));
|
||||
QFile *dump_file = new QFile(report_filename_);
|
||||
dump_file->open(QFile::ReadOnly);
|
||||
dump_part.setBodyDevice(dump_file);
|
||||
dump_file->setParent(multipart); // Delete file with multipart
|
||||
multipart->append(dump_part);
|
||||
|
||||
// Find symbol file
|
||||
QDir symbol_dir(GetSymbolPath());
|
||||
// Find symbol file
|
||||
QDir symbol_dir(GetSymbolPath());
|
||||
|
||||
QString symbol_bin_name;
|
||||
QString symbol_bin_name;
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
symbol_bin_name = QStringLiteral("olive-editor.pdb");
|
||||
symbol_bin_name = QStringLiteral("olive-editor.pdb");
|
||||
#elif BUILDFLAG(IS_APPLE)
|
||||
symbol_bin_name = QStringLiteral("Olive");
|
||||
symbol_bin_name = QStringLiteral("Olive");
|
||||
#else
|
||||
symbol_bin_name = QStringLiteral("olive-editor");
|
||||
symbol_bin_name = QStringLiteral("olive-editor");
|
||||
#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) {
|
||||
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
|
||||
} else {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Failed to send report"));
|
||||
b.setText(tr("Failed to find symbols necessary to send report. "
|
||||
"This is a packaging issue. Please notify "
|
||||
"the maintainers of this package."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
if (folders_in_symbol_path.size() > 0) {
|
||||
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
|
||||
} else {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Failed to send report"));
|
||||
b.setText(tr("Failed to find symbols necessary to send report. "
|
||||
"This is a packaging issue. Please notify "
|
||||
"the maintainers of this package."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create sym section
|
||||
QString symbol_filename;
|
||||
// Create sym section
|
||||
QString symbol_filename;
|
||||
#if BUILDFLAG(IS_APPLE)
|
||||
symbol_filename = QStringLiteral("Olive.sym");
|
||||
symbol_filename = QStringLiteral("Olive.sym");
|
||||
#else
|
||||
symbol_filename = QStringLiteral("olive-editor.sym");
|
||||
symbol_filename = QStringLiteral("olive-editor.sym");
|
||||
#endif
|
||||
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
|
||||
QHttpPart sym_part;
|
||||
sym_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream"));
|
||||
sym_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
|
||||
.arg(symbol_filename));
|
||||
QFile sym_file(symbol_full_path);
|
||||
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
|
||||
QHttpPart sym_part;
|
||||
sym_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("application/octet-stream"));
|
||||
sym_part.setHeader(
|
||||
QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
|
||||
.arg(symbol_filename));
|
||||
QFile sym_file(symbol_full_path);
|
||||
|
||||
if (!sym_file.open(QFile::ReadOnly)) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Failed to send report"));
|
||||
b.setText(tr("Failed to open symbol file. You may not have "
|
||||
"permission to access it."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
if (!sym_file.open(QFile::ReadOnly)) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Failed to send report"));
|
||||
b.setText(tr("Failed to open symbol file. You may not have "
|
||||
"permission to access it."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
|
||||
sym_file.close();
|
||||
QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
|
||||
sym_file.close();
|
||||
|
||||
sym_part.setBody(symbol_data);
|
||||
multipart->append(sym_part);
|
||||
sym_part.setBody(symbol_data);
|
||||
multipart->append(sym_part);
|
||||
|
||||
manager->post(request, multipart);
|
||||
manager->post(request, multipart);
|
||||
|
||||
SetGUIObjectsEnabled(false);
|
||||
waiting_for_upload_ = true;
|
||||
SetGUIObjectsEnabled(false);
|
||||
waiting_for_upload_ = true;
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::closeEvent(QCloseEvent* e)
|
||||
void CrashHandlerDialog::closeEvent(QCloseEvent *e)
|
||||
{
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Warning);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Confirm Close"));
|
||||
b.setText(tr("Crash report is still uploading. Closing now may result in no "
|
||||
"report being sent. Are you sure you wish to close?"));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.addButton(QMessageBox::Cancel);
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Warning);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Confirm Close"));
|
||||
b.setText(
|
||||
tr("Crash report is still uploading. Closing now may result in no "
|
||||
"report being sent. Are you sure you wish to close?"));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.addButton(QMessageBox::Cancel);
|
||||
|
||||
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
|
||||
e->ignore();
|
||||
} else {
|
||||
e->accept();
|
||||
}
|
||||
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
|
||||
e->ignore();
|
||||
} else {
|
||||
e->accept();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QString report;
|
||||
QString report;
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
int num_args;
|
||||
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
|
||||
if (num_args < 2) {
|
||||
LocalFree(args);
|
||||
return 1;
|
||||
}
|
||||
int num_args;
|
||||
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
|
||||
if (num_args < 2) {
|
||||
LocalFree(args);
|
||||
return 1;
|
||||
}
|
||||
|
||||
report = QString::fromWCharArray(args[1]);
|
||||
LocalFree(args);
|
||||
report = QString::fromWCharArray(args[1]);
|
||||
LocalFree(args);
|
||||
#else
|
||||
if (argc < 2) {
|
||||
return 1;
|
||||
}
|
||||
if (argc < 2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
report = argv[1];
|
||||
report = argv[1];
|
||||
#endif
|
||||
|
||||
QApplication a(argc, argv);
|
||||
QApplication a(argc, argv);
|
||||
|
||||
olive::CrashHandlerDialog chd(report);
|
||||
chd.open();
|
||||
olive::CrashHandlerDialog chd(report);
|
||||
chd.open();
|
||||
|
||||
return a.exec();
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
@@ -30,51 +30,50 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CrashHandlerDialog : public QDialog
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class CrashHandlerDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CrashHandlerDialog(const QString& report_path);
|
||||
CrashHandlerDialog(const QString &report_path);
|
||||
|
||||
private:
|
||||
void SetGUIObjectsEnabled(bool e);
|
||||
void SetGUIObjectsEnabled(bool e);
|
||||
|
||||
void GenerateReport();
|
||||
void GenerateReport();
|
||||
|
||||
static QString GetSymbolPath();
|
||||
static QString GetSymbolPath();
|
||||
|
||||
QTextEdit* summary_edit_;
|
||||
QTextEdit *summary_edit_;
|
||||
|
||||
QTextEdit* crash_report_;
|
||||
QTextEdit *crash_report_;
|
||||
|
||||
QPushButton* send_report_btn_;
|
||||
QPushButton *send_report_btn_;
|
||||
|
||||
QPushButton* dont_send_btn_;
|
||||
QPushButton *dont_send_btn_;
|
||||
|
||||
QString report_filename_;
|
||||
QString report_filename_;
|
||||
|
||||
QByteArray report_data_;
|
||||
QByteArray report_data_;
|
||||
|
||||
bool waiting_for_upload_;
|
||||
bool waiting_for_upload_;
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent* e) override;
|
||||
virtual void closeEvent(QCloseEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void ReplyFinished(QNetworkReply *reply);
|
||||
void ReplyFinished(QNetworkReply *reply);
|
||||
|
||||
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||
|
||||
void AttemptToFindReport();
|
||||
void AttemptToFindReport();
|
||||
|
||||
void ReadProcessHasData();
|
||||
void ReadProcessHasData();
|
||||
|
||||
void ReadProcessFinished();
|
||||
|
||||
void SendErrorReport();
|
||||
void ReadProcessFinished();
|
||||
|
||||
void SendErrorReport();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+96
-83
@@ -30,114 +30,127 @@
|
||||
#include "patreon.h"
|
||||
#include "scrollinglabel.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
||||
QDialog(parent)
|
||||
namespace olive
|
||||
{
|
||||
if (welcome_dialog) {
|
||||
setWindowTitle(tr("Welcome to %1").arg(QApplication::applicationName()));
|
||||
} else {
|
||||
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
|
||||
}
|
||||
|
||||
QFontMetrics fm = fontMetrics();
|
||||
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
if (welcome_dialog) {
|
||||
setWindowTitle(
|
||||
tr("Welcome to %1").arg(QApplication::applicationName()));
|
||||
} else {
|
||||
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
|
||||
}
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
|
||||
QFontMetrics fm = fontMetrics();
|
||||
|
||||
QHBoxLayout *horiz_layout = new QHBoxLayout();
|
||||
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
|
||||
horiz_layout->setSpacing(fm.height()*2);
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||
fm.height());
|
||||
|
||||
QLabel* icon = new QLabel(QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
|
||||
icon->setAlignment(Qt::AlignCenter);
|
||||
horiz_layout->addWidget(icon);
|
||||
QHBoxLayout *horiz_layout = new QHBoxLayout();
|
||||
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||
fm.height());
|
||||
horiz_layout->setSpacing(fm.height() * 2);
|
||||
|
||||
// Construct About text
|
||||
QLabel* label =
|
||||
new QLabel(QStringLiteral("<html><head/><body>"
|
||||
"<p><b>%1</b> %2</p>" // AppName (version identifier)
|
||||
"<p><a href=\"https://www.olivevideoeditor.org/\">"
|
||||
"https://www.olivevideoeditor.org/"
|
||||
"</a></p>"
|
||||
"<p>%3</p>" // First statement
|
||||
"</body></html>").arg(QApplication::applicationName(),
|
||||
QApplication::applicationVersion(),
|
||||
tr("Olive is a free open source non-linear video editor. "
|
||||
"This software is licensed under the GNU GPL Version 3.")));
|
||||
QLabel *icon = new QLabel(
|
||||
QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
|
||||
icon->setAlignment(Qt::AlignCenter);
|
||||
horiz_layout->addWidget(icon);
|
||||
|
||||
// Set text formatting
|
||||
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
label->setWordWrap(true);
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
||||
label->setCursor(Qt::IBeamCursor);
|
||||
horiz_layout->addWidget(label);
|
||||
// Construct About text
|
||||
QLabel *label = new QLabel(
|
||||
QStringLiteral("<html><head/><body>"
|
||||
"<p><b>%1</b> %2</p>" // AppName (version identifier)
|
||||
"<p><a href=\"https://www.olivevideoeditor.org/\">"
|
||||
"https://www.olivevideoeditor.org/"
|
||||
"</a></p>"
|
||||
"<p>%3</p>" // First statement
|
||||
"</body></html>")
|
||||
.arg(QApplication::applicationName(),
|
||||
QApplication::applicationVersion(),
|
||||
tr("Olive is a free open source non-linear video editor. "
|
||||
"This software is licensed under the GNU GPL Version 3.")));
|
||||
|
||||
layout->addLayout(horiz_layout);
|
||||
// Set text formatting
|
||||
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
label->setWordWrap(true);
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse |
|
||||
Qt::LinksAccessibleByMouse);
|
||||
label->setCursor(Qt::IBeamCursor);
|
||||
horiz_layout->addWidget(label);
|
||||
|
||||
// Patrons where possible
|
||||
layout->addWidget(new QLabel());
|
||||
layout->addLayout(horiz_layout);
|
||||
|
||||
QString opening_statement;
|
||||
// Patrons where possible
|
||||
layout->addWidget(new QLabel());
|
||||
|
||||
if (welcome_dialog || patrons.isEmpty()) {
|
||||
opening_statement = tr("<b>Olive relies on support from the community to continue its development.</b>");
|
||||
} else {
|
||||
opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people.");
|
||||
}
|
||||
QString opening_statement;
|
||||
|
||||
QLabel* support_lbl = new QLabel(tr("<html>%1 "
|
||||
"If you like this project, please consider making a "
|
||||
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
|
||||
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
|
||||
"support its development.</html>").arg(opening_statement));
|
||||
support_lbl->setWordWrap(true);
|
||||
support_lbl->setAlignment(Qt::AlignCenter);
|
||||
support_lbl->setOpenExternalLinks(true);
|
||||
layout->addWidget(support_lbl);
|
||||
if (welcome_dialog || patrons.isEmpty()) {
|
||||
opening_statement = tr(
|
||||
"<b>Olive relies on support from the community to continue its development.</b>");
|
||||
} else {
|
||||
opening_statement = tr(
|
||||
"Olive wouldn't be possible without the support of gracious donations from the following people.");
|
||||
}
|
||||
|
||||
if (!patrons.isEmpty()) {
|
||||
ScrollingLabel* scroll = new ScrollingLabel(patrons);
|
||||
scroll->StartAnimating();
|
||||
layout->addWidget(scroll);
|
||||
}
|
||||
QLabel *support_lbl = new QLabel(
|
||||
tr("<html>%1 "
|
||||
"If you like this project, please consider making a "
|
||||
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
|
||||
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
|
||||
"support its development.</html>")
|
||||
.arg(opening_statement));
|
||||
support_lbl->setWordWrap(true);
|
||||
support_lbl->setAlignment(Qt::AlignCenter);
|
||||
support_lbl->setOpenExternalLinks(true);
|
||||
layout->addWidget(support_lbl);
|
||||
|
||||
layout->addWidget(new QLabel());
|
||||
if (!patrons.isEmpty()) {
|
||||
ScrollingLabel *scroll = new ScrollingLabel(patrons);
|
||||
scroll->StartAnimating();
|
||||
layout->addWidget(scroll);
|
||||
}
|
||||
|
||||
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||
btn_layout->setContentsMargins(0, 0, 0, 0);
|
||||
btn_layout->setSpacing(0);
|
||||
layout->addWidget(new QLabel());
|
||||
|
||||
if (welcome_dialog) {
|
||||
dont_show_again_checkbox_ = new QCheckBox(tr("Don't show this message again"));
|
||||
btn_layout->addWidget(dont_show_again_checkbox_);
|
||||
} else {
|
||||
dont_show_again_checkbox_ = nullptr;
|
||||
}
|
||||
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||
btn_layout->setContentsMargins(0, 0, 0, 0);
|
||||
btn_layout->setSpacing(0);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
||||
if (!welcome_dialog) {
|
||||
buttons->setCenterButtons(true);
|
||||
}
|
||||
btn_layout->addWidget(buttons);
|
||||
if (welcome_dialog) {
|
||||
dont_show_again_checkbox_ =
|
||||
new QCheckBox(tr("Don't show this message again"));
|
||||
btn_layout->addWidget(dont_show_again_checkbox_);
|
||||
} else {
|
||||
dont_show_again_checkbox_ = nullptr;
|
||||
}
|
||||
|
||||
layout->addLayout(btn_layout);
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
||||
if (!welcome_dialog) {
|
||||
buttons->setCenterButtons(true);
|
||||
}
|
||||
btn_layout->addWidget(buttons);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
layout->addLayout(btn_layout);
|
||||
|
||||
setFixedSize(sizeHint());
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
|
||||
setFixedSize(sizeHint());
|
||||
}
|
||||
|
||||
void AboutDialog::accept()
|
||||
{
|
||||
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
|
||||
OLIVE_CONFIG("ShowWelcomeDialog") = false;
|
||||
}
|
||||
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
|
||||
OLIVE_CONFIG("ShowWelcomeDialog") = false;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The AboutDialog class
|
||||
@@ -34,11 +35,10 @@ namespace olive {
|
||||
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
|
||||
* anywhere
|
||||
*/
|
||||
class AboutDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
class AboutDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief AboutDialog Constructor
|
||||
*
|
||||
* Creates About dialog.
|
||||
@@ -47,14 +47,13 @@ public:
|
||||
*
|
||||
* QWidget parent object. Usually this will be MainWindow.
|
||||
*/
|
||||
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
|
||||
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
QCheckBox *dont_show_again_checkbox_;
|
||||
|
||||
QCheckBox *dont_show_again_checkbox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -24,98 +24,101 @@
|
||||
|
||||
#include "common/qtutils.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const int ScrollingLabel::kMinLineHeight = 10;
|
||||
|
||||
ScrollingLabel::ScrollingLabel(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
animate_(0)
|
||||
ScrollingLabel::ScrollingLabel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, animate_(0)
|
||||
{
|
||||
timer_.setInterval(50);
|
||||
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
|
||||
timer_.setInterval(50);
|
||||
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
|
||||
}
|
||||
|
||||
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent) :
|
||||
ScrollingLabel(parent)
|
||||
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent)
|
||||
: ScrollingLabel(parent)
|
||||
{
|
||||
SetText(text);
|
||||
SetText(text);
|
||||
}
|
||||
|
||||
void ScrollingLabel::SetText(const QStringList &text)
|
||||
{
|
||||
text_ = text;
|
||||
text_ = text;
|
||||
|
||||
QFontMetrics fm = fontMetrics();
|
||||
text_height_ = fm.height();
|
||||
QFontMetrics fm = fontMetrics();
|
||||
text_height_ = fm.height();
|
||||
|
||||
int width = 0;
|
||||
foreach (const QString& s, text_) {
|
||||
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
|
||||
}
|
||||
int width = 0;
|
||||
foreach (const QString &s, text_) {
|
||||
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
|
||||
}
|
||||
|
||||
setMinimumSize(width, text_height_ * kMinLineHeight);
|
||||
setMinimumSize(width, text_height_ * kMinLineHeight);
|
||||
}
|
||||
|
||||
void ScrollingLabel::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
|
||||
map.fill(Qt::transparent);
|
||||
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
|
||||
map.fill(Qt::transparent);
|
||||
|
||||
{
|
||||
QPainter p(&map);
|
||||
p.setPen(palette().text().color());
|
||||
{
|
||||
QPainter p(&map);
|
||||
p.setPen(palette().text().color());
|
||||
|
||||
QFontMetrics fm = p.fontMetrics();
|
||||
int half_width = width();
|
||||
QFontMetrics fm = p.fontMetrics();
|
||||
int half_width = width();
|
||||
|
||||
for (int i=0; i<text_.size(); i++) {
|
||||
int text_y = fm.ascent() + height() - animate_ + (fm.height()*i);
|
||||
int text_bottom = text_y + fm.descent();
|
||||
int text_top = text_y - fm.ascent();
|
||||
for (int i = 0; i < text_.size(); i++) {
|
||||
int text_y = fm.ascent() + height() - animate_ + (fm.height() * i);
|
||||
int text_bottom = text_y + fm.descent();
|
||||
int text_top = text_y - fm.ascent();
|
||||
|
||||
if (text_bottom < 0 || text_top >= height()) {
|
||||
continue;
|
||||
}
|
||||
if (text_bottom < 0 || text_top >= height()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString& s = text_.at(i);
|
||||
const QString &s = text_.at(i);
|
||||
|
||||
int width = QtUtils::QFontMetricsWidth(fm, s);
|
||||
p.drawText(half_width/2 - width/2, text_y, s);
|
||||
}
|
||||
int width = QtUtils::QFontMetricsWidth(fm, s);
|
||||
p.drawText(half_width / 2 - width / 2, text_y, s);
|
||||
}
|
||||
|
||||
for (int y=0; y<text_height_; y++) {
|
||||
double mul = double(y)/double(text_height_);
|
||||
for (int y = 0; y < text_height_; y++) {
|
||||
double mul = double(y) / double(text_height_);
|
||||
|
||||
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
|
||||
SetOpacityOfScanLine(map.scanLine(map.height()-1-y), map.width(), 4, mul);
|
||||
}
|
||||
}
|
||||
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
|
||||
SetOpacityOfScanLine(map.scanLine(map.height() - 1 - y),
|
||||
map.width(), 4, mul);
|
||||
}
|
||||
}
|
||||
|
||||
QPainter wp(this);
|
||||
wp.drawImage(0, 0, map);
|
||||
QPainter wp(this);
|
||||
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++) {
|
||||
uchar* pixel = &scan_line[x*4];
|
||||
for (int x = 0; x < width; x++) {
|
||||
uchar *pixel = &scan_line[x * 4];
|
||||
|
||||
for (int c=0; c<channels; c++) {
|
||||
pixel[c] *= mul;
|
||||
}
|
||||
}
|
||||
for (int c = 0; c < channels; c++) {
|
||||
pixel[c] *= mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollingLabel::AnimationUpdate()
|
||||
{
|
||||
animate_++;
|
||||
animate_++;
|
||||
|
||||
if (animate_ >= height() + text_.size() * text_height_) {
|
||||
animate_ = 0;
|
||||
}
|
||||
if (animate_ >= height() + text_.size() * text_height_) {
|
||||
animate_ = 0;
|
||||
}
|
||||
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,46 +24,46 @@
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ScrollingLabel : public QWidget
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class ScrollingLabel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScrollingLabel(QWidget* parent = nullptr);
|
||||
ScrollingLabel(const QStringList& text, QWidget* parent = nullptr);
|
||||
ScrollingLabel(QWidget *parent = nullptr);
|
||||
ScrollingLabel(const QStringList &text, QWidget *parent = nullptr);
|
||||
|
||||
void SetText(const QStringList& text);
|
||||
void SetText(const QStringList &text);
|
||||
|
||||
void StartAnimating()
|
||||
{
|
||||
timer_.start();
|
||||
}
|
||||
void StartAnimating()
|
||||
{
|
||||
timer_.start();
|
||||
}
|
||||
|
||||
void StopAnimating()
|
||||
{
|
||||
timer_.stop();
|
||||
}
|
||||
void StopAnimating()
|
||||
{
|
||||
timer_.stop();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* e) override;
|
||||
virtual void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
static void SetOpacityOfScanLine(uchar* scan_line, int width, int channels, double mul);
|
||||
static void SetOpacityOfScanLine(uchar *scan_line, int width, int channels,
|
||||
double mul);
|
||||
|
||||
static const int kMinLineHeight;
|
||||
static const int kMinLineHeight;
|
||||
|
||||
QStringList text_;
|
||||
QStringList text_;
|
||||
|
||||
int text_height_;
|
||||
int text_height_;
|
||||
|
||||
QTimer timer_;
|
||||
QTimer timer_;
|
||||
|
||||
int animate_;
|
||||
int animate_;
|
||||
|
||||
private slots:
|
||||
void AnimationUpdate();
|
||||
|
||||
void AnimationUpdate();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,247 +25,251 @@
|
||||
#include <QMenuBar>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace olive {
|
||||
|
||||
ActionSearch::ActionSearch(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
menu_bar_(nullptr)
|
||||
namespace olive
|
||||
{
|
||||
// ActionSearch requires a parent widget
|
||||
Q_ASSERT(parent != nullptr);
|
||||
|
||||
// Set styling (object name is required for CSS specific to this object)
|
||||
setObjectName("ASDiag");
|
||||
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
|
||||
ActionSearch::ActionSearch(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, menu_bar_(nullptr)
|
||||
{
|
||||
// ActionSearch requires a parent widget
|
||||
Q_ASSERT(parent != nullptr);
|
||||
|
||||
// Size proportionally to the parent (usually MainWindow).
|
||||
resize(parent->width()/3, parent->height()/3);
|
||||
// Set styling (object name is required for CSS specific to this object)
|
||||
setObjectName("ASDiag");
|
||||
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
|
||||
|
||||
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
|
||||
setWindowFlags(Qt::Popup);
|
||||
// Size proportionally to the parent (usually MainWindow).
|
||||
resize(parent->width() / 3, parent->height() / 3);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
|
||||
setWindowFlags(Qt::Popup);
|
||||
|
||||
// Construct the main entry text field.
|
||||
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
// Set the main entry field font size to 1.2x its standard font size.
|
||||
QFont entry_field_font = entry_field->font();
|
||||
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
|
||||
entry_field->setFont(entry_field_font);
|
||||
// Construct the main entry text field.
|
||||
ActionSearchEntry *entry_field = new ActionSearchEntry(this);
|
||||
|
||||
// Set placeholder text for the main entry field
|
||||
entry_field->setPlaceholderText(tr("Search for action..."));
|
||||
// Set the main entry field font size to 1.2x its standard font size.
|
||||
QFont entry_field_font = entry_field->font();
|
||||
entry_field_font.setPointSize(qRound(entry_field_font.pointSize() * 1.2));
|
||||
entry_field->setFont(entry_field_font);
|
||||
|
||||
// Connect signals/slots
|
||||
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
|
||||
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
|
||||
// Set placeholder text for the main entry field
|
||||
entry_field->setPlaceholderText(tr("Search for action..."));
|
||||
|
||||
// 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.
|
||||
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up()));
|
||||
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
|
||||
layout->addWidget(entry_field);
|
||||
// Connect signals/slots
|
||||
connect(entry_field, SIGNAL(textChanged(const QString &)), this,
|
||||
SLOT(search_update(const QString &)));
|
||||
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
|
||||
|
||||
// Construct list of actions
|
||||
list_widget = new ActionSearchList(this);
|
||||
// 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.
|
||||
connect(entry_field, SIGNAL(moveSelectionUp()), this,
|
||||
SLOT(move_selection_up()));
|
||||
connect(entry_field, SIGNAL(moveSelectionDown()), this,
|
||||
SLOT(move_selection_down()));
|
||||
layout->addWidget(entry_field);
|
||||
|
||||
// Set list's font to 1.2x its standard font size
|
||||
QFont list_widget_font = list_widget->font();
|
||||
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
|
||||
list_widget->setFont(list_widget_font);
|
||||
// Construct list of actions
|
||||
list_widget = new ActionSearchList(this);
|
||||
|
||||
layout->addWidget(list_widget);
|
||||
// Set list's font to 1.2x its standard font size
|
||||
QFont list_widget_font = list_widget->font();
|
||||
list_widget_font.setPointSize(qRound(list_widget_font.pointSize() * 1.2));
|
||||
list_widget->setFont(list_widget_font);
|
||||
|
||||
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
|
||||
layout->addWidget(list_widget);
|
||||
|
||||
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
|
||||
// shortcut for example).
|
||||
entry_field->setFocus();
|
||||
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
|
||||
|
||||
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
|
||||
// shortcut for example).
|
||||
entry_field->setFocus();
|
||||
}
|
||||
|
||||
void ActionSearch::SetMenuBar(QMenuBar *menu_bar)
|
||||
{
|
||||
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
|
||||
if (menu_bar_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
// Do nothing if there's no menu bar to work with
|
||||
if (menu_bar_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
|
||||
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
|
||||
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
|
||||
// submenus).
|
||||
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
|
||||
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
|
||||
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
|
||||
// submenus).
|
||||
|
||||
if (parent == nullptr) {
|
||||
if (parent == nullptr) {
|
||||
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
|
||||
// (and their submenus).
|
||||
|
||||
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
|
||||
// (and their submenus).
|
||||
// We'll clear all the current items in the list since if we're here, we're just starting.
|
||||
list_widget->clear();
|
||||
|
||||
// We'll clear all the current items in the list since if we're here, we're just starting.
|
||||
list_widget->clear();
|
||||
QList<QAction *> menus = menu_bar_->actions();
|
||||
|
||||
QList<QAction*> menus = menu_bar_->actions();
|
||||
// Loop through all menus from the menubar and run this function on each one.
|
||||
for (int i = 0; i < menus.size(); i++) {
|
||||
QMenu *menu = menus.at(i)->menu();
|
||||
|
||||
// Loop through all menus from the menubar and run this function on each one.
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
search_update(s, p, menu);
|
||||
}
|
||||
|
||||
search_update(s, p, menu);
|
||||
}
|
||||
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
|
||||
// keyboard-exclusive functionality.
|
||||
if (list_widget->count() > 0) {
|
||||
list_widget->item(0)->setSelected(true);
|
||||
}
|
||||
|
||||
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
|
||||
// keyboard-exclusive functionality.
|
||||
if (list_widget->count() > 0) {
|
||||
list_widget->item(0)->setSelected(true);
|
||||
}
|
||||
} else {
|
||||
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
|
||||
|
||||
} else {
|
||||
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
|
||||
// adding the current menu's text to the existing hierarchy (passed in `p`).
|
||||
QString menu_text;
|
||||
if (!p.isEmpty())
|
||||
menu_text += p + " > ";
|
||||
menu_text += parent->title().replace(
|
||||
"&", ""); // Strip out any &s used in menu action names
|
||||
|
||||
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
|
||||
// Loop over the menu's actions
|
||||
QList<QAction *> actions = parent->actions();
|
||||
for (int i = 0; i < actions.size(); i++) {
|
||||
QAction *a = actions.at(i);
|
||||
|
||||
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
|
||||
// adding the current menu's text to the existing hierarchy (passed in `p`).
|
||||
QString menu_text;
|
||||
if (!p.isEmpty()) menu_text += p + " > ";
|
||||
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names
|
||||
// Ignore separator actions
|
||||
if (!a->isSeparator()) {
|
||||
if (a->menu() != nullptr) {
|
||||
// If the action is a menu, run this function recursively on it
|
||||
search_update(s, menu_text, a->menu());
|
||||
|
||||
// Loop over the menu's actions
|
||||
QList<QAction*> actions = parent->actions();
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
} else {
|
||||
// This is a valid non-separator non-menu action, so check it against the currently entered string.
|
||||
|
||||
QAction* a = actions.at(i);
|
||||
// Strip out all &s from the action's name
|
||||
QString comp = a->text().replace("&", "");
|
||||
|
||||
// Ignore separator actions
|
||||
if (!a->isSeparator()) {
|
||||
// See if the action's name contains any of the currently entered string
|
||||
if (comp.contains(s, Qt::CaseInsensitive)) {
|
||||
// If so, we add it to the list widget.
|
||||
QListWidgetItem *item = new QListWidgetItem(
|
||||
QStringLiteral("%1\n(%2)").arg(comp, menu_text),
|
||||
list_widget);
|
||||
|
||||
if (a->menu() != nullptr) {
|
||||
// Add a pointer to the original QAction in the item's data
|
||||
item->setData(Qt::UserRole + 1,
|
||||
reinterpret_cast<quintptr>(a));
|
||||
|
||||
// If the action is a menu, run this function recursively on it
|
||||
search_update(s, menu_text, a->menu());
|
||||
|
||||
} else {
|
||||
|
||||
// This is a valid non-separator non-menu action, so check it against the currently entered string.
|
||||
|
||||
// Strip out all &s from the action's name
|
||||
QString comp = a->text().replace("&", "");
|
||||
|
||||
// See if the action's name contains any of the currently entered string
|
||||
if (comp.contains(s, Qt::CaseInsensitive)) {
|
||||
|
||||
// If so, we add it to the list widget.
|
||||
QListWidgetItem* item = new QListWidgetItem(QStringLiteral("%1\n(%2)").arg(comp, menu_text), list_widget);
|
||||
|
||||
// Add a pointer to the original QAction in the item's data
|
||||
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
|
||||
|
||||
list_widget->addItem(item);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
list_widget->addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionSearch::perform_action() {
|
||||
void ActionSearch::perform_action()
|
||||
{
|
||||
// Loop over all the items in the list and if we find one that's selected, we trigger it.
|
||||
QList<QListWidgetItem *> selected_items = list_widget->selectedItems();
|
||||
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
||||
QListWidgetItem *item = selected_items.at(0);
|
||||
|
||||
// Loop over all the items in the list and if we find one that's selected, we trigger it.
|
||||
QList<QListWidgetItem*> selected_items = list_widget->selectedItems();
|
||||
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
||||
// Get QAction pointer from item's data
|
||||
QAction *a = reinterpret_cast<QAction *>(
|
||||
item->data(Qt::UserRole + 1).value<quintptr>());
|
||||
|
||||
QListWidgetItem* item = selected_items.at(0);
|
||||
|
||||
// Get QAction pointer from item's data
|
||||
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
|
||||
|
||||
a->trigger();
|
||||
|
||||
}
|
||||
|
||||
// Close this popup
|
||||
accept();
|
||||
a->trigger();
|
||||
}
|
||||
|
||||
// Close this popup
|
||||
accept();
|
||||
}
|
||||
|
||||
void ActionSearch::move_selection_up() {
|
||||
void ActionSearch::move_selection_up()
|
||||
{
|
||||
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
|
||||
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
|
||||
// bottom item).
|
||||
|
||||
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
|
||||
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
|
||||
// bottom item).
|
||||
|
||||
int lim = list_widget->count();
|
||||
for (int i=1;i<lim;i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i-1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i-1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
int lim = list_widget->count();
|
||||
for (int i = 1; i < lim; i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i - 1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionSearch::move_selection_down() {
|
||||
void ActionSearch::move_selection_down()
|
||||
{
|
||||
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
|
||||
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
|
||||
// bottom item).
|
||||
|
||||
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
|
||||
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
|
||||
// bottom item).
|
||||
|
||||
int lim = list_widget->count()-1;
|
||||
for (int i=0;i<lim;i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i+1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i+1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
int lim = list_widget->count() - 1;
|
||||
for (int i = 0; i < lim; i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i + 1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {}
|
||||
ActionSearchEntry::ActionSearchEntry(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionSearchEntry::event(QEvent *e)
|
||||
{
|
||||
switch (e->type()) {
|
||||
case QEvent::ShortcutOverride:
|
||||
switch (static_cast<QKeyEvent*>(e)->key()) {
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_Down:
|
||||
e->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::KeyPress:
|
||||
// Listen for up/down, otherwise pass the key event to the base class.
|
||||
switch (static_cast<QKeyEvent*>(e)->key()) {
|
||||
case Qt::Key_Up:
|
||||
e->accept();
|
||||
emit moveSelectionUp();
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
e->accept();
|
||||
emit moveSelectionDown();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (e->type()) {
|
||||
case QEvent::ShortcutOverride:
|
||||
switch (static_cast<QKeyEvent *>(e)->key()) {
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_Down:
|
||||
e->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::KeyPress:
|
||||
// Listen for up/down, otherwise pass the key event to the base class.
|
||||
switch (static_cast<QKeyEvent *>(e)->key()) {
|
||||
case Qt::Key_Up:
|
||||
e->accept();
|
||||
emit moveSelectionUp();
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
e->accept();
|
||||
emit moveSelectionDown();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QLineEdit::event(e);
|
||||
return QLineEdit::event(e);
|
||||
}
|
||||
|
||||
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {}
|
||||
|
||||
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) {
|
||||
|
||||
// Indiscriminately emit a signal on any double click
|
||||
emit dbl_click();
|
||||
ActionSearchList::ActionSearchList(QWidget *parent)
|
||||
: QListWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *)
|
||||
{
|
||||
// Indiscriminately emit a signal on any double click
|
||||
emit dbl_click();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ActionSearchList;
|
||||
|
||||
@@ -39,11 +40,10 @@ class ActionSearchList;
|
||||
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
|
||||
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
|
||||
*/
|
||||
class ActionSearch : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
class ActionSearch : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief ActionSearch Constructor
|
||||
*
|
||||
* Create ActionSearch popup.
|
||||
@@ -52,14 +52,14 @@ public:
|
||||
*
|
||||
* QWidget parent. Usually MainWindow.
|
||||
*/
|
||||
ActionSearch(QWidget* parent);
|
||||
ActionSearch(QWidget *parent);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set the menu bar to use in this action search
|
||||
*/
|
||||
void SetMenuBar(QMenuBar* menu_bar);
|
||||
void SetMenuBar(QMenuBar *menu_bar);
|
||||
private slots:
|
||||
/**
|
||||
/**
|
||||
* @brief Update the list of actions according to a search query
|
||||
*
|
||||
* This function adds/removes actions in the action list according to a given search query entered by the user.
|
||||
@@ -82,42 +82,44 @@ private slots:
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
*
|
||||
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
|
||||
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
|
||||
* triggered and the popup closes anyway.
|
||||
*/
|
||||
void perform_action();
|
||||
void perform_action();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Move selection up
|
||||
*
|
||||
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
|
||||
* selection is already at the top of the list, this is a no-op.
|
||||
*/
|
||||
void move_selection_up();
|
||||
void move_selection_up();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Move selection down
|
||||
*
|
||||
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
|
||||
* selection is already at the bottom of the list, this is a no-op.
|
||||
*/
|
||||
void move_selection_down();
|
||||
void move_selection_down();
|
||||
|
||||
private:
|
||||
/**
|
||||
/**
|
||||
* @brief Main widget that shows the list of commands
|
||||
*/
|
||||
ActionSearchList* list_widget;
|
||||
ActionSearchList *list_widget;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Attached menu bar object
|
||||
*/
|
||||
QMenuBar* menu_bar_;
|
||||
QMenuBar *menu_bar_;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -127,25 +129,26 @@ private:
|
||||
* to a slot that triggers the currently selected action.
|
||||
*/
|
||||
class ActionSearchList : public QListWidget {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief ActionSearchList Constructor
|
||||
* @param parent
|
||||
*
|
||||
* Usually ActionSearch.
|
||||
*/
|
||||
ActionSearchList(QWidget* parent);
|
||||
ActionSearchList(QWidget *parent);
|
||||
|
||||
protected:
|
||||
/**
|
||||
/**
|
||||
* @brief Override of QListWidget's double click event that emits a signal.
|
||||
*/
|
||||
void mouseDoubleClickEvent(QMouseEvent *);
|
||||
void mouseDoubleClickEvent(QMouseEvent *);
|
||||
signals:
|
||||
/**
|
||||
/**
|
||||
* @brief Signal emitted when a QListWidget item is double clicked.
|
||||
*/
|
||||
void dbl_click();
|
||||
void dbl_click();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -155,32 +158,33 @@ signals:
|
||||
* can connect them to moving the current selection up or down.
|
||||
*/
|
||||
class ActionSearchEntry : public QLineEdit {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief ActionSearchEntry
|
||||
* @param parent
|
||||
*
|
||||
* Usually ActionSearch.
|
||||
*/
|
||||
ActionSearchEntry(QWidget* parent);
|
||||
ActionSearchEntry(QWidget *parent);
|
||||
|
||||
protected:
|
||||
/**
|
||||
/**
|
||||
* @brief Override of QLineEdit's key press event that listens for up/down key presses.
|
||||
* @param event
|
||||
*/
|
||||
virtual bool event(QEvent *e) override;
|
||||
virtual bool event(QEvent *e) override;
|
||||
|
||||
signals:
|
||||
/**
|
||||
/**
|
||||
* @brief Emitted when the user presses the up arrow key.
|
||||
*/
|
||||
void moveSelectionUp();
|
||||
void moveSelectionUp();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Emitted when the user presses the down arrow key.
|
||||
*/
|
||||
void moveSelectionDown();
|
||||
void moveSelectionDown();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -29,108 +29,127 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super QDialog
|
||||
|
||||
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message, const QStringList &recoveries, bool autocheck_latest, QWidget* parent) :
|
||||
QDialog(parent)
|
||||
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message,
|
||||
const QStringList &recoveries,
|
||||
bool autocheck_latest, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
Init(message);
|
||||
Init(message);
|
||||
|
||||
PopulateTree(recoveries, autocheck_latest);
|
||||
PopulateTree(recoveries, autocheck_latest);
|
||||
}
|
||||
|
||||
void AutoRecoveryDialog::accept()
|
||||
{
|
||||
foreach (QTreeWidgetItem* checkable, checkable_items_) {
|
||||
if (checkable->checkState(0) == Qt::Checked) {
|
||||
QString filename = checkable->data(0, kFilenameRole).toString();
|
||||
Core::instance()->OpenRecoveryProject(filename);
|
||||
}
|
||||
}
|
||||
foreach (QTreeWidgetItem *checkable, checkable_items_) {
|
||||
if (checkable->checkState(0) == Qt::Checked) {
|
||||
QString filename = checkable->data(0, kFilenameRole).toString();
|
||||
Core::instance()->OpenRecoveryProject(filename);
|
||||
}
|
||||
}
|
||||
|
||||
super::accept();
|
||||
super::accept();
|
||||
}
|
||||
|
||||
void AutoRecoveryDialog::Init(const QString& header_text)
|
||||
void AutoRecoveryDialog::Init(const QString &header_text)
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
setWindowTitle(tr("Auto-Recovery"));
|
||||
setWindowTitle(tr("Auto-Recovery"));
|
||||
|
||||
layout->addWidget(new QLabel(header_text));
|
||||
layout->addWidget(new QLabel(header_text));
|
||||
|
||||
tree_widget_ = new QTreeWidget();
|
||||
tree_widget_->setHeaderHidden(true);
|
||||
layout->addWidget(tree_widget_);
|
||||
tree_widget_ = new QTreeWidget();
|
||||
tree_widget_->setHeaderHidden(true);
|
||||
layout->addWidget(tree_widget_);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &AutoRecoveryDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &AutoRecoveryDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
&AutoRecoveryDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||
&AutoRecoveryDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
}
|
||||
|
||||
void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autocheck_latest)
|
||||
void AutoRecoveryDialog::PopulateTree(const QStringList &recoveries,
|
||||
bool autocheck_latest)
|
||||
{
|
||||
// Each entry in `recoveries` is a directory with 1+ recovery projects in it
|
||||
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
|
||||
// Each entry in `recoveries` is a directory with 1+ recovery projects in it
|
||||
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
|
||||
|
||||
foreach (const QString& recovery_folder, recoveries) {
|
||||
QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
|
||||
foreach (const QString &recovery_folder, recoveries) {
|
||||
QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
|
||||
|
||||
QString pretty_name;
|
||||
{
|
||||
// Retrieve pretty name
|
||||
QFile pretty_name_file(recovery_dir.filePath(QStringLiteral("realname.txt")));
|
||||
if (pretty_name_file.open(QFile::ReadOnly)) {
|
||||
// Read pretty name that we should have written in the autorecovery process
|
||||
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
|
||||
pretty_name_file.close();
|
||||
}
|
||||
QString pretty_name;
|
||||
{
|
||||
// Retrieve pretty name
|
||||
QFile pretty_name_file(
|
||||
recovery_dir.filePath(QStringLiteral("realname.txt")));
|
||||
if (pretty_name_file.open(QFile::ReadOnly)) {
|
||||
// Read pretty name that we should have written in the autorecovery process
|
||||
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
|
||||
pretty_name_file.close();
|
||||
}
|
||||
|
||||
if (pretty_name.isEmpty()) {
|
||||
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
|
||||
pretty_name = recovery_dir.dirName();
|
||||
}
|
||||
}
|
||||
if (pretty_name.isEmpty()) {
|
||||
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
|
||||
pretty_name = recovery_dir.dirName();
|
||||
}
|
||||
}
|
||||
|
||||
QTreeWidgetItem* top_level = new QTreeWidgetItem(tree_widget_);
|
||||
top_level->setText(0, pretty_name);
|
||||
QTreeWidgetItem *top_level = new QTreeWidgetItem(tree_widget_);
|
||||
top_level->setText(0, pretty_name);
|
||||
|
||||
{
|
||||
// Populate with recoveries
|
||||
QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
|
||||
for (int i=0; i<entries.size(); i++) {
|
||||
const QString& entry = entries.at(i);
|
||||
{
|
||||
// Populate with recoveries
|
||||
QStringList entries =
|
||||
recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
|
||||
QDir::Name | QDir::Reversed);
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
const QString &entry = entries.at(i);
|
||||
|
||||
if (entry.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) {
|
||||
QTreeWidgetItem* entry_item = new QTreeWidgetItem(top_level);
|
||||
if (entry.endsWith(QStringLiteral(".ove"),
|
||||
Qt::CaseInsensitive)) {
|
||||
QTreeWidgetItem *entry_item =
|
||||
new QTreeWidgetItem(top_level);
|
||||
|
||||
bool ok;
|
||||
qint64 recovery_time = entry.left(entry.indexOf('.')).toLongLong(&ok);
|
||||
QString entry_name;
|
||||
bool ok;
|
||||
qint64 recovery_time =
|
||||
entry.left(entry.indexOf('.')).toLongLong(&ok);
|
||||
QString entry_name;
|
||||
|
||||
if (ok) {
|
||||
// Set as time/date of recovery
|
||||
entry_name = QDateTime::fromSecsSinceEpoch(recovery_time).toString();
|
||||
} else {
|
||||
// Fallback if we couldn't discern a date from this
|
||||
entry_name = entry;
|
||||
}
|
||||
if (ok) {
|
||||
// Set as time/date of recovery
|
||||
entry_name =
|
||||
QDateTime::fromSecsSinceEpoch(recovery_time)
|
||||
.toString();
|
||||
} else {
|
||||
// Fallback if we couldn't discern a date from this
|
||||
entry_name = entry;
|
||||
}
|
||||
|
||||
entry_item->setText(0, entry_name);
|
||||
entry_item->setData(0, kFilenameRole, recovery_dir.filePath(entry));
|
||||
entry_item->setText(0, entry_name);
|
||||
entry_item->setData(0, kFilenameRole,
|
||||
recovery_dir.filePath(entry));
|
||||
|
||||
// Allow to be checked, auto-checking the first entry
|
||||
entry_item->setCheckState(0, (autocheck_latest && top_level->childCount() == 1) ? Qt::Checked : Qt::Unchecked);
|
||||
// Allow to be checked, auto-checking the first entry
|
||||
entry_item->setCheckState(
|
||||
0, (autocheck_latest && top_level->childCount() == 1) ?
|
||||
Qt::Checked :
|
||||
Qt::Unchecked);
|
||||
|
||||
checkable_items_.append(entry_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkable_items_.append(entry_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,30 +26,28 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class AutoRecoveryDialog : public QDialog
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class AutoRecoveryDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
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:
|
||||
virtual void accept() override;
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
void Init(const QString &header_text);
|
||||
void Init(const QString &header_text);
|
||||
|
||||
void PopulateTree(const QStringList &recoveries, bool autocheck);
|
||||
void PopulateTree(const QStringList &recoveries, bool autocheck);
|
||||
|
||||
QTreeWidget* tree_widget_;
|
||||
QTreeWidget *tree_widget_;
|
||||
|
||||
QVector<QTreeWidgetItem*> checkable_items_;
|
||||
|
||||
enum DataRole {
|
||||
kFilenameRole = Qt::UserRole
|
||||
};
|
||||
QVector<QTreeWidgetItem *> checkable_items_;
|
||||
|
||||
enum DataRole { kFilenameRole = Qt::UserRole };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+120
-101
@@ -26,167 +26,186 @@
|
||||
|
||||
#include "common/qtutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
color_manager_(color_manager)
|
||||
namespace olive
|
||||
{
|
||||
setWindowTitle(tr("Select Color"));
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, color_manager_(color_manager)
|
||||
{
|
||||
setWindowTitle(tr("Select Color"));
|
||||
|
||||
QSplitter* splitter = new QSplitter(Qt::Horizontal);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
QWidget* graphics_area = new QWidget();
|
||||
splitter->addWidget(graphics_area);
|
||||
QSplitter *splitter = new QSplitter(Qt::Horizontal);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
|
||||
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
|
||||
QWidget *graphics_area = new QWidget();
|
||||
splitter->addWidget(graphics_area);
|
||||
|
||||
QHBoxLayout* wheel_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(wheel_layout);
|
||||
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
|
||||
|
||||
color_wheel_ = new ColorWheelWidget();
|
||||
wheel_layout->addWidget(color_wheel_);
|
||||
QHBoxLayout *wheel_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(wheel_layout);
|
||||
|
||||
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
|
||||
hsv_value_gradient_->setFixedWidth(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
|
||||
wheel_layout->addWidget(hsv_value_gradient_);
|
||||
color_wheel_ = new ColorWheelWidget();
|
||||
wheel_layout->addWidget(color_wheel_);
|
||||
|
||||
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(swatch_layout);
|
||||
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
|
||||
hsv_value_gradient_->setFixedWidth(
|
||||
QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
|
||||
wheel_layout->addWidget(hsv_value_gradient_);
|
||||
|
||||
swatch_layout->addStretch();
|
||||
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(swatch_layout);
|
||||
|
||||
swatch_ = new ColorSwatchChooser(color_manager_);
|
||||
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
swatch_layout->addWidget(swatch_);
|
||||
swatch_layout->addStretch();
|
||||
|
||||
swatch_layout->addStretch();
|
||||
swatch_ = new ColorSwatchChooser(color_manager_);
|
||||
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
swatch_layout->addWidget(swatch_);
|
||||
|
||||
QWidget* value_area = new QWidget();
|
||||
QVBoxLayout* value_layout = new QVBoxLayout(value_area);
|
||||
value_layout->setSpacing(0);
|
||||
splitter->addWidget(value_area);
|
||||
swatch_layout->addStretch();
|
||||
|
||||
color_values_widget_ = new ColorValuesWidget(color_manager_);
|
||||
color_values_widget_->IgnorePickFrom(this);
|
||||
value_layout->addWidget(color_values_widget_);
|
||||
QWidget *value_area = new QWidget();
|
||||
QVBoxLayout *value_layout = new QVBoxLayout(value_area);
|
||||
value_layout->setSpacing(0);
|
||||
splitter->addWidget(value_area);
|
||||
|
||||
chooser_ = new ColorSpaceChooser(color_manager_);
|
||||
color_values_widget_ = new ColorValuesWidget(color_manager_);
|
||||
color_values_widget_->IgnorePickFrom(this);
|
||||
value_layout->addWidget(color_values_widget_);
|
||||
|
||||
value_layout->addWidget(chooser_);
|
||||
chooser_ = new ColorSpaceChooser(color_manager_);
|
||||
|
||||
// Split window 50/50
|
||||
splitter->setSizes({INT_MAX, INT_MAX});
|
||||
value_layout->addWidget(chooser_);
|
||||
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
// Split window 50/50
|
||||
splitter->setSizes({ INT_MAX, INT_MAX });
|
||||
|
||||
connect(color_wheel_, &ColorWheelWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
|
||||
color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
|
||||
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_,
|
||||
&ColorSwatchChooser::SetCurrentColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||
color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||
color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
|
||||
swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
|
||||
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
|
||||
color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_,
|
||||
&ColorSwatchChooser::SetCurrentColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_,
|
||||
&ColorGradientWidget::SetSelectedColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_,
|
||||
&ColorWheelWidget::SetSelectedColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_,
|
||||
&ColorValuesWidget::SetColor);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
connect(color_wheel_, &ColorWheelWidget::DiameterChanged,
|
||||
hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
|
||||
|
||||
SetColor(start);
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged);
|
||||
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
||||
SetColor(start);
|
||||
|
||||
// Set default size ratio to 2:1
|
||||
resize(sizeHint().height() * 2, sizeHint().height());
|
||||
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this,
|
||||
&ColorDialog::ColorSpaceChanged);
|
||||
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
||||
|
||||
// Set default size ratio to 2:1
|
||||
resize(sizeHint().height() * 2, sizeHint().height());
|
||||
}
|
||||
|
||||
void ColorDialog::SetColor(const ManagedColor &start)
|
||||
{
|
||||
chooser_->set_input(start.color_input());
|
||||
chooser_->set_output(start.color_output());
|
||||
chooser_->set_input(start.color_input());
|
||||
chooser_->set_output(start.color_output());
|
||||
|
||||
Color managed_start;
|
||||
Color managed_start;
|
||||
|
||||
if (start.color_input().isEmpty()) {
|
||||
if (start.color_input().isEmpty()) {
|
||||
managed_start = start;
|
||||
|
||||
managed_start = start;
|
||||
} else {
|
||||
// Convert reference color to the input space
|
||||
ColorProcessorPtr linear_to_input = ColorProcessor::Create(
|
||||
color_manager_, color_manager_->GetReferenceColorSpace(),
|
||||
start.color_input());
|
||||
|
||||
} else {
|
||||
managed_start = linear_to_input->ConvertColor(start);
|
||||
}
|
||||
|
||||
// Convert reference color to the input space
|
||||
ColorProcessorPtr linear_to_input = ColorProcessor::Create(color_manager_,
|
||||
color_manager_->GetReferenceColorSpace(),
|
||||
start.color_input());
|
||||
|
||||
managed_start = linear_to_input->ConvertColor(start);
|
||||
|
||||
}
|
||||
|
||||
color_wheel_->SetSelectedColor(managed_start);
|
||||
hsv_value_gradient_->SetSelectedColor(managed_start);
|
||||
color_values_widget_->SetColor(managed_start);
|
||||
swatch_->SetCurrentColor(managed_start);
|
||||
color_wheel_->SetSelectedColor(managed_start);
|
||||
hsv_value_gradient_->SetSelectedColor(managed_start);
|
||||
color_values_widget_->SetColor(managed_start);
|
||||
swatch_->SetCurrentColor(managed_start);
|
||||
}
|
||||
|
||||
ManagedColor ColorDialog::GetSelectedColor() const
|
||||
{
|
||||
ManagedColor selected = color_wheel_->GetSelectedColor();
|
||||
ManagedColor selected = color_wheel_->GetSelectedColor();
|
||||
|
||||
// Convert to linear and return a linear color
|
||||
if (input_to_ref_processor_) {
|
||||
selected = input_to_ref_processor_->ConvertColor(selected);
|
||||
}
|
||||
// Convert to linear and return a linear color
|
||||
if (input_to_ref_processor_) {
|
||||
selected = input_to_ref_processor_->ConvertColor(selected);
|
||||
}
|
||||
|
||||
selected.set_color_input(GetColorSpaceInput());
|
||||
selected.set_color_output(GetColorSpaceOutput());
|
||||
selected.set_color_input(GetColorSpaceInput());
|
||||
selected.set_color_output(GetColorSpaceOutput());
|
||||
|
||||
return selected;
|
||||
return selected;
|
||||
}
|
||||
|
||||
QString ColorDialog::GetColorSpaceInput() const
|
||||
{
|
||||
return chooser_->input();
|
||||
return chooser_->input();
|
||||
}
|
||||
|
||||
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_,
|
||||
color_manager_->GetReferenceColorSpace(),
|
||||
output);
|
||||
ColorProcessorPtr ref_to_display = ColorProcessor::Create(
|
||||
color_manager_, color_manager_->GetReferenceColorSpace(), 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
|
||||
// crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget)
|
||||
// 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)
|
||||
|
||||
/*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(),
|
||||
/*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(),
|
||||
color_manager_->GetReferenceColorSpace(),
|
||||
display,
|
||||
view,
|
||||
look,
|
||||
ColorProcessor::kInverse);*/
|
||||
|
||||
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
|
||||
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
|
||||
color_values_widget_->SetColorProcessor(input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
|
||||
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
|
||||
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_,
|
||||
ref_to_display);
|
||||
color_values_widget_->SetColorProcessor(
|
||||
input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
#include "widget/colorwheel/colorvalueswidget.h"
|
||||
#include "widget/colorwheel/colorwheelwidget.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ColorDialog : public QDialog
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class ColorDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief ColorDialog Constructor
|
||||
*
|
||||
* @param color_manager
|
||||
@@ -56,40 +56,41 @@ public:
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* The color is always returned in the ColorManager's reference space (usually scene linear).
|
||||
*/
|
||||
ManagedColor GetSelectedColor() const;
|
||||
ManagedColor GetSelectedColor() const;
|
||||
|
||||
QString GetColorSpaceInput() const;
|
||||
QString GetColorSpaceInput() const;
|
||||
|
||||
ColorTransform GetColorSpaceOutput() const;
|
||||
ColorTransform GetColorSpaceOutput() const;
|
||||
|
||||
public slots:
|
||||
void SetColor(const ManagedColor &c);
|
||||
void SetColor(const ManagedColor &c);
|
||||
|
||||
private:
|
||||
ColorManager* color_manager_;
|
||||
ColorManager *color_manager_;
|
||||
|
||||
ColorWheelWidget* color_wheel_;
|
||||
ColorWheelWidget *color_wheel_;
|
||||
|
||||
ColorValuesWidget* color_values_widget_;
|
||||
ColorValuesWidget *color_values_widget_;
|
||||
|
||||
ColorGradientWidget* hsv_value_gradient_;
|
||||
ColorGradientWidget *hsv_value_gradient_;
|
||||
|
||||
ColorProcessorPtr input_to_ref_processor_;
|
||||
ColorProcessorPtr input_to_ref_processor_;
|
||||
|
||||
ColorSpaceChooser* chooser_;
|
||||
ColorSpaceChooser *chooser_;
|
||||
|
||||
ColorSwatchChooser *swatch_;
|
||||
ColorSwatchChooser *swatch_;
|
||||
|
||||
private slots:
|
||||
void ColorSpaceChanged(const QString& input, const ColorTransform &output);
|
||||
|
||||
void ColorSpaceChanged(const QString &input, const ColorTransform &output);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -26,66 +26,68 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
|
||||
QDialog(parent)
|
||||
namespace olive
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
QSplitter* splitter = new QSplitter();
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
ConfigDialogBase::ConfigDialogBase(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
list_widget_ = new QListWidget();
|
||||
QSplitter *splitter = new QSplitter();
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
|
||||
preference_pane_stack_ = new QStackedWidget(this);
|
||||
list_widget_ = new QListWidget();
|
||||
|
||||
splitter->addWidget(list_widget_);
|
||||
splitter->addWidget(preference_pane_stack_);
|
||||
preference_pane_stack_ = new QStackedWidget(this);
|
||||
|
||||
QDialogButtonBox* button_box = new QDialogButtonBox(this);
|
||||
button_box->setOrientation(Qt::Horizontal);
|
||||
button_box->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
splitter->addWidget(list_widget_);
|
||||
splitter->addWidget(preference_pane_stack_);
|
||||
|
||||
layout->addWidget(button_box);
|
||||
QDialogButtonBox *button_box = new QDialogButtonBox(this);
|
||||
button_box->setOrientation(Qt::Horizontal);
|
||||
button_box->setStandardButtons(QDialogButtonBox::Cancel |
|
||||
QDialogButtonBox::Ok);
|
||||
|
||||
connect(button_box, &QDialogButtonBox::accepted, this, &ConfigDialogBase::accept);
|
||||
connect(button_box, &QDialogButtonBox::rejected, this, &ConfigDialogBase::reject);
|
||||
layout->addWidget(button_box);
|
||||
|
||||
connect(list_widget_,
|
||||
&QListWidget::currentRowChanged,
|
||||
preference_pane_stack_,
|
||||
&QStackedWidget::setCurrentIndex);
|
||||
connect(button_box, &QDialogButtonBox::accepted, this,
|
||||
&ConfigDialogBase::accept);
|
||||
connect(button_box, &QDialogButtonBox::rejected, this,
|
||||
&ConfigDialogBase::reject);
|
||||
|
||||
connect(list_widget_, &QListWidget::currentRowChanged,
|
||||
preference_pane_stack_, &QStackedWidget::setCurrentIndex);
|
||||
}
|
||||
|
||||
void ConfigDialogBase::accept()
|
||||
{
|
||||
foreach (ConfigDialogBaseTab* tab, tabs_) {
|
||||
if (!tab->Validate()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
||||
if (!tab->Validate()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
foreach (ConfigDialogBaseTab* tab, tabs_) {
|
||||
tab->Accept(command);
|
||||
}
|
||||
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
||||
tab->Accept(command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
|
||||
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
|
||||
|
||||
AcceptEvent();
|
||||
AcceptEvent();
|
||||
|
||||
QDialog::accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ConfigDialogBase::AddTab(ConfigDialogBaseTab *tab, const QString &title)
|
||||
{
|
||||
list_widget_->addItem(title);
|
||||
preference_pane_stack_->addWidget(tab);
|
||||
list_widget_->addItem(title);
|
||||
preference_pane_stack_->addWidget(tab);
|
||||
|
||||
tabs_.append(tab);
|
||||
tabs_.append(tab);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,32 +27,33 @@
|
||||
|
||||
#include "configdialogbasetab.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ConfigDialogBase : public QDialog
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class ConfigDialogBase : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConfigDialogBase(QWidget* parent = nullptr);
|
||||
ConfigDialogBase(QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
/**
|
||||
* @brief Override of accept to save preferences to Config.
|
||||
*/
|
||||
virtual void accept() override;
|
||||
virtual void accept() override;
|
||||
|
||||
protected:
|
||||
void AddTab(ConfigDialogBaseTab* tab, const QString& title);
|
||||
void AddTab(ConfigDialogBaseTab *tab, const QString &title);
|
||||
|
||||
virtual void AcceptEvent(){}
|
||||
virtual void AcceptEvent()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
QListWidget* list_widget_;
|
||||
QListWidget *list_widget_;
|
||||
|
||||
QStackedWidget* preference_pane_stack_;
|
||||
|
||||
QList<ConfigDialogBaseTab*> tabs_;
|
||||
QStackedWidget *preference_pane_stack_;
|
||||
|
||||
QList<ConfigDialogBaseTab *> tabs_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
|
||||
#include "configdialogbasetab.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
bool ConfigDialogBaseTab::Validate()
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,16 +26,16 @@
|
||||
#include "config/config.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ConfigDialogBaseTab : public QWidget
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ConfigDialogBaseTab : public QWidget {
|
||||
public:
|
||||
ConfigDialogBaseTab() = default;
|
||||
ConfigDialogBaseTab() = default;
|
||||
|
||||
virtual bool Validate();
|
||||
virtual bool Validate();
|
||||
|
||||
virtual void Accept(MultiUndoCommand *parent) = 0;
|
||||
virtual void Accept(MultiUndoCommand *parent) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -28,86 +28,101 @@
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
folder_(folder)
|
||||
namespace olive
|
||||
{
|
||||
QGridLayout* layout = new QGridLayout(this);
|
||||
|
||||
int row = 0;
|
||||
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, folder_(folder)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())), row, 0, 1, 2);
|
||||
setWindowTitle(tr("Disk Cache Settings"));
|
||||
int row = 0;
|
||||
|
||||
row++;
|
||||
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())),
|
||||
row, 0, 1, 2);
|
||||
setWindowTitle(tr("Disk Cache Settings"));
|
||||
|
||||
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
|
||||
row++;
|
||||
|
||||
maximum_cache_slider_ = new FloatSlider();
|
||||
maximum_cache_slider_->SetFormat(tr("%1 GB"));
|
||||
maximum_cache_slider_->SetMinimum(1.0);
|
||||
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) / static_cast<double>(kBytesInGigabyte));
|
||||
layout->addWidget(maximum_cache_slider_, row, 1);
|
||||
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
|
||||
|
||||
row++;
|
||||
maximum_cache_slider_ = new FloatSlider();
|
||||
maximum_cache_slider_->SetFormat(tr("%1 GB"));
|
||||
maximum_cache_slider_->SetMinimum(1.0);
|
||||
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) /
|
||||
static_cast<double>(kBytesInGigabyte));
|
||||
layout->addWidget(maximum_cache_slider_, row, 1);
|
||||
|
||||
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
|
||||
connect(clear_cache_btn_, &QPushButton::clicked, this, static_cast<void(DiskCacheDialog::*)()>(&DiskCacheDialog::ClearDiskCache));
|
||||
layout->addWidget(clear_cache_btn_, row, 1);
|
||||
row++;
|
||||
|
||||
row++;
|
||||
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
|
||||
connect(clear_cache_btn_, &QPushButton::clicked, this,
|
||||
static_cast<void (DiskCacheDialog::*)()>(
|
||||
&DiskCacheDialog::ClearDiskCache));
|
||||
layout->addWidget(clear_cache_btn_, row, 1);
|
||||
|
||||
clear_disk_cache_ = new QCheckBox(tr("Automatically clear disk cache on close"));
|
||||
clear_disk_cache_->setChecked(folder->GetClearOnClose());
|
||||
layout->addWidget(clear_disk_cache_, row, 1);
|
||||
row++;
|
||||
|
||||
row++;
|
||||
clear_disk_cache_ =
|
||||
new QCheckBox(tr("Automatically clear disk cache on close"));
|
||||
clear_disk_cache_->setChecked(folder->GetClearOnClose());
|
||||
layout->addWidget(clear_disk_cache_, row, 1);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &DiskCacheDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &DiskCacheDialog::reject);
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
row++;
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
&DiskCacheDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||
&DiskCacheDialog::reject);
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
}
|
||||
|
||||
void DiskCacheDialog::accept()
|
||||
{
|
||||
qint64 new_disk_cache_limit = qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
|
||||
if (new_disk_cache_limit != folder_->GetLimit()) {
|
||||
folder_->SetLimit(new_disk_cache_limit);
|
||||
}
|
||||
qint64 new_disk_cache_limit =
|
||||
qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
|
||||
if (new_disk_cache_limit != folder_->GetLimit()) {
|
||||
folder_->SetLimit(new_disk_cache_limit);
|
||||
}
|
||||
|
||||
if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) {
|
||||
folder_->SetClearOnClose(clear_disk_cache_->isChecked());
|
||||
}
|
||||
if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) {
|
||||
folder_->SetClearOnClose(clear_disk_cache_->isChecked());
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
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,
|
||||
tr("Clear Disk Cache"),
|
||||
tr("Are you sure you want to clear the disk cache in '%1'?").arg(path),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
if (clear_btn) clear_btn->setEnabled(false);
|
||||
if (QMessageBox::question(
|
||||
parent, tr("Clear Disk Cache"),
|
||||
tr("Are you sure you want to clear the disk cache in '%1'?")
|
||||
.arg(path),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
if (clear_btn)
|
||||
clear_btn->setEnabled(false);
|
||||
|
||||
if (DiskManager::instance()->ClearDiskCache(path)) {
|
||||
if (clear_btn) clear_btn->setText(tr("Disk Cache Cleared"));
|
||||
} else {
|
||||
QMessageBox::information(parent,
|
||||
tr("Clear Disk Cache"),
|
||||
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
|
||||
QMessageBox::Ok);
|
||||
if (clear_btn) clear_btn->setText(tr("Disk Cache Partially Cleared"));
|
||||
}
|
||||
}
|
||||
if (DiskManager::instance()->ClearDiskCache(path)) {
|
||||
if (clear_btn)
|
||||
clear_btn->setText(tr("Disk Cache Cleared"));
|
||||
} else {
|
||||
QMessageBox::information(
|
||||
parent, tr("Clear Disk Cache"),
|
||||
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
|
||||
QMessageBox::Ok);
|
||||
if (clear_btn)
|
||||
clear_btn->setText(tr("Disk Cache Partially Cleared"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,31 +28,31 @@
|
||||
#include "render/diskmanager.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class DiskCacheDialog : public QDialog
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiskCacheDialog(DiskCacheFolder* folder, QWidget* parent = nullptr);
|
||||
|
||||
static void ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn = nullptr);
|
||||
class DiskCacheDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
|
||||
|
||||
static void ClearDiskCache(const QString &path, QWidget *parent,
|
||||
QPushButton *clear_btn = nullptr);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
DiskCacheFolder* folder_;
|
||||
DiskCacheFolder *folder_;
|
||||
|
||||
FloatSlider* maximum_cache_slider_;
|
||||
FloatSlider *maximum_cache_slider_;
|
||||
|
||||
QCheckBox* clear_disk_cache_;
|
||||
QCheckBox *clear_disk_cache_;
|
||||
|
||||
QPushButton* clear_cache_btn_;
|
||||
QPushButton *clear_cache_btn_;
|
||||
|
||||
private slots:
|
||||
void ClearDiskCache();
|
||||
|
||||
void ClearDiskCache();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -28,105 +28,111 @@
|
||||
#include "common/qtutils.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
AV1Section::AV1Section(QWidget *parent) :
|
||||
AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
|
||||
AV1Section::AV1Section(QWidget *parent)
|
||||
: AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
|
||||
{
|
||||
}
|
||||
|
||||
AV1Section::AV1Section(int default_crf, QWidget *parent) :
|
||||
CodecSection(parent)
|
||||
AV1Section::AV1Section(int default_crf, QWidget *parent)
|
||||
: CodecSection(parent)
|
||||
{
|
||||
QGridLayout* layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
layout->addWidget(new QLabel(tr("Preset:")), row, 0);
|
||||
int row = 0;
|
||||
layout->addWidget(new QLabel(tr("Preset:")), row, 0);
|
||||
|
||||
preset_combobox_ = new QComboBox();
|
||||
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"
|
||||
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
|
||||
preset_combobox_ = new QComboBox();
|
||||
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"
|
||||
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
|
||||
|
||||
for (int i = 0; i <= 13; i++)
|
||||
preset_combobox_->addItem(QString::number(i));
|
||||
for (int i = 0; i <= 13; i++)
|
||||
preset_combobox_->addItem(QString::number(i));
|
||||
|
||||
preset_combobox_->setCurrentIndex(8);
|
||||
preset_combobox_->setCurrentIndex(8);
|
||||
|
||||
layout->addWidget(preset_combobox_, row, 1);
|
||||
layout->addWidget(preset_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
||||
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
||||
|
||||
QComboBox* compression_box = new QComboBox();
|
||||
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"
|
||||
"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."));
|
||||
QComboBox *compression_box = new QComboBox();
|
||||
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"
|
||||
"Lower CRF values retain more detail at the cost of larger file sizes.\n"
|
||||
"The possible range of CRF in SVT-AV1 is 1-63."));
|
||||
|
||||
// These items must correspond to the CompressionMethod enum
|
||||
compression_box->addItem(tr("Constant Rate Factor"));
|
||||
// These items must correspond to the CompressionMethod enum
|
||||
compression_box->addItem(tr("Constant Rate Factor"));
|
||||
|
||||
layout->addWidget(compression_box, row, 1);
|
||||
layout->addWidget(compression_box, row, 1);
|
||||
|
||||
row++;
|
||||
row++;
|
||||
|
||||
compression_method_stack_ = new QStackedWidget();
|
||||
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
||||
compression_method_stack_ = new QStackedWidget();
|
||||
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
||||
|
||||
crf_section_ = new AV1CRFSection(default_crf);
|
||||
compression_method_stack_->addWidget(crf_section_);
|
||||
crf_section_ = new AV1CRFSection(default_crf);
|
||||
compression_method_stack_->addWidget(crf_section_);
|
||||
|
||||
connect(compression_box,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
compression_method_stack_,
|
||||
&QStackedWidget::setCurrentIndex);
|
||||
connect(
|
||||
compression_box,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
||||
}
|
||||
|
||||
void AV1Section::AddOpts(EncodingParams *params)
|
||||
{
|
||||
CompressionMethod method = static_cast<CompressionMethod>(
|
||||
compression_method_stack_->currentIndex());
|
||||
|
||||
CompressionMethod method = static_cast<CompressionMethod>(compression_method_stack_->currentIndex());
|
||||
if (method == kConstantRateFactor) {
|
||||
// Set Quantizer value
|
||||
params->set_video_option(QStringLiteral("qp"),
|
||||
QString::number(crf_section_->GetValue()));
|
||||
}
|
||||
|
||||
if (method == kConstantRateFactor) {
|
||||
|
||||
// Set Quantizer value
|
||||
params->set_video_option(QStringLiteral("qp"), QString::number(crf_section_->GetValue()));
|
||||
|
||||
}
|
||||
|
||||
params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex()));
|
||||
params->set_video_option(QStringLiteral("preset"),
|
||||
QString::number(preset_combobox_->currentIndex()));
|
||||
}
|
||||
|
||||
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) :
|
||||
QWidget(parent)
|
||||
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
crf_slider_ = new QSlider(Qt::Horizontal);
|
||||
crf_slider_->setMinimum(kMinimumCRF);
|
||||
crf_slider_->setMaximum(kMaximumCRF);
|
||||
crf_slider_->setValue(default_crf);
|
||||
layout->addWidget(crf_slider_);
|
||||
crf_slider_ = new QSlider(Qt::Horizontal);
|
||||
crf_slider_->setMinimum(kMinimumCRF);
|
||||
crf_slider_->setMaximum(kMaximumCRF);
|
||||
crf_slider_->setValue(default_crf);
|
||||
layout->addWidget(crf_slider_);
|
||||
|
||||
IntegerSlider* crf_input = new IntegerSlider();
|
||||
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
||||
crf_input->SetMinimum(kMinimumCRF);
|
||||
crf_input->SetMaximum(kMaximumCRF);
|
||||
crf_input->SetValue(default_crf);
|
||||
crf_input->SetDefaultValue(default_crf);
|
||||
layout->addWidget(crf_input);
|
||||
IntegerSlider *crf_input = new IntegerSlider();
|
||||
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(
|
||||
crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
||||
crf_input->SetMinimum(kMinimumCRF);
|
||||
crf_input->SetMaximum(kMaximumCRF);
|
||||
crf_input->SetValue(default_crf);
|
||||
crf_input->SetDefaultValue(default_crf);
|
||||
layout->addWidget(crf_input);
|
||||
|
||||
connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue);
|
||||
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_, &QSlider::setValue);
|
||||
connect(crf_slider_, &QSlider::valueChanged, crf_input,
|
||||
&IntegerSlider::SetValue);
|
||||
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_,
|
||||
&QSlider::setValue);
|
||||
}
|
||||
|
||||
int AV1CRFSection::GetValue() const
|
||||
{
|
||||
return crf_slider_->value();
|
||||
return crf_slider_->value();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,45 +28,43 @@
|
||||
#include "codecsection.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class AV1CRFSection : public QWidget
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class AV1CRFSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AV1CRFSection(int default_crf, QWidget* parent = nullptr);
|
||||
AV1CRFSection(int default_crf, QWidget *parent = nullptr);
|
||||
|
||||
int GetValue() const;
|
||||
int GetValue() const;
|
||||
|
||||
static const int kDefaultAV1CRF = 30;
|
||||
static const int kDefaultAV1CRF = 30;
|
||||
|
||||
private:
|
||||
static const int kMinimumCRF = 0;
|
||||
static const int kMaximumCRF = 63;
|
||||
|
||||
QSlider* crf_slider_;
|
||||
static const int kMinimumCRF = 0;
|
||||
static const int kMaximumCRF = 63;
|
||||
|
||||
QSlider *crf_slider_;
|
||||
};
|
||||
|
||||
class AV1Section : public CodecSection
|
||||
{
|
||||
Q_OBJECT
|
||||
class AV1Section : public CodecSection {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum CompressionMethod {
|
||||
kConstantRateFactor,
|
||||
};
|
||||
enum CompressionMethod {
|
||||
kConstantRateFactor,
|
||||
};
|
||||
|
||||
AV1Section(QWidget* parent = nullptr);
|
||||
AV1Section(int default_crf, QWidget* parent);
|
||||
AV1Section(QWidget *parent = nullptr);
|
||||
AV1Section(int default_crf, QWidget *parent);
|
||||
|
||||
virtual void AddOpts(EncodingParams* params) override;
|
||||
virtual void AddOpts(EncodingParams *params) override;
|
||||
|
||||
private:
|
||||
QStackedWidget* compression_method_stack_;
|
||||
QStackedWidget *compression_method_stack_;
|
||||
|
||||
AV1CRFSection* crf_section_;
|
||||
AV1CRFSection *crf_section_;
|
||||
|
||||
QComboBox *preset_combobox_;
|
||||
QComboBox *preset_combobox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -23,22 +23,23 @@
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
namespace olive {
|
||||
|
||||
CineformSection::CineformSection(QWidget *parent) :
|
||||
CodecSection(parent)
|
||||
namespace olive
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
CineformSection::CineformSection(QWidget *parent)
|
||||
: CodecSection(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
int row = 0;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||
int row = 0;
|
||||
|
||||
quality_combobox_ = new QComboBox();
|
||||
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||
|
||||
/* Correspond to the following indexes for FFmpeg
|
||||
quality_combobox_ = new QComboBox();
|
||||
|
||||
/* Correspond to the following indexes for FFmpeg
|
||||
*
|
||||
* -quality <int> E..V....... set quality (from 0 to 12) (default film3+)
|
||||
* film3+ 0 E..V.......
|
||||
@@ -57,34 +58,37 @@ CineformSection::CineformSection(QWidget *parent) :
|
||||
*
|
||||
*/
|
||||
|
||||
quality_combobox_->addItem(tr("Film Scan 3+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 3"));
|
||||
quality_combobox_->addItem(tr("Film Scan 2+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 2"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1.5"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1"));
|
||||
quality_combobox_->addItem(tr("High+"));
|
||||
quality_combobox_->addItem(tr("High"));
|
||||
quality_combobox_->addItem(tr("Medium+"));
|
||||
quality_combobox_->addItem(tr("Medium"));
|
||||
quality_combobox_->addItem(tr("Low+"));
|
||||
quality_combobox_->addItem(tr("Low"));
|
||||
quality_combobox_->addItem(tr("Film Scan 3+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 3"));
|
||||
quality_combobox_->addItem(tr("Film Scan 2+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 2"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1.5"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1"));
|
||||
quality_combobox_->addItem(tr("High+"));
|
||||
quality_combobox_->addItem(tr("High"));
|
||||
quality_combobox_->addItem(tr("Medium+"));
|
||||
quality_combobox_->addItem(tr("Medium"));
|
||||
quality_combobox_->addItem(tr("Low+"));
|
||||
quality_combobox_->addItem(tr("Low"));
|
||||
|
||||
// Default to "medium"
|
||||
quality_combobox_->setCurrentIndex(10);
|
||||
// Default to "medium"
|
||||
quality_combobox_->setCurrentIndex(10);
|
||||
|
||||
layout->addWidget(quality_combobox_, row, 1);
|
||||
layout->addWidget(quality_combobox_, row, 1);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
quality_combobox_->setCurrentIndex(p->video_option(QStringLiteral("quality")).toInt());
|
||||
quality_combobox_->setCurrentIndex(
|
||||
p->video_option(QStringLiteral("quality")).toInt());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,21 +25,20 @@
|
||||
|
||||
#include "codecsection.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CineformSection : public CodecSection
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class CineformSection : public CodecSection {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CineformSection(QWidget *parent = nullptr);
|
||||
CineformSection(QWidget *parent = nullptr);
|
||||
|
||||
virtual void AddOpts(EncodingParams* params) override;
|
||||
virtual void AddOpts(EncodingParams *params) override;
|
||||
|
||||
virtual void SetOpts(const EncodingParams *p) override;
|
||||
virtual void SetOpts(const EncodingParams *p) override;
|
||||
|
||||
private:
|
||||
QComboBox *quality_combobox_;
|
||||
|
||||
QComboBox *quality_combobox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,10 +20,11 @@
|
||||
|
||||
#include "codecsection.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
CodecSection::CodecSection(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
CodecSection::CodecSection(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -25,18 +25,23 @@
|
||||
|
||||
#include "codec/encoder.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CodecSection : public QWidget
|
||||
namespace olive
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class CodecSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CodecSection(QWidget* parent = nullptr);
|
||||
CodecSection(QWidget *parent = nullptr);
|
||||
|
||||
virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)}
|
||||
|
||||
virtual void SetOpts(const EncodingParams *p){Q_UNUSED(p)}
|
||||
virtual void AddOpts(EncodingParams *params)
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
}
|
||||
|
||||
virtual void SetOpts(const EncodingParams *p)
|
||||
{
|
||||
Q_UNUSED(p)
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user