change: change code style to Linux style except indent.

This commit is contained in:
Mike Solar
2025-08-03 03:09:40 +08:00
parent 65ab76edc8
commit 74f73ab3be
789 changed files with 77113 additions and 68888 deletions
+71 -103
View File
@@ -1,148 +1,116 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 11.
#
# For more information, see:
#
# Documentation/dev-tools/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
--- ---
Language: Cpp AccessModifierOffset: -4
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left AlignEscapedNewlines: Left
AlignOperands: true AlignOperands: true
AlignTrailingComments: true AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: true AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: true AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes AlwaysBreakTemplateDeclarations: false
BinPackArguments: true BinPackArguments: true
BinPackParameters: true BinPackParameters: true
BraceWrapping: BraceWrapping:
AfterClass: false AfterClass: false
AfterControlStatement: false AfterControlStatement: false
AfterEnum: false AfterEnum: false
AfterFunction: false AfterFunction: true
AfterNamespace: false AfterNamespace: true
AfterObjCDeclaration: false AfterObjCDeclaration: false
AfterStruct: false AfterStruct: false
AfterUnion: false AfterUnion: false
AfterExternBlock: false AfterExternBlock: false
BeforeCatch: false BeforeCatch: false
BeforeElse: false BeforeElse: false
IndentBraces: false IndentBraces: false
SplitEmptyFunction: true SplitEmptyFunction: true
SplitEmptyRecord: true SplitEmptyRecord: true
SplitEmptyNamespace: true SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true BreakStringLiterals: false
ColumnLimit: 120 ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:' CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
Cpp11BracedListStyle: true Cpp11BracedListStyle: false
DerivePointerAlignment: true DerivePointerAlignment: false
DisableFormat: false DisableFormat: false
ExperimentalAutoDetectBinPacking: false ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true FixNamespaceComments: false
ForEachMacros: IncludeBlocks: Preserve
- foreach IncludeCategories:
- Q_FOREACH - Regex: '.*'
- BOOST_FOREACH Priority: 1
IncludeBlocks: Preserve IncludeIsMainRegex: '(Test)?$'
IncludeCategories: IndentCaseLabels: false
- Regex: '^<ext/.*\.h>' IndentGotoLabels: false
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: None IndentPPDirectives: None
IndentWidth: 2 IndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave JavaScriptQuotes: Leave
JavaScriptWrapImports: true JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: '' MacroBlockBegin: ''
MacroBlockEnd: '' MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: None NamespaceIndentation: None
ObjCBinPackProtocolList: Never ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2 ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1 # Taken from git's rules
PenaltyBreakComment: 300 PenaltyBreakAssignment: 10
PenaltyBreakFirstLessLess: 120 PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakString: 1000 PenaltyBreakComment: 10
PenaltyBreakTemplateDeclaration: 10 PenaltyBreakFirstLessLess: 0
PenaltyExcessCharacter: 1000000 PenaltyBreakString: 10
PenaltyReturnTypeOnItsOwnLine: 200 PenaltyExcessCharacter: 100
PointerAlignment: Left PenaltyReturnTypeOnItsOwnLine: 60
RawStringFormats:
- Language: Cpp PointerAlignment: Right
Delimiters: ReflowComments: false
- cc SortIncludes: false
- CC SortUsingDeclarations: false
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2 SpacesBeforeTrailingComments: 1
SpacesInAngles: false SpacesInAngles: false
SpacesInContainerLiterals: true SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false SpacesInCStyleCastParentheses: false
SpacesInParentheses: false SpacesInParentheses: false
SpacesInSquareBrackets: false SpacesInSquareBrackets: false
Standard: Auto TabWidth: 4
TabWidth: 8 UseTab: Always
UseTab: Never
... ...
+147
View File
@@ -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
View File
@@ -28,290 +28,319 @@
#include "config/config.h" #include "config/config.h"
namespace olive { namespace olive
{
AudioManager* AudioManager::instance_ = nullptr; AudioManager *AudioManager::instance_ = nullptr;
void AudioManager::CreateInstance() void AudioManager::CreateInstance()
{ {
if (instance_ == nullptr) { if (instance_ == nullptr) {
instance_ = new AudioManager(); instance_ = new AudioManager();
} }
} }
void AudioManager::DestroyInstance() void AudioManager::DestroyInstance()
{ {
delete instance_; delete instance_;
instance_ = nullptr; instance_ = nullptr;
} }
AudioManager *AudioManager::instance() AudioManager *AudioManager::instance()
{ {
return instance_; return instance_;
} }
void AudioManager::SetOutputNotifyInterval(int n) void AudioManager::SetOutputNotifyInterval(int n)
{ {
output_buffer_->set_notify_interval(n); output_buffer_->set_notify_interval(n);
} }
int OutputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) int OutputCallback(const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{ {
PreviewAudioDevice *device = static_cast<PreviewAudioDevice*>(userData); PreviewAudioDevice *device = static_cast<PreviewAudioDevice *>(userData);
qint64 max_read = frameCount * device->bytes_per_frame(); qint64 max_read = frameCount * device->bytes_per_frame();
qint64 read_count = device->read(reinterpret_cast<char*>(output), max_read); qint64 read_count =
if (read_count < max_read) { device->read(reinterpret_cast<char *>(output), max_read);
memset(reinterpret_cast<uint8_t*>(output) + read_count, 0, max_read - read_count); if (read_count < max_read) {
} memset(reinterpret_cast<uint8_t *>(output) + read_count, 0,
max_read - read_count);
}
return paContinue; return paContinue;
} }
int InputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) int InputCallback(const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{ {
FFmpegEncoder *f = static_cast<FFmpegEncoder*>(userData); FFmpegEncoder *f = static_cast<FFmpegEncoder *>(userData);
AudioParams our_params = f->params().audio_params(); AudioParams our_params = f->params().audio_params();
our_params.set_format(f->params().audio_params().format().to_packed_equivalent()); our_params.set_format(
f->params().audio_params().format().to_packed_equivalent());
f->WriteAudioData(our_params, reinterpret_cast<const uint8_t**>(&input), frameCount); f->WriteAudioData(our_params, reinterpret_cast<const uint8_t **>(&input),
frameCount);
return paContinue; return paContinue;
} }
bool AudioManager::PushToOutput(const AudioParams &params, const QByteArray &samples, QString *error) bool AudioManager::PushToOutput(const AudioParams &params,
const QByteArray &samples, QString *error)
{ {
if (output_device_ == paNoDevice) { if (output_device_ == paNoDevice) {
if (error) *error = tr("No output device is set"); if (error)
return false; *error = tr("No output device is set");
} return false;
}
if (output_params_ != params || output_stream_ == nullptr) { if (output_params_ != params || output_stream_ == nullptr) {
output_params_ = params; output_params_ = params;
CloseOutputStream(); CloseOutputStream();
PaStreamParameters p = GetPortAudioParams(params, output_device_); PaStreamParameters p = GetPortAudioParams(params, output_device_);
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_); PaError r = Pa_OpenStream(&output_stream_, nullptr, &p,
if (r != paNoError) { output_params_.sample_rate(),
// Unhandled error paFramesPerBufferUnspecified, paNoFlag,
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r); OutputCallback, output_buffer_);
if (error) *error = Pa_GetErrorText(r); if (r != paNoError) {
return false; // Unhandled error
} //qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
if (error)
*error = Pa_GetErrorText(r);
return false;
}
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1)); output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
} }
output_buffer_->write(samples); output_buffer_->write(samples);
if (!Pa_IsStreamActive(output_stream_)) { if (!Pa_IsStreamActive(output_stream_)) {
Pa_StartStream(output_stream_); Pa_StartStream(output_stream_);
} }
return true; return true;
} }
void AudioManager::ClearBufferedOutput() void AudioManager::ClearBufferedOutput()
{ {
output_buffer_->clear(); output_buffer_->clear();
} }
PaSampleFormat AudioManager::GetPortAudioSampleFormat(SampleFormat fmt) PaSampleFormat AudioManager::GetPortAudioSampleFormat(SampleFormat fmt)
{ {
switch (fmt) { switch (fmt) {
case SampleFormat::U8: case SampleFormat::U8:
case SampleFormat::U8P: case SampleFormat::U8P:
return paUInt8; return paUInt8;
case SampleFormat::S16: case SampleFormat::S16:
case SampleFormat::S16P: case SampleFormat::S16P:
return paInt16; return paInt16;
case SampleFormat::S32: case SampleFormat::S32:
case SampleFormat::S32P: case SampleFormat::S32P:
return paInt32; return paInt32;
case SampleFormat::F32: case SampleFormat::F32:
case SampleFormat::F32P: case SampleFormat::F32P:
return paFloat32; return paFloat32;
case SampleFormat::S64: case SampleFormat::S64:
case SampleFormat::S64P: case SampleFormat::S64P:
case SampleFormat::F64: case SampleFormat::F64:
case SampleFormat::F64P: case SampleFormat::F64P:
case SampleFormat::INVALID: case SampleFormat::INVALID:
case SampleFormat::COUNT: case SampleFormat::COUNT:
break; break;
} }
return 0; return 0;
} }
void AudioManager::CloseOutputStream() void AudioManager::CloseOutputStream()
{ {
if (output_stream_) { if (output_stream_) {
if (Pa_IsStreamActive(output_stream_)) { if (Pa_IsStreamActive(output_stream_)) {
StopOutput(); StopOutput();
} }
Pa_CloseStream(output_stream_); Pa_CloseStream(output_stream_);
output_stream_ = nullptr; output_stream_ = nullptr;
} }
} }
void AudioManager::StopOutput() void AudioManager::StopOutput()
{ {
// Abort the stream so playback stops immediately // Abort the stream so playback stops immediately
if (output_stream_) { if (output_stream_) {
Pa_AbortStream(output_stream_); Pa_AbortStream(output_stream_);
ClearBufferedOutput(); ClearBufferedOutput();
} }
} }
void AudioManager::SetOutputDevice(PaDeviceIndex device) void AudioManager::SetOutputDevice(PaDeviceIndex device)
{ {
if (device == paNoDevice) { if (device == paNoDevice) {
qInfo() << "No output device found"; qInfo() << "No output device found";
} else { } else {
qInfo() << "Setting output audio device to" << Pa_GetDeviceInfo(device)->name; qInfo() << "Setting output audio device to"
} << Pa_GetDeviceInfo(device)->name;
}
output_device_ = device; output_device_ = device;
CloseOutputStream(); CloseOutputStream();
} }
void AudioManager::SetInputDevice(PaDeviceIndex device) void AudioManager::SetInputDevice(PaDeviceIndex device)
{ {
if (device == paNoDevice) { if (device == paNoDevice) {
qInfo() << "No input device found"; qInfo() << "No input device found";
} else { } else {
qInfo() << "Setting input audio device to" << Pa_GetDeviceInfo(device)->name; qInfo() << "Setting input audio device to"
} << Pa_GetDeviceInfo(device)->name;
}
input_device_ = device; input_device_ = device;
} }
void AudioManager::HardReset() void AudioManager::HardReset()
{ {
CloseOutputStream(); CloseOutputStream();
Pa_Terminate(); Pa_Terminate();
Pa_Initialize(); Pa_Initialize();
} }
bool AudioManager::StartRecording(const EncodingParams &params, QString *error_str) bool AudioManager::StartRecording(const EncodingParams &params,
QString *error_str)
{ {
if (input_device_ == paNoDevice) { if (input_device_ == paNoDevice) {
return false; return false;
} }
input_encoder_ = new FFmpegEncoder(params); input_encoder_ = new FFmpegEncoder(params);
if (!input_encoder_->Open()) { if (!input_encoder_->Open()) {
qCritical() << "Failed to open encoder for recording"; qCritical() << "Failed to open encoder for recording";
return false; return false;
} }
PaStreamParameters p = GetPortAudioParams(params.audio_params(), input_device_); PaStreamParameters p =
GetPortAudioParams(params.audio_params(), input_device_);
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_); PaError r = Pa_OpenStream(&input_stream_, &p, nullptr,
if (r == paNoError) { params.audio_params().sample_rate(),
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_); paFramesPerBufferUnspecified, paNoFlag,
r = Pa_StartStream(input_stream_); InputCallback, input_encoder_);
if (r == paNoError) { if (r == paNoError) {
return true; //const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
} r = Pa_StartStream(input_stream_);
} if (r == paNoError) {
return true;
}
}
if (error_str) { if (error_str) {
*error_str = Pa_GetErrorText(r); *error_str = Pa_GetErrorText(r);
} }
StopRecording(); StopRecording();
return false; return false;
} }
void AudioManager::StopRecording() void AudioManager::StopRecording()
{ {
if (input_stream_) { if (input_stream_) {
if (Pa_IsStreamActive(input_stream_)) { if (Pa_IsStreamActive(input_stream_)) {
Pa_StopStream(input_stream_); Pa_StopStream(input_stream_);
} }
Pa_CloseStream(input_stream_); Pa_CloseStream(input_stream_);
input_stream_ = nullptr; input_stream_ = nullptr;
} }
if (input_encoder_) { if (input_encoder_) {
input_encoder_->Close(); input_encoder_->Close();
delete input_encoder_; delete input_encoder_;
input_encoder_ = nullptr; input_encoder_ = nullptr;
} }
} }
PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device) PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device)
{ {
QString entry = is_output_device ? QStringLiteral("AudioOutput") : QStringLiteral("AudioInput"); QString entry = is_output_device ? QStringLiteral("AudioOutput") :
QStringLiteral("AudioInput");
return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(), is_output_device); return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(),
is_output_device);
} }
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s, bool is_output_device) PaDeviceIndex AudioManager::FindDeviceByName(const QString &s,
bool is_output_device)
{ {
if (!s.isEmpty()) { if (!s.isEmpty()) {
for (PaDeviceIndex i=0, end=Pa_GetDeviceCount(); i<end; i++) { for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) {
const PaDeviceInfo *device = Pa_GetDeviceInfo(i); const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
if (((is_output_device && device->maxOutputChannels) || (!is_output_device && device->maxInputChannels)) if (((is_output_device && device->maxOutputChannels) ||
&& !s.compare(device->name)) { (!is_output_device && device->maxInputChannels)) &&
return i; !s.compare(device->name)) {
} return i;
} }
} }
}
return is_output_device ? Pa_GetDefaultOutputDevice() : Pa_GetDefaultInputDevice(); return is_output_device ? Pa_GetDefaultOutputDevice() :
Pa_GetDefaultInputDevice();
} }
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams &params, PaDeviceIndex device) PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams &params,
PaDeviceIndex device)
{ {
PaStreamParameters p; PaStreamParameters p;
p.channelCount = params.channel_count(); p.channelCount = params.channel_count();
p.device = device; p.device = device;
p.hostApiSpecificStreamInfo = nullptr; p.hostApiSpecificStreamInfo = nullptr;
p.sampleFormat = GetPortAudioSampleFormat(params.format()); p.sampleFormat = GetPortAudioSampleFormat(params.format());
p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency; p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
return p; return p;
} }
AudioManager::AudioManager() : AudioManager::AudioManager()
output_stream_(nullptr), : output_stream_(nullptr)
input_stream_(nullptr), , input_stream_(nullptr)
input_encoder_(nullptr) , input_encoder_(nullptr)
{ {
#ifdef PA_HAS_JACK #ifdef PA_HAS_JACK
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not // PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
// a QString converted to UTF-8) // a QString converted to UTF-8)
PaJack_SetClientName("Olive"); PaJack_SetClientName("Olive");
#endif #endif
Pa_Initialize(); Pa_Initialize();
// Get device from config // Get device from config
PaDeviceIndex output_device = FindConfigDeviceByName(true); PaDeviceIndex output_device = FindConfigDeviceByName(true);
PaDeviceIndex input_device = FindConfigDeviceByName(false); PaDeviceIndex input_device = FindConfigDeviceByName(false);
SetOutputDevice(output_device); SetOutputDevice(output_device);
SetInputDevice(input_device); SetInputDevice(input_device);
output_buffer_ = new PreviewAudioDevice(this); output_buffer_ = new PreviewAudioDevice(this);
output_buffer_->open(PreviewAudioDevice::ReadWrite); output_buffer_->open(PreviewAudioDevice::ReadWrite);
connect(output_buffer_, &PreviewAudioDevice::Notify, this, &AudioManager::OutputNotify); connect(output_buffer_, &PreviewAudioDevice::Notify, this,
&AudioManager::OutputNotify);
} }
AudioManager::~AudioManager() AudioManager::~AudioManager()
{ {
CloseOutputStream(); CloseOutputStream();
Pa_Terminate(); Pa_Terminate();
} }
} }
+45 -42
View File
@@ -33,7 +33,8 @@
#include "render/audioplaybackcache.h" #include "render/audioplaybackcache.h"
#include "render/previewaudiodevice.h" #include "render/previewaudiodevice.h"
namespace olive { namespace olive
{
/** /**
* @brief Audio input and output management class * @brief Audio input and output management class
@@ -41,74 +42,76 @@ namespace olive {
* Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to * Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to
* the rest of the system. * the rest of the system.
*/ */
class AudioManager : public QObject class AudioManager : public QObject {
{ Q_OBJECT
Q_OBJECT
public: public:
static void CreateInstance(); static void CreateInstance();
static void DestroyInstance(); static void DestroyInstance();
static AudioManager* instance(); static AudioManager *instance();
void SetOutputNotifyInterval(int n); void SetOutputNotifyInterval(int n);
bool PushToOutput(const AudioParams &params, const QByteArray& samples, QString *error = nullptr); bool PushToOutput(const AudioParams &params, const QByteArray &samples,
QString *error = nullptr);
void ClearBufferedOutput(); void ClearBufferedOutput();
void StopOutput(); void StopOutput();
PaDeviceIndex GetOutputDevice() const PaDeviceIndex GetOutputDevice() const
{ {
return output_device_; return output_device_;
} }
PaDeviceIndex GetInputDevice() const PaDeviceIndex GetInputDevice() const
{ {
return input_device_; return input_device_;
} }
void SetOutputDevice(PaDeviceIndex device); void SetOutputDevice(PaDeviceIndex device);
void SetInputDevice(PaDeviceIndex device); void SetInputDevice(PaDeviceIndex device);
void HardReset(); void HardReset();
bool StartRecording(const EncodingParams &params, QString *error_str = nullptr); bool StartRecording(const EncodingParams &params,
QString *error_str = nullptr);
void StopRecording(); void StopRecording();
static PaDeviceIndex FindConfigDeviceByName(bool is_output_device); static PaDeviceIndex FindConfigDeviceByName(bool is_output_device);
static PaDeviceIndex FindDeviceByName(const QString &s, bool is_output_device); static PaDeviceIndex FindDeviceByName(const QString &s,
bool is_output_device);
static PaStreamParameters GetPortAudioParams(const AudioParams &p, PaDeviceIndex device); static PaStreamParameters GetPortAudioParams(const AudioParams &p,
PaDeviceIndex device);
signals: signals:
void OutputNotify(); void OutputNotify();
void OutputParamsChanged(); void OutputParamsChanged();
private: private:
AudioManager(); AudioManager();
virtual ~AudioManager() override; virtual ~AudioManager() override;
static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt); static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt);
void CloseOutputStream(); void CloseOutputStream();
static AudioManager* instance_; static AudioManager *instance_;
PaDeviceIndex output_device_; PaDeviceIndex output_device_;
PaStream *output_stream_; PaStream *output_stream_;
AudioParams output_params_; AudioParams output_params_;
PreviewAudioDevice *output_buffer_; PreviewAudioDevice *output_buffer_;
PaDeviceIndex input_device_; PaDeviceIndex input_device_;
PaStream *input_stream_; PaStream *input_stream_;
FFmpegEncoder *input_encoder_;
FFmpegEncoder *input_encoder_;
}; };
} }
+226 -207
View File
@@ -29,279 +29,298 @@ extern "C" {
#include "common/ffmpegutils.h" #include "common/ffmpegutils.h"
namespace olive { namespace olive
{
AudioProcessor::AudioProcessor() AudioProcessor::AudioProcessor()
{ {
filter_graph_ = nullptr; filter_graph_ = nullptr;
in_frame_ = nullptr; in_frame_ = nullptr;
out_frame_ = nullptr; out_frame_ = nullptr;
} }
AudioProcessor::~AudioProcessor() AudioProcessor::~AudioProcessor()
{ {
Close(); Close();
} }
bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double tempo) bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to,
double tempo)
{ {
if (filter_graph_) { if (filter_graph_) {
qWarning() << "Tried to open a processor that was already open"; qWarning() << "Tried to open a processor that was already open";
return false; return false;
} }
filter_graph_ = avfilter_graph_alloc(); filter_graph_ = avfilter_graph_alloc();
if (!filter_graph_) { if (!filter_graph_) {
qCritical() << "Failed to allocate filter graph"; qCritical() << "Failed to allocate filter graph";
return false; return false;
} }
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format()); from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format()); to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
// Set up audio buffer args // Set up audio buffer args
char filter_args[200]; char filter_args[200];
snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64, snprintf(
1, filter_args, 200,
from.sample_rate(), "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
from.sample_rate(), 1, from.sample_rate(), from.sample_rate(), from_fmt_,
from_fmt_, from.channel_layout().u.mask);
from.channel_layout().u.mask);
int r; int r;
// Create buffersrc (input) // Create buffersrc (input)
r = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("abuffer"), "in", filter_args, nullptr, filter_graph_); r = avfilter_graph_create_filter(&buffersrc_ctx_,
if (r < 0) { avfilter_get_by_name("abuffer"), "in",
qCritical() << "Failed to create buffersrc:" << r; filter_args, nullptr, filter_graph_);
Close(); if (r < 0) {
return false; qCritical() << "Failed to create buffersrc:" << r;
} Close();
return false;
}
// Store "previous" filter for linking // Store "previous" filter for linking
AVFilterContext *previous_filter = buffersrc_ctx_; AVFilterContext *previous_filter = buffersrc_ctx_;
// Create tempo // Create tempo
bool create_tempo; bool create_tempo;
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) { if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside // Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
// those boundaries, we need to daisychain more than one together. // those boundaries, we need to daisychain more than one together.
double base = (tempo > 1.0) ? 2.0 : 0.5; double base = (tempo > 1.0) ? 2.0 : 0.5;
double speed_log = log(tempo) / log(base); double speed_log = log(tempo) / log(base);
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain // This is the number of how many 0.5 or 2.0 tempos we need to daisychain
int whole = std::floor(speed_log); int whole = std::floor(speed_log);
// Set speed_log to the remainder // Set speed_log to the remainder
speed_log -= whole; speed_log -= whole;
for (int i=0;i<=whole;i++) { for (int i = 0; i <= whole; i++) {
double filter_tempo = (i == whole) ? std::pow(base, speed_log) : base; double filter_tempo = (i == whole) ? std::pow(base, speed_log) :
/* base;
/*
if (qFuzzyCompare(filter_tempo, 1.0)) { if (qFuzzyCompare(filter_tempo, 1.0)) {
// This filter would do nothing // This filter would do nothing
continue; continue;
}*/ }*/
previous_filter = CreateTempoFilter(filter_graph_, previous_filter =
previous_filter, CreateTempoFilter(filter_graph_, previous_filter, filter_tempo);
filter_tempo);
if (!previous_filter) { if (!previous_filter) {
qCritical() << "Failed to create audio tempo filter"; qCritical() << "Failed to create audio tempo filter";
Close(); Close();
return false; return false;
} }
} }
} }
// Create conversion filter // Create conversion filter
auto ch1=from.channel_layout(); auto ch1 = from.channel_layout();
auto ch2=to.channel_layout(); auto ch2 = to.channel_layout();
if (from.sample_rate() != to.sample_rate() || av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format() if (from.sample_rate() != to.sample_rate() ||
|| (to.format().is_planar() && create_tempo)) { // Tempo processor automatically converts to packed, av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format() ||
// so if the desired output is planar, it'll need (to.format().is_planar() &&
// to be converted create_tempo)) { // Tempo processor automatically converts to packed,
snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64, // so if the desired output is planar, it'll need
av_get_sample_fmt_name(to_fmt_), // to be converted
to.sample_rate(), snprintf(filter_args, 200,
to.channel_layout().u.mask); "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
av_get_sample_fmt_name(to_fmt_), to.sample_rate(),
to.channel_layout().u.mask);
AVFilterContext *c; AVFilterContext *c;
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"), "fmt", filter_args, nullptr, filter_graph_); r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"),
if (r < 0) { "fmt", filter_args, nullptr,
qCritical() << "Failed to create format conversion filter:" << r << filter_args; filter_graph_);
Close(); if (r < 0) {
return false; qCritical() << "Failed to create format conversion filter:" << r
} << filter_args;
Close();
return false;
}
r = avfilter_link(previous_filter, 0, c, 0); r = avfilter_link(previous_filter, 0, c, 0);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to link filters:" << r; qCritical() << "Failed to link filters:" << r;
Close(); Close();
return false; return false;
} }
previous_filter = c; previous_filter = c;
} }
// Create buffersink (output) // Create buffersink (output)
r = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph_); r = avfilter_graph_create_filter(&buffersink_ctx_,
if (r < 0) { avfilter_get_by_name("abuffersink"), "out",
qCritical() << "Failed to create buffersink:" << r; nullptr, nullptr, filter_graph_);
Close(); if (r < 0) {
return false; qCritical() << "Failed to create buffersink:" << r;
} Close();
return false;
}
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0); r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to link filters:" << r; qCritical() << "Failed to link filters:" << r;
Close(); Close();
return false; return false;
} }
char *dump = avfilter_graph_dump(filter_graph_, nullptr); char *dump = avfilter_graph_dump(filter_graph_, nullptr);
qDebug() << dump; qDebug() << dump;
av_free(dump); av_free(dump);
r = avfilter_graph_config(filter_graph_, nullptr); r = avfilter_graph_config(filter_graph_, nullptr);
if (r < 0) { if (r < 0) {
qCritical() << "Failed to configure graph:" << r; qCritical() << "Failed to configure graph:" << r;
Close(); Close();
return false; return false;
} }
in_frame_ = av_frame_alloc(); in_frame_ = av_frame_alloc();
if (in_frame_) { if (in_frame_) {
in_frame_->sample_rate = from.sample_rate(); in_frame_->sample_rate = from.sample_rate();
in_frame_->format = from_fmt_; in_frame_->format = from_fmt_;
in_frame_->ch_layout = from.channel_layout(); in_frame_->ch_layout = from.channel_layout();
in_frame_->pts = 0; in_frame_->pts = 0;
} else { } else {
qCritical() << "Failed to allocate input frame"; qCritical() << "Failed to allocate input frame";
Close(); Close();
return false; return false;
} }
out_frame_ = av_frame_alloc(); out_frame_ = av_frame_alloc();
if (!out_frame_) { if (!out_frame_) {
qCritical() << "Failed to allocate output frame"; qCritical() << "Failed to allocate output frame";
Close(); Close();
return false; return false;
} }
from_ = from; from_ = from;
to_ = to; to_ = to;
return true; return true;
} }
void AudioProcessor::Close() void AudioProcessor::Close()
{ {
if (filter_graph_) { if (filter_graph_) {
avfilter_graph_free(&filter_graph_); avfilter_graph_free(&filter_graph_);
filter_graph_ = nullptr; filter_graph_ = nullptr;
buffersrc_ctx_ = nullptr; buffersrc_ctx_ = nullptr;
buffersink_ctx_ = nullptr; buffersink_ctx_ = nullptr;
} }
if (in_frame_) { if (in_frame_) {
av_frame_free(&in_frame_); av_frame_free(&in_frame_);
in_frame_ = nullptr; in_frame_ = nullptr;
} }
if (out_frame_) { if (out_frame_) {
av_frame_free(&out_frame_); av_frame_free(&out_frame_);
out_frame_ = nullptr; out_frame_ = nullptr;
} }
} }
int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output) int AudioProcessor::Convert(float **in, int nb_in_samples,
AudioProcessor::Buffer *output)
{ {
if (!IsOpen()) { if (!IsOpen()) {
qCritical() << "Tried to convert on closed processor"; qCritical() << "Tried to convert on closed processor";
return -1; return -1;
} }
int r = 0; int r = 0;
if (in && nb_in_samples) { if (in && nb_in_samples) {
// Set frame parameters // Set frame parameters
in_frame_->nb_samples = nb_in_samples; in_frame_->nb_samples = nb_in_samples;
for (int i=0; i<from_.channel_count(); i++) { for (int i = 0; i < from_.channel_count(); i++) {
in_frame_->data[i] = reinterpret_cast<uint8_t*>(in[i]); in_frame_->data[i] = reinterpret_cast<uint8_t *>(in[i]);
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples); in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
} }
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_, AV_BUFFERSRC_FLAG_KEEP_REF); r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_,
if (r < 0) { AV_BUFFERSRC_FLAG_KEEP_REF);
qCritical() << "Failed to add frame to buffersrc:" << r; if (r < 0) {
return r; qCritical() << "Failed to add frame to buffersrc:" << r;
} return r;
} }
}
if (output) { if (output) {
int nb_channels = to_.channel_count(); int nb_channels = to_.channel_count();
if (to_.format().is_packed()) { if (to_.format().is_packed()) {
nb_channels = 1; nb_channels = 1;
} }
AudioProcessor::Buffer &result = *output; AudioProcessor::Buffer &result = *output;
result.resize(nb_channels); result.resize(nb_channels);
int byte_offset = 0; int byte_offset = 0;
while (true) { while (true) {
av_frame_unref(out_frame_); av_frame_unref(out_frame_);
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_); r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
if (r < 0) { if (r < 0) {
if (r == AVERROR(EAGAIN)) { if (r == AVERROR(EAGAIN)) {
r = 0; r = 0;
} else { } else {
// Handle unexpected error // Handle unexpected error
qCritical() << "Failed to pull from buffersink:" << r; qCritical() << "Failed to pull from buffersink:" << r;
} }
break; break;
} }
int nb_bytes = out_frame_->nb_samples * to_.bytes_per_sample_per_channel(); int nb_bytes =
if (to_.format().is_packed()) { out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
nb_bytes *= to_.channel_count(); if (to_.format().is_packed()) {
} nb_bytes *= to_.channel_count();
}
for (int i=0; i<nb_channels; i++) { for (int i = 0; i < nb_channels; i++) {
result[i].resize(byte_offset + nb_bytes); result[i].resize(byte_offset + nb_bytes);
memcpy(result[i].data() + byte_offset, out_frame_->data[i], nb_bytes); memcpy(result[i].data() + byte_offset, out_frame_->data[i],
} nb_bytes);
byte_offset += nb_bytes; }
} byte_offset += nb_bytes;
av_frame_unref(out_frame_); }
} av_frame_unref(out_frame_);
}
return r; return r;
} }
void AudioProcessor::Flush() void AudioProcessor::Flush()
{ {
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF); int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr,
if (r < 0) { AV_BUFFERSRC_FLAG_KEEP_REF);
qCritical() << "Failed to flush:" << r; if (r < 0) {
} qCritical() << "Failed to flush:" << r;
}
} }
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilterContext* link, const double &tempo) AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph *graph,
AVFilterContext *link,
const double &tempo)
{ {
// Set up tempo param, which is taken as a C string // Set up tempo param, which is taken as a C string
char speed_param[20]; char speed_param[20];
snprintf(speed_param, 20, "%f", tempo); snprintf(speed_param, 20, "%f", tempo);
AVFilterContext* tempo_ctx = nullptr; AVFilterContext *tempo_ctx = nullptr;
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0 if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"),
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) { "atempo", speed_param, nullptr,
return tempo_ctx; graph) >= 0 &&
} avfilter_link(link, 0, tempo_ctx, 0) == 0) {
return tempo_ctx;
}
return nullptr; return nullptr;
} }
} }
+36 -25
View File
@@ -31,52 +31,63 @@ extern "C" {
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
using namespace core; using namespace core;
class AudioProcessor class AudioProcessor {
{
public: public:
AudioProcessor(); AudioProcessor();
~AudioProcessor(); ~AudioProcessor();
DISABLE_COPY_MOVE(AudioProcessor) DISABLE_COPY_MOVE(AudioProcessor)
bool Open(const AudioParams &from, const AudioParams &to, double tempo = 1.0); bool Open(const AudioParams &from, const AudioParams &to,
double tempo = 1.0);
void Close(); void Close();
bool IsOpen() const { return filter_graph_; } bool IsOpen() const
{
return filter_graph_;
}
using Buffer = QVector<QByteArray>; using Buffer = QVector<QByteArray>;
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output); int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
void Flush(); void Flush();
const AudioParams &from() const { return from_; } const AudioParams &from() const
const AudioParams &to() const { return to_; } {
return from_;
}
const AudioParams &to() const
{
return to_;
}
private: private:
static AVFilterContext* CreateTempoFilter(AVFilterGraph *graph, AVFilterContext *link, const double& tempo); static AVFilterContext *CreateTempoFilter(AVFilterGraph *graph,
AVFilterContext *link,
const double &tempo);
AVFilterGraph* filter_graph_; AVFilterGraph *filter_graph_;
AVFilterContext* buffersrc_ctx_; AVFilterContext *buffersrc_ctx_;
AVFilterContext* buffersink_ctx_; AVFilterContext *buffersink_ctx_;
AudioParams from_; AudioParams from_;
AVSampleFormat from_fmt_; AVSampleFormat from_fmt_;
AudioParams to_; AudioParams to_;
AVSampleFormat to_fmt_; AVSampleFormat to_fmt_;
AVFrame *in_frame_; AVFrame *in_frame_;
AVFrame *out_frame_;
AVFrame *out_frame_;
}; };
} }
+371 -315
View File
@@ -25,480 +25,536 @@
#include "config/config.h" #include "config/config.h"
namespace olive { namespace olive
{
const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8); const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8);
const rational AudioVisualWaveform::kMaximumSampleRate = 1024; const rational AudioVisualWaveform::kMaximumSampleRate = 1024;
AudioVisualWaveform::AudioVisualWaveform() : AudioVisualWaveform::AudioVisualWaveform()
channels_(0) : channels_(0)
{ {
for (rational i=kMinimumSampleRate; i<=kMaximumSampleRate; i*=2) { for (rational i = kMinimumSampleRate; i <= kMaximumSampleRate; i *= 2) {
mipmapped_data_.insert({i, Sample()}); mipmapped_data_.insert({ i, Sample() });
} }
} }
void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational &start, double target_rate, Sample& data, size_t &start_index, size_t &samples_length) void AudioVisualWaveform::OverwriteSamplesFromBuffer(
const SampleBuffer &samples, int sample_rate, const rational &start,
double target_rate, Sample &data, size_t &start_index,
size_t &samples_length)
{ {
start_index = time_to_samples(start, target_rate); start_index = time_to_samples(start, target_rate);
samples_length = time_to_samples(static_cast<double>(samples.sample_count()) / static_cast<double>(sample_rate), target_rate); samples_length =
time_to_samples(static_cast<double>(samples.sample_count()) /
static_cast<double>(sample_rate),
target_rate);
size_t end_index = start_index + samples_length; size_t end_index = start_index + samples_length;
if (data.size() < end_index) { if (data.size() < end_index) {
data.resize(end_index); data.resize(end_index);
} }
double chunk_size = double(sample_rate) / double(target_rate); double chunk_size = double(sample_rate) / double(target_rate);
for (size_t i=0; i<samples_length; i+=channels_) { for (size_t i = 0; i < samples_length; i += channels_) {
size_t src_start = qRound((double(i) * chunk_size)) / channels_; size_t src_start = qRound((double(i) * chunk_size)) / channels_;
size_t src_end = qMin(size_t(qRound64((double(i + channels_) * chunk_size))) / channels_, samples.sample_count()); size_t src_end = qMin(
size_t(qRound64((double(i + channels_) * chunk_size))) / channels_,
samples.sample_count());
Sample summary = SumSamples(samples, Sample summary = SumSamples(samples, src_start, src_end - src_start);
src_start,
src_end - src_start);
memcpy(&data.data()[i + start_index], memcpy(&data.data()[i + start_index], summary.data(),
summary.data(), summary.size() * sizeof(SamplePerChannel));
summary.size() * sizeof(SamplePerChannel)); }
}
} }
void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::Sample &input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational &start, double output_rate, AudioVisualWaveform::Sample &output_data) void AudioVisualWaveform::OverwriteSamplesFromMipmap(
const AudioVisualWaveform::Sample &input, double input_sample_rate,
size_t &input_start, size_t &input_length, const rational &start,
double output_rate, AudioVisualWaveform::Sample &output_data)
{ {
size_t start_index = time_to_samples(start, output_rate); size_t start_index = time_to_samples(start, output_rate);
size_t samples_length = time_to_samples(static_cast<double>(input_length / channels_) / input_sample_rate, output_rate); size_t samples_length = time_to_samples(
static_cast<double>(input_length / channels_) / input_sample_rate,
output_rate);
size_t end_index = start_index + samples_length; size_t end_index = start_index + samples_length;
if (output_data.size() < end_index) { if (output_data.size() < end_index) {
output_data.resize(end_index); output_data.resize(end_index);
} }
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here // We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
size_t chunk_size = input_sample_rate / output_rate; size_t chunk_size = input_sample_rate / output_rate;
for (size_t i=0; i<samples_length; i+=channels_) { for (size_t i = 0; i < samples_length; i += channels_) {
Sample summary = ReSumSamples(&input.data()[input_start + (i*chunk_size)], chunk_size * channels_, channels_); Sample summary =
ReSumSamples(&input.data()[input_start + (i * chunk_size)],
chunk_size * channels_, channels_);
memcpy(&output_data.data()[i + start_index], memcpy(&output_data.data()[i + start_index], summary.data(),
summary.data(), summary.size() * sizeof(SamplePerChannel));
summary.size() * sizeof(SamplePerChannel)); }
}
input_start = start_index; input_start = start_index;
input_length = samples_length; input_length = samples_length;
} }
void AudioVisualWaveform::ValidateVirtualStart(const rational &new_start) void AudioVisualWaveform::ValidateVirtualStart(const rational &new_start)
{ {
if (length_ == 0) { if (length_ == 0) {
virtual_start_ = new_start; virtual_start_ = new_start;
} else if (virtual_start_ > new_start) { } else if (virtual_start_ > new_start) {
TrimIn(new_start - virtual_start_); TrimIn(new_start - virtual_start_);
} }
} }
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational &start) void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples,
int sample_rate,
const rational &start)
{ {
if (!channels_) { if (!channels_) {
qWarning() << "Failed to write samples - channel count is zero"; qWarning() << "Failed to write samples - channel count is zero";
return; return;
} }
ValidateVirtualStart(start); ValidateVirtualStart(start);
// Process the largest mipmap directly for the samples // Process the largest mipmap directly for the samples
auto current_mipmap = mipmapped_data_.rbegin(); auto current_mipmap = mipmapped_data_.rbegin();
size_t input_start, input_length; size_t input_start, input_length;
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_, current_mipmap->first.toDouble(), current_mipmap->second, input_start, input_length); OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_,
current_mipmap->first.toDouble(),
current_mipmap->second, input_start,
input_length);
while (true) { while (true) {
// For each smaller mipmap, we just process from the mipmap before it, making each one // For each smaller mipmap, we just process from the mipmap before it, making each one
// exponentially faster to create // exponentially faster to create
auto previous_mipmap = current_mipmap; auto previous_mipmap = current_mipmap;
current_mipmap++; current_mipmap++;
if (current_mipmap == mipmapped_data_.rend()) { if (current_mipmap == mipmapped_data_.rend()) {
break; break;
} }
OverwriteSamplesFromMipmap(previous_mipmap->second, previous_mipmap->first.toDouble(), OverwriteSamplesFromMipmap(
input_start, input_length, start - virtual_start_, current_mipmap->first.toDouble(), previous_mipmap->second, previous_mipmap->first.toDouble(),
current_mipmap->second); input_start, input_length, start - virtual_start_,
} current_mipmap->first.toDouble(), current_mipmap->second);
}
rational sample_length(samples.sample_count(), sample_rate); rational sample_length(samples.sample_count(), sample_rate);
length_ = qMax(length_, start + sample_length); length_ = qMax(length_, start + sample_length);
} }
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const rational &dest, const rational& offset, const rational& length) void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums,
const rational &dest,
const rational &offset,
const rational &length)
{ {
ValidateVirtualStart(dest); ValidateVirtualStart(dest);
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
Sample& our_arr = it->second; Sample &our_arr = it->second;
const Sample& their_arr = sums.mipmapped_data_.at(rate); const Sample &their_arr = sums.mipmapped_data_.at(rate);
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
// Get our destination sample // Get our destination sample
size_t our_start_index = time_to_samples(dest - virtual_start_, rate_dbl); size_t our_start_index =
time_to_samples(dest - virtual_start_, rate_dbl);
// Get our source sample // Get our source sample
size_t their_start_index = time_to_samples(offset, rate_dbl); size_t their_start_index = time_to_samples(offset, rate_dbl);
if (their_start_index >= their_arr.size()) { if (their_start_index >= their_arr.size()) {
continue; continue;
} }
// Determine how much we're copying // Determine how much we're copying
size_t copy_len = their_arr.size() - their_start_index; size_t copy_len = their_arr.size() - their_start_index;
if (!length.isNull()) { if (!length.isNull()) {
copy_len = qMin(copy_len, time_to_samples(length, rate_dbl)); copy_len = qMin(copy_len, time_to_samples(length, rate_dbl));
if (copy_len == 0) { if (copy_len == 0) {
continue; continue;
} }
} }
// Determine end index of our array // Determine end index of our array
size_t end_index = our_start_index + copy_len; size_t end_index = our_start_index + copy_len;
if (our_arr.size() < end_index) { if (our_arr.size() < end_index) {
our_arr.resize(end_index); our_arr.resize(end_index);
} }
memcpy(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel), memcpy(reinterpret_cast<char *>(our_arr.data()) +
reinterpret_cast<const char*>(their_arr.data()) + their_start_index * sizeof(SamplePerChannel), our_start_index * sizeof(SamplePerChannel),
copy_len * sizeof(SamplePerChannel)); reinterpret_cast<const char *>(their_arr.data()) +
} their_start_index * sizeof(SamplePerChannel),
copy_len * sizeof(SamplePerChannel));
}
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset : length)); length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset :
length));
} }
void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational &length) void AudioVisualWaveform::OverwriteSilence(const rational &start,
const rational &length)
{ {
ValidateVirtualStart(start); ValidateVirtualStart(start);
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
Sample& our_arr = it->second; Sample &our_arr = it->second;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
// Get our destination sample // Get our destination sample
size_t our_start_index = time_to_samples(start - virtual_start_, rate_dbl); size_t our_start_index =
size_t our_length_index = time_to_samples(length, rate_dbl); time_to_samples(start - virtual_start_, rate_dbl);
size_t our_end_index = our_start_index + our_length_index; size_t our_length_index = time_to_samples(length, rate_dbl);
size_t our_end_index = our_start_index + our_length_index;
if (our_arr.size() < our_end_index) { if (our_arr.size() < our_end_index) {
our_arr.resize(our_end_index); our_arr.resize(our_end_index);
} }
memset(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel), 0, our_length_index * sizeof(SamplePerChannel)); memset(reinterpret_cast<char *>(our_arr.data()) +
} our_start_index * sizeof(SamplePerChannel),
0, our_length_index * sizeof(SamplePerChannel));
}
length_ = qMax(length_, start + length); length_ = qMax(length_, start + length);
} }
void AudioVisualWaveform::TrimIn(rational length) void AudioVisualWaveform::TrimIn(rational length)
{ {
if (length == 0) { if (length == 0) {
return; return;
} }
virtual_start_ += length; virtual_start_ += length;
bool negative = (length < 0); bool negative = (length < 0);
if (negative) { if (negative) {
length = -length; length = -length;
} }
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
Sample& data = it->second; Sample &data = it->second;
size_t chop_length = time_to_samples(length, rate_dbl); size_t chop_length = time_to_samples(length, rate_dbl);
if (chop_length == 0) { if (chop_length == 0) {
continue; continue;
} }
if (!negative) { if (!negative) {
data = Sample(data.begin() + chop_length, data.end()); data = Sample(data.begin() + chop_length, data.end());
} else { } else {
data.insert(data.begin(), chop_length, SamplePerChannel()); data.insert(data.begin(), chop_length, SamplePerChannel());
} }
} }
length_ = qMax(rational(0), length_ - length); length_ = qMax(rational(0), length_ - length);
} }
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const
{ {
AudioVisualWaveform mid = *this; AudioVisualWaveform mid = *this;
mid.TrimIn(offset - virtual_start_); mid.TrimIn(offset - virtual_start_);
return mid; return mid;
} }
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset, const rational &length) const AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset,
const rational &length) const
{ {
AudioVisualWaveform mid = *this; AudioVisualWaveform mid = *this;
mid.TrimRange(offset - virtual_start_, length); mid.TrimRange(offset - virtual_start_, length);
return mid; return mid;
} }
void AudioVisualWaveform::Resize(const rational &length) void AudioVisualWaveform::Resize(const rational &length)
{ {
if (length_ == length) { if (length_ == length) {
return; return;
} }
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first; rational rate = it->first;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
Sample& data = it->second; Sample &data = it->second;
size_t chop_length = time_to_samples(length, rate_dbl); size_t chop_length = time_to_samples(length, rate_dbl);
data.resize(chop_length); data.resize(chop_length);
} }
length_ = length; length_ = length;
} }
void AudioVisualWaveform::TrimRange(const rational &in, const rational &length) void AudioVisualWaveform::TrimRange(const rational &in, const rational &length)
{ {
TrimIn(in); TrimIn(in);
Resize(length); Resize(length);
} }
AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const rational &start, const rational &length) const AudioVisualWaveform::Sample
AudioVisualWaveform::GetSummaryFromTime(const rational &start,
const rational &length) const
{ {
// Find mipmap that requires // Find mipmap that requires
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble()); auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
double rate_dbl = using_mipmap->first.toDouble(); double rate_dbl = using_mipmap->first.toDouble();
size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl); size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl);
size_t sample_length = time_to_samples(length, rate_dbl); size_t sample_length = time_to_samples(length, rate_dbl);
const Sample &mipmap_data = using_mipmap->second; const Sample &mipmap_data = using_mipmap->second;
// Determine if the array actually has this sample // Determine if the array actually has this sample
sample_length = qMin(sample_length, mipmap_data.size() - start_sample); sample_length = qMin(sample_length, mipmap_data.size() - start_sample);
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the // Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
// array and nothing can be returned. // array and nothing can be returned.
if (sample_length > 0) { if (sample_length > 0) {
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length, channels_); return ReSumSamples(&mipmap_data.data()[start_sample], sample_length,
} channels_);
}
// Return null samples // Return null samples
return AudioVisualWaveform::Sample(channel_count(), {0, 0}); return AudioVisualWaveform::Sample(channel_count(), { 0, 0 });
} }
void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &max_val) void ExpandMinMaxChannel(const float *a, size_t length, float &min_val,
float &max_val)
{ {
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM) #if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
// SSE optimized // SSE optimized
// load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits) // load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits)
__m128 max = _mm_loadu_ps(a); __m128 max = _mm_loadu_ps(a);
__m128 min = _mm_loadu_ps(a); __m128 min = _mm_loadu_ps(a);
// loop over 'a' and compare current elements with min and max 4 by 4. // loop over 'a' and compare current elements with min and max 4 by 4.
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4 // we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
for(size_t i = 4; i < length-4; i+=4) { for (size_t i = 4; i < length - 4; i += 4) {
__m128 cur = _mm_loadu_ps(a + i); __m128 cur = _mm_loadu_ps(a + i);
max = _mm_max_ps(max, cur); max = _mm_max_ps(max, cur);
min = _mm_min_ps(min, cur); min = _mm_min_ps(min, cur);
} }
// so we read the last 4 (or less) elements in a safe manner. // so we read the last 4 (or less) elements in a safe manner.
__m128 cur = _mm_loadu_ps(a + length - 4); __m128 cur = _mm_loadu_ps(a + length - 4);
max = _mm_max_ps(max, cur); max = _mm_max_ps(max, cur);
min = _mm_min_ps(min, cur); min = _mm_min_ps(min, cur);
// this potentially overlaps up to the last 3 elements but it's not an issue. // this potentially overlaps up to the last 3 elements but it's not an issue.
// min and max will contain 4 min and max. To get the absolute min and max // min and max will contain 4 min and max. To get the absolute min and max
// we need to compare the 4 values over themselves by shuffling each time. // we need to compare the 4 values over themselves by shuffling each time.
for (size_t i = 0; i < 3; i++) { for (size_t i = 0; i < 3; i++) {
max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93)); max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93));
min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93)); min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93));
} }
// now min and max contain 4 identical items each representing min and max value respectively. // now min and max contain 4 identical items each representing min and max value respectively.
// and we store the first one into a float variable. // and we store the first one into a float variable.
_mm_store_ss(&max_val, max); _mm_store_ss(&max_val, max);
_mm_store_ss(&min_val, min); _mm_store_ss(&min_val, min);
// I bet you don't find annotated low level code very often. // I bet you don't find annotated low level code very often.
#else #else
// Standard unoptimized function // Standard unoptimized function
for (size_t i=0; i<length; i++) { for (size_t i = 0; i < length; i++) {
min_val = std::min(min_val, a[i]); min_val = std::min(min_val, a[i]);
max_val = std::max(max_val, a[i]); max_val = std::max(max_val, a[i]);
} }
#endif #endif
} }
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index, size_t length) AudioVisualWaveform::Sample
AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index,
size_t length)
{ {
int channels = samples.audio_params().channel_count(); int channels = samples.audio_params().channel_count();
AudioVisualWaveform::Sample summed_samples(channels); AudioVisualWaveform::Sample summed_samples(channels);
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) { for (int channel = 0; channel < samples.audio_params().channel_count();
ExpandMinMaxChannel(samples.data(channel) + start_index, length, summed_samples[channel].min, summed_samples[channel].max); channel++) {
} ExpandMinMaxChannel(samples.data(channel) + start_index, length,
summed_samples[channel].min,
summed_samples[channel].max);
}
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip // for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
// for (size_t i=start_index; i<end_index; i++) { // for (size_t i=start_index; i<end_index; i++) {
// ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]); // ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]);
// } // }
return summed_samples; return summed_samples;
} }
AudioVisualWaveform::Sample AudioVisualWaveform::ReSumSamples(const SamplePerChannel* samples, AudioVisualWaveform::Sample
size_t nb_samples, AudioVisualWaveform::ReSumSamples(const SamplePerChannel *samples,
int nb_channels) size_t nb_samples, int nb_channels)
{ {
AudioVisualWaveform::Sample summed_samples(nb_channels); AudioVisualWaveform::Sample summed_samples(nb_channels);
for (size_t i=0;i<nb_samples;i+=nb_channels) { for (size_t i = 0; i < nb_samples; i += nb_channels) {
for (int j=0;j<nb_channels;j++) { for (int j = 0; j < nb_channels; j++) {
const AudioVisualWaveform::SamplePerChannel& sample = samples[i + j]; const AudioVisualWaveform::SamplePerChannel &sample =
samples[i + j];
if (sample.min < summed_samples[j].min) { if (sample.min < summed_samples[j].min) {
summed_samples[j].min = sample.min; summed_samples[j].min = sample.min;
} }
if (sample.max > summed_samples[j].max) { if (sample.max > summed_samples[j].max) {
summed_samples[j].max = sample.max; summed_samples[j].max = sample.max;
} }
} }
} }
return summed_samples; return summed_samples;
} }
template <typename T> template <typename T> inline int round_away_from_zero(T t)
inline int round_away_from_zero(T t)
{ {
return (t < 0) ? std::floor(t) : std::ceil(t); return (t < 0) ? std::floor(t) : std::ceil(t);
} }
void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample& sample, int x, int y, int height, bool rectified) void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample &sample,
int x, int y, int height, bool rectified)
{ {
if (sample.empty()) { if (sample.empty()) {
return; return;
} }
int channel_height = height / sample.size(); int channel_height = height / sample.size();
int channel_half_height = channel_height / 2; int channel_half_height = channel_height / 2;
for (size_t i=0;i<sample.size();i++) { for (size_t i = 0; i < sample.size(); i++) {
float max = qMin(sample.at(i).max, 1.0f); float max = qMin(sample.at(i).max, 1.0f);
float min = qMax(sample.at(i).min, -1.0f); float min = qMax(sample.at(i).min, -1.0f);
if (rectified) { if (rectified) {
int channel_bottom = y + channel_height * (i + 1); int channel_bottom = y + channel_height * (i + 1);
int diff = round_away_from_zero((max - min) * channel_half_height); int diff = round_away_from_zero((max - min) * channel_half_height);
painter->drawLine(x, painter->drawLine(x, channel_bottom - diff, x, channel_bottom);
channel_bottom - diff, } else {
x, int channel_mid = y + channel_height * i + channel_half_height;
channel_bottom);
} else {
int channel_mid = y + channel_height * i + channel_half_height;
// We subtract the sample so that positive Y values go up on the screen rather than down, // We subtract the sample so that positive Y values go up on the screen rather than down,
// which is how waveforms are usually rendered // which is how waveforms are usually rendered
painter->drawLine(x, painter->drawLine(
channel_mid - round_away_from_zero(min * static_cast<float>(channel_half_height)), x,
x, channel_mid -
channel_mid - round_away_from_zero(max * static_cast<float>(channel_half_height))); round_away_from_zero(
} min * static_cast<float>(channel_half_height)),
} x,
channel_mid -
round_away_from_zero(
max * static_cast<float>(channel_half_height)));
}
}
} }
void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, const double& scale, const AudioVisualWaveform &samples, const rational& start_time) void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect &rect,
const double &scale,
const AudioVisualWaveform &samples,
const rational &start_time)
{ {
if (samples.mipmapped_data_.empty()) { if (samples.mipmapped_data_.empty()) {
return; return;
} }
auto using_mipmap = samples.GetMipmapForScale(scale); auto using_mipmap = samples.GetMipmapForScale(scale);
rational rate = using_mipmap->first; rational rate = using_mipmap->first;
double rate_dbl = rate.toDouble(); double rate_dbl = rate.toDouble();
const Sample& arr = using_mipmap->second; const Sample &arr = using_mipmap->second;
size_t start_sample_index = samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl); size_t start_sample_index =
samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl);
if (start_sample_index >= arr.size()) { if (start_sample_index >= arr.size()) {
return; return;
} }
size_t next_sample_index = start_sample_index; size_t next_sample_index = start_sample_index;
size_t sample_index; size_t sample_index;
Sample summary; Sample summary;
size_t summary_index = -1; size_t summary_index = -1;
const QRect& viewport = painter->viewport(); const QRect &viewport = painter->viewport();
QPoint top_left = painter->transform().map(viewport.topLeft()); QPoint top_left = painter->transform().map(viewport.topLeft());
size_t start = qMax(rect.x(), -top_left.x()); size_t start = qMax(rect.x(), -top_left.x());
size_t end = qMin(rect.right(), -top_left.x() + viewport.width()); size_t end = qMin(rect.right(), -top_left.x() + viewport.width());
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool(); bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
for (size_t i=start;i<end;i++) { for (size_t i = start; i < end; i++) {
sample_index = next_sample_index; sample_index = next_sample_index;
if (sample_index == arr.size()) { if (sample_index == arr.size()) {
break; break;
} }
next_sample_index = std::min(arr.size(), next_sample_index = std::min(
size_t(start_sample_index + std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) / scale) * samples.channel_count())); arr.size(),
size_t(start_sample_index +
std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) /
scale) *
samples.channel_count()));
if (summary_index != sample_index) { if (summary_index != sample_index) {
summary = AudioVisualWaveform::ReSumSamples(&arr.at(sample_index), summary = AudioVisualWaveform::ReSumSamples(
qMax(size_t(samples.channel_count()), next_sample_index - sample_index), &arr.at(sample_index),
samples.channel_count()); qMax(size_t(samples.channel_count()),
summary_index = sample_index; next_sample_index - sample_index),
} samples.channel_count());
summary_index = sample_index;
}
DrawSample(painter, summary, i, rect.y(), rect.height(), rectified); DrawSample(painter, summary, i, rect.y(), rect.height(), rectified);
} }
} }
size_t AudioVisualWaveform::time_to_samples(const rational &time, double sample_rate) const size_t AudioVisualWaveform::time_to_samples(const rational &time,
double sample_rate) const
{ {
return time_to_samples(time.toDouble(), sample_rate); return time_to_samples(time.toDouble(), sample_rate);
} }
size_t AudioVisualWaveform::time_to_samples(const double &time, double sample_rate) const size_t AudioVisualWaveform::time_to_samples(const double &time,
double sample_rate) const
{ {
return std::floor(time * sample_rate) * channels_; return std::floor(time * sample_rate) * channels_;
} }
std::map<rational, AudioVisualWaveform::Sample>::const_iterator AudioVisualWaveform::GetMipmapForScale(double scale) const std::map<rational, AudioVisualWaveform::Sample>::const_iterator
AudioVisualWaveform::GetMipmapForScale(double scale) const
{ {
// Find largest mipmap for this scale (or the largest if we don't find one sufficient) // Find largest mipmap for this scale (or the largest if we don't find one sufficient)
for (auto it=mipmapped_data_.cbegin(); it!=mipmapped_data_.cend(); it++) { for (auto it = mipmapped_data_.cbegin(); it != mipmapped_data_.cend();
if (it->first.toDouble() >= scale) { it++) {
return it; if (it->first.toDouble() >= scale) {
} return it;
} }
}
// We don't have a mipmap large enough for this scale, so just return the largest we have // We don't have a mipmap large enough for this scale, so just return the largest we have
return std::prev(mipmapped_data_.cend()); return std::prev(mipmapped_data_.cend());
} }
} }
+67 -48
View File
@@ -25,7 +25,8 @@
#include <QPainter> #include <QPainter>
#include <QVector> #include <QVector>
namespace olive { namespace olive
{
using namespace core; using namespace core;
@@ -37,38 +38,39 @@ using namespace core;
*/ */
class AudioVisualWaveform { class AudioVisualWaveform {
public: public:
AudioVisualWaveform(); AudioVisualWaveform();
struct SamplePerChannel { struct SamplePerChannel {
float min; float min;
float max; float max;
}; };
using Sample = std::vector<SamplePerChannel>; using Sample = std::vector<SamplePerChannel>;
int channel_count() const int channel_count() const
{ {
return channels_; return channels_;
} }
void set_channel_count(int channels) void set_channel_count(int channels)
{ {
channels_ = channels; channels_ = channels;
} }
const rational& length() const const rational &length() const
{ {
return length_; return length_;
} }
/** /**
* @brief Writes samples into the visual waveform buffer * @brief Writes samples into the visual waveform buffer
* *
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary. * Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
*/ */
void OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational& start = 0); void OverwriteSamples(const SampleBuffer &samples, int sample_rate,
const rational &start = 0);
/** /**
* @brief Replaces sums at a certain range in this visual waveform * @brief Replaces sums at a certain range in this visual waveform
* *
* @param sums * @param sums
@@ -87,53 +89,70 @@ public:
* *
* Maximum length of `sums` to overwrite with. * Maximum length of `sums` to overwrite with.
*/ */
void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = 0, const rational &length = 0); void OverwriteSums(const AudioVisualWaveform &sums, const rational &dest,
const rational &offset = 0, const rational &length = 0);
void OverwriteSilence(const rational &start, const rational &length); void OverwriteSilence(const rational &start, const rational &length);
void TrimIn(rational length); void TrimIn(rational length);
AudioVisualWaveform Mid(const rational &offset) const; AudioVisualWaveform Mid(const rational &offset) const;
AudioVisualWaveform Mid(const rational &offset, const rational &length) const; AudioVisualWaveform Mid(const rational &offset,
const rational &length) const;
void Resize(const rational &length); void Resize(const rational &length);
void TrimRange(const rational &in, const rational &length); void TrimRange(const rational &in, const rational &length);
Sample GetSummaryFromTime(const rational& start, const rational& length) const; Sample GetSummaryFromTime(const rational &start,
const rational &length) const;
static Sample SumSamples(const SampleBuffer &samples, size_t start_index, size_t length); static Sample SumSamples(const SampleBuffer &samples, size_t start_index,
size_t length);
static Sample ReSumSamples(const SamplePerChannel *samples, size_t nb_samples, int nb_channels); static Sample ReSumSamples(const SamplePerChannel *samples,
size_t nb_samples, int nb_channels);
static void DrawSample(QPainter* painter, const Sample &sample, int x, int y, int height, bool rectified); static void DrawSample(QPainter *painter, const Sample &sample, int x,
int y, int height, bool rectified);
static void DrawWaveform(QPainter* painter, const QRect &rect, const double &scale, const AudioVisualWaveform& samples, const rational &start_time); static void DrawWaveform(QPainter *painter, const QRect &rect,
const double &scale,
const AudioVisualWaveform &samples,
const rational &start_time);
// Must be a power of 2 // Must be a power of 2
static const rational kMinimumSampleRate; static const rational kMinimumSampleRate;
static const rational kMaximumSampleRate; static const rational kMaximumSampleRate;
private: private:
void OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational& start, double target_rate, Sample &data, size_t &start_index, size_t &samples_length); void OverwriteSamplesFromBuffer(const SampleBuffer &samples,
int sample_rate, const rational &start,
double target_rate, Sample &data,
size_t &start_index,
size_t &samples_length);
void OverwriteSamplesFromMipmap(const Sample& input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational& start, double output_rate, Sample &output_data); void OverwriteSamplesFromMipmap(const Sample &input,
double input_sample_rate,
size_t &input_start, size_t &input_length,
const rational &start, double output_rate,
Sample &output_data);
size_t time_to_samples(const rational& time, double sample_rate) const; size_t time_to_samples(const rational &time, double sample_rate) const;
size_t time_to_samples(const double& time, double sample_rate) const; size_t time_to_samples(const double &time, double sample_rate) const;
std::map<rational, Sample>::const_iterator GetMipmapForScale(double scale) const; std::map<rational, Sample>::const_iterator
GetMipmapForScale(double scale) const;
void ValidateVirtualStart(const rational &new_start); void ValidateVirtualStart(const rational &new_start);
rational virtual_start_; rational virtual_start_;
int channels_; int channels_;
std::map<rational, Sample> mipmapped_data_; std::map<rational, Sample> mipmapped_data_;
rational length_;
rational length_;
}; };
} }
+2 -2
View File
@@ -20,11 +20,11 @@
#include "cliexportmanager.h" #include "cliexportmanager.h"
namespace olive { namespace olive
{
CLIExportManager::CLIExportManager() CLIExportManager::CLIExportManager()
{ {
} }
} }
+4 -4
View File
@@ -23,12 +23,12 @@
#include "task/export/export.h" #include "task/export/export.h"
namespace olive { namespace olive
class CLIExportManager : public QObject
{ {
class CLIExportManager : public QObject {
public: public:
CLIExportManager(); CLIExportManager();
}; };
} }
+56 -54
View File
@@ -22,85 +22,87 @@
#include <iostream> #include <iostream>
namespace olive { namespace olive
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) :
QObject(parent),
title_(title),
progress_(-1),
drawn_(false)
{ {
SetProgress(0);
CLIProgressDialog::CLIProgressDialog(const QString &title, QObject *parent)
: QObject(parent)
, title_(title)
, progress_(-1)
, drawn_(false)
{
SetProgress(0);
} }
void CLIProgressDialog::Update() void CLIProgressDialog::Update()
{ {
if (drawn_) { if (drawn_) {
// We've been here before, do a carriage return back to the start of the terminal line // We've been here before, do a carriage return back to the start of the terminal line
std::cout << "\r"; std::cout << "\r";
} else { } else {
drawn_ = true; drawn_ = true;
} }
// FIXME: Get real column count // FIXME: Get real column count
int columns = 80; int columns = 80;
int title_columns = columns / 2 - 1; int title_columns = columns / 2 - 1;
// Print "title" text // Print "title" text
QString sized_title = title_; QString sized_title = title_;
if (title_.size() > title_columns) { if (title_.size() > title_columns) {
sized_title = title_.left(title_columns - 3).append(QStringLiteral("...")); sized_title =
} else { title_.left(title_columns - 3).append(QStringLiteral("..."));
sized_title = title_; } else {
} sized_title = title_;
}
std::cout << sized_title.toUtf8().constData(); std::cout << sized_title.toUtf8().constData();
// Pad out the rest of the title area if necessary // Pad out the rest of the title area if necessary
for (int i=sized_title.size(); i<title_columns; i++) { for (int i = sized_title.size(); i < title_columns; i++) {
std::cout << " "; std::cout << " ";
} }
// Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters // Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters
int progress_bar_columns = columns / 2 - 7; int progress_bar_columns = columns / 2 - 7;
std::cout << "["; std::cout << "[";
// Get UI bar progress // Get UI bar progress
int bar_prog = qRound(progress_ * progress_bar_columns); int bar_prog = qRound(progress_ * progress_bar_columns);
// Draw filled in bar // Draw filled in bar
for (int i=0;i<bar_prog;i++) { for (int i = 0; i < bar_prog; i++) {
std::cout << "="; std::cout << "=";
} }
// Draw empty space // Draw empty space
for (int i=bar_prog;i<progress_bar_columns;i++) { for (int i = bar_prog; i < progress_bar_columns; i++) {
std::cout << " "; std::cout << " ";
} }
std::cout << "] "; std::cout << "] ";
if (progress_ < 100) { if (progress_ < 100) {
std::cout << " "; std::cout << " ";
} }
if (progress_ < 10) { if (progress_ < 10) {
std::cout << " "; std::cout << " ";
} }
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush; std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
} }
void CLIProgressDialog::SetProgress(double p) void CLIProgressDialog::SetProgress(double p)
{ {
if (progress_ != p) { if (progress_ != p) {
progress_ = p; progress_ = p;
Update(); Update();
} }
} }
} }
+9 -10
View File
@@ -26,25 +26,24 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
class CLIProgressDialog : public QObject
{ {
class CLIProgressDialog : public QObject {
public: public:
CLIProgressDialog(const QString &title, QObject* parent = nullptr); CLIProgressDialog(const QString &title, QObject *parent = nullptr);
public slots: public slots:
void SetProgress(double p); void SetProgress(double p);
private: private:
void Update(); void Update();
QString title_; QString title_;
double progress_; double progress_;
bool drawn_;
bool drawn_;
}; };
} }
+8 -7
View File
@@ -20,18 +20,19 @@
#include "clitaskdialog.h" #include "clitaskdialog.h"
namespace olive { namespace olive
CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) :
CLIProgressDialog(task->GetTitle(), parent),
task_(task)
{ {
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
CLITaskDialog::CLITaskDialog(Task *task, QObject *parent)
: CLIProgressDialog(task->GetTitle(), parent)
, task_(task)
{
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
} }
bool CLITaskDialog::Run() bool CLITaskDialog::Run()
{ {
return task_->Start(); return task_->Start();
} }
} }
+8 -9
View File
@@ -24,19 +24,18 @@
#include "cli/cliprogress/cliprogressdialog.h" #include "cli/cliprogress/cliprogressdialog.h"
#include "task/task.h" #include "task/task.h"
namespace olive { namespace olive
class CLITaskDialog : public CLIProgressDialog
{ {
Q_OBJECT
public:
CLITaskDialog(Task *task, QObject* parent = nullptr);
bool Run(); class CLITaskDialog : public CLIProgressDialog {
Q_OBJECT
public:
CLITaskDialog(Task *task, QObject *parent = nullptr);
bool Run();
private: private:
Task* task_; Task *task_;
}; };
} }
+98 -84
View File
@@ -4,123 +4,137 @@
#include "task/taskmanager.h" #include "task/taskmanager.h"
namespace olive { namespace olive
{
ConformManager *ConformManager::instance_ = nullptr; ConformManager *ConformManager::instance_ = nullptr;
ConformManager::Conform ConformManager::GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params, bool wait) ConformManager::Conform ConformManager::GetConformState(
const QString &decoder_id, const QString &cache_path,
const Decoder::CodecStream &stream, const AudioParams &params, bool wait)
{ {
// Mutex because we'll need to check the status of a conform task // Mutex because we'll need to check the status of a conform task
QMutexLocker locker(&mutex_); QMutexLocker locker(&mutex_);
// Return existing conform if exists // Return existing conform if exists
QVector<QString> filenames = GetConformedFilename(cache_path, stream, params); QVector<QString> filenames =
if (AllConformsExist(filenames)) { GetConformedFilename(cache_path, stream, params);
return {kConformExists, filenames, nullptr}; if (AllConformsExist(filenames)) {
} return { kConformExists, filenames, nullptr };
}
ConformTask *conforming_task = nullptr; ConformTask *conforming_task = nullptr;
foreach (const ConformData &data, conforming_) { foreach (const ConformData &data, conforming_) {
if (data.stream == stream && data.params == params) { if (data.stream == stream && data.params == params) {
// Already creating conform in a task // Already creating conform in a task
conforming_task = data.task; conforming_task = data.task;
break; break;
} }
} }
if (!conforming_task) { if (!conforming_task) {
// Not conforming yet, create a task to do so // Not conforming yet, create a task to do so
// We conform to a different filename until it's done to make it clear even across sessions // We conform to a different filename until it's done to make it clear even across sessions
// whether this conform is ready or not // whether this conform is ready or not
QVector<QString> working_filenames = filenames; QVector<QString> working_filenames = filenames;
for (int i=0; i<working_filenames.size(); i++) { for (int i = 0; i < working_filenames.size(); i++) {
working_filenames[i].append(QStringLiteral(".working")); working_filenames[i].append(QStringLiteral(".working"));
} }
conforming_task = new ConformTask(decoder_id, stream, params, working_filenames); conforming_task =
connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished); new ConformTask(decoder_id, stream, params, working_filenames);
conforming_task->moveToThread(TaskManager::instance()->thread()); connect(conforming_task, &ConformTask::Finished, this,
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task)); &ConformManager::ConformTaskFinished);
conforming_task->moveToThread(TaskManager::instance()->thread());
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask",
Qt::QueuedConnection,
Q_ARG(Task *, conforming_task));
conforming_.append({stream, params, conforming_task, working_filenames, filenames}); conforming_.append(
} { stream, params, conforming_task, working_filenames, filenames });
}
if (wait) { if (wait) {
do { do {
conform_done_condition_.wait(&mutex_); conform_done_condition_.wait(&mutex_);
} while (!AllConformsExist(filenames)); } while (!AllConformsExist(filenames));
return {kConformExists, filenames, nullptr}; return { kConformExists, filenames, nullptr };
} }
return {kConformGenerating, QVector<QString>(), conforming_task}; return { kConformGenerating, QVector<QString>(), conforming_task };
} }
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params) QVector<QString>
ConformManager::GetConformedFilename(const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params)
{ {
QVector<QString> filenames(params.channel_count()); QVector<QString> filenames(params.channel_count());
for (int i=0; i<filenames.size(); i++) { for (int i = 0; i < filenames.size(); i++) {
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()), QString index_fn =
QString::number(stream.stream()), QStringLiteral("%1-%2.%3.%4.%5.%6.pcm")
QString::number(params.sample_rate()), .arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
QString::number(params.format()), QString::number(stream.stream()),
QString::number(params.channel_layout().u.mask), QString::number(params.sample_rate()),
QString::number(i)); QString::number(params.format()),
QString::number(params.channel_layout().u.mask),
QString::number(i));
filenames[i] = QDir(cache_path).filePath(index_fn); filenames[i] = QDir(cache_path).filePath(index_fn);
} }
return filenames; return filenames;
} }
bool ConformManager::AllConformsExist(const QVector<QString> &filenames) bool ConformManager::AllConformsExist(const QVector<QString> &filenames)
{ {
foreach (const QString &fn, filenames) { foreach (const QString &fn, filenames) {
if (!QFileInfo::exists(fn)) { if (!QFileInfo::exists(fn)) {
return false; return false;
} }
} }
return true; return true;
} }
void ConformManager::ConformTaskFinished(Task *task, bool succeeded) void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
{ {
QMutexLocker locker(&mutex_); QMutexLocker locker(&mutex_);
ConformData data; ConformData data;
// Remove conform data from list // Remove conform data from list
for (int i=0; i<conforming_.size(); i++) { for (int i = 0; i < conforming_.size(); i++) {
const ConformData &c = conforming_.at(i); const ConformData &c = conforming_.at(i);
if (c.task == task) { if (c.task == task) {
data = c; data = c;
conforming_.removeAt(i); conforming_.removeAt(i);
break; break;
} }
} }
if (succeeded) { if (succeeded) {
// Move file to standard conform name, making it clear this conform is ready for use // Move file to standard conform name, making it clear this conform is ready for use
for (int i=0; i<data.finished_filename.size(); i++) { for (int i = 0; i < data.finished_filename.size(); i++) {
const QString &finished = data.finished_filename.at(i); const QString &finished = data.finished_filename.at(i);
const QString &working = data.working_filename.at(i); const QString &working = data.working_filename.at(i);
QFile::remove(finished); QFile::remove(finished);
QFile::rename(working, finished); QFile::rename(working, finished);
} }
conform_done_condition_.wakeAll(); conform_done_condition_.wakeAll();
locker.unlock(); locker.unlock();
emit ConformReady(); emit ConformReady();
} else { } else {
// Failed, just delete the working filename if exists // Failed, just delete the working filename if exists
for (int i=0; i<data.working_filename.size(); i++) { for (int i = 0; i < data.working_filename.size(); i++) {
QFile::remove(data.working_filename.at(i)); QFile::remove(data.working_filename.at(i));
} }
} }
} }
} }
+57 -55
View File
@@ -7,82 +7,84 @@
#include "decoder.h" #include "decoder.h"
#include "task/conform/conform.h" #include "task/conform/conform.h"
namespace olive { namespace olive
class ConformManager : public QObject
{ {
Q_OBJECT
class ConformManager : public QObject {
Q_OBJECT
public: public:
static void CreateInstance() static void CreateInstance()
{ {
if (!instance_) { if (!instance_) {
instance_ = new ConformManager(); instance_ = new ConformManager();
} }
} }
static void DestroyInstance() static void DestroyInstance()
{ {
delete instance_; delete instance_;
instance_ = nullptr; instance_ = nullptr;
} }
static ConformManager *instance() static ConformManager *instance()
{ {
return instance_; return instance_;
} }
enum ConformState { enum ConformState { kConformExists, kConformGenerating };
kConformExists,
kConformGenerating
};
struct Conform { struct Conform {
ConformState state; ConformState state;
QVector<QString> filenames; QVector<QString> filenames;
ConformTask *task; ConformTask *task;
}; };
/** /**
* @brief Get conform state, and start conforming if no conform exists * @brief Get conform state, and start conforming if no conform exists
* *
* Thread-safe. * Thread-safe.
*/ */
Conform GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params, bool wait); Conform GetConformState(const QString &decoder_id,
const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params, bool wait);
signals: signals:
void ConformReady(); void ConformReady();
private: private:
ConformManager() = default; ConformManager() = default;
static ConformManager *instance_; static ConformManager *instance_;
QMutex mutex_; QMutex mutex_;
QWaitCondition conform_done_condition_; QWaitCondition conform_done_condition_;
struct ConformData { struct ConformData {
Decoder::CodecStream stream; Decoder::CodecStream stream;
AudioParams params; AudioParams params;
ConformTask *task; ConformTask *task;
QVector<QString> working_filename; QVector<QString> working_filename;
QVector<QString> finished_filename; QVector<QString> finished_filename;
}; };
QVector<ConformData> conforming_; QVector<ConformData> conforming_;
/** /**
* @brief Get the destination filename of an audio stream conformed to a set of parameters * @brief Get the destination filename of an audio stream conformed to a set of parameters
*/ */
static QVector<QString> GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params); static QVector<QString>
GetConformedFilename(const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params);
static bool AllConformsExist(const QVector<QString> &filenames); static bool AllConformsExist(const QVector<QString> &filenames);
private slots: private slots:
void ConformTaskFinished(Task *task, bool succeeded); void ConformTaskFinished(Task *task, bool succeeded);
}; };
} } // namespace olive
#endif // CONFORMMANAGER_H #endif // CONFORMMANAGER_H
+215 -185
View File
@@ -33,151 +33,162 @@
#include "node/project.h" #include "node/project.h"
#include "task/taskmanager.h" #include "task/taskmanager.h"
namespace olive { namespace olive
{
const rational Decoder::kAnyTimecode = RATIONAL_MIN; const rational Decoder::kAnyTimecode = RATIONAL_MIN;
Decoder::Decoder() : Decoder::Decoder()
cached_texture_(nullptr) : cached_texture_(nullptr)
{ {
UpdateLastAccessed(); UpdateLastAccessed();
} }
void Decoder::IncrementAccessTime(qint64 t) void Decoder::IncrementAccessTime(qint64 t)
{ {
last_accessed_ += t; last_accessed_ += t;
} }
bool Decoder::Open(const CodecStream &stream) bool Decoder::Open(const CodecStream &stream)
{ {
QMutexLocker locker(&mutex_); QMutexLocker locker(&mutex_);
UpdateLastAccessed(); UpdateLastAccessed();
if (stream_.IsValid()) { if (stream_.IsValid()) {
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not. // Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
if (stream_ == stream) { if (stream_ == stream) {
return true; return true;
} else { } else {
qWarning() << "Tried to open a decoder that was already open with another stream"; qWarning()
return false; << "Tried to open a decoder that was already open with another stream";
} return false;
} else { }
// Stream was not open, try opening it now } else {
if (!stream.IsValid()) { // Stream was not open, try opening it now
// Cannot open null stream if (!stream.IsValid()) {
qCritical() << "Decoder attempted to open null stream"; // Cannot open null stream
return false; qCritical() << "Decoder attempted to open null stream";
} return false;
}
if (!stream.Exists()) { if (!stream.Exists()) {
// Cannot open file that doesn't exist // Cannot open file that doesn't exist
qCritical() << "Decoder attempted to open file that doesn't exist"; qCritical() << "Decoder attempted to open file that doesn't exist";
return false; return false;
} }
// Set stream // Set stream
stream_ = stream; stream_ = stream;
// Try open internal // Try open internal
if (OpenInternal()) { if (OpenInternal()) {
return true; return true;
} else { } else {
// Unset stream // Unset stream
qCritical() << "Failed to open" << stream_.filename() << "stream" << stream_.stream(); qCritical() << "Failed to open" << stream_.filename() << "stream"
CloseInternal(); << stream_.stream();
stream_.Reset(); CloseInternal();
return false; stream_.Reset();
} return false;
} }
}
} }
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p) TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
{ {
QMutexLocker locker(&mutex_); QMutexLocker locker(&mutex_);
UpdateLastAccessed(); UpdateLastAccessed();
if (!stream_.IsValid()) { if (!stream_.IsValid()) {
qCritical() << "Can't retrieve video on a closed decoder"; qCritical() << "Can't retrieve video on a closed decoder";
return nullptr; return nullptr;
} }
if (!SupportsVideo()) { if (!SupportsVideo()) {
qCritical() << "Decoder doesn't support video"; qCritical() << "Decoder doesn't support video";
return nullptr; return nullptr;
} }
if (p.cancelled && p.cancelled->IsCancelled()) { if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr; return nullptr;
} }
if (cached_texture_ && cached_time_ == p.time && cached_divider_ == p.divider) { if (cached_texture_ && cached_time_ == p.time &&
return cached_texture_; cached_divider_ == p.divider) {
} return cached_texture_;
}
cached_texture_ = RetrieveVideoInternal(p); cached_texture_ = RetrieveVideoInternal(p);
cached_time_ = p.time; cached_time_ = p.time;
cached_divider_ = p.divider; cached_divider_ = p.divider;
return cached_texture_; return cached_texture_;
} }
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams &params, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode) Decoder::RetrieveAudioStatus
Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
const AudioParams &params, const QString &cache_path,
LoopMode loop_mode, RenderMode::Mode mode)
{ {
QMutexLocker locker(&mutex_); QMutexLocker locker(&mutex_);
UpdateLastAccessed(); UpdateLastAccessed();
if (!stream_.IsValid()) { if (!stream_.IsValid()) {
qCritical() << "Can't retrieve audio on a closed decoder"; qCritical() << "Can't retrieve audio on a closed decoder";
return kInvalid; return kInvalid;
} }
if (!SupportsAudio()) { if (!SupportsAudio()) {
qCritical() << "Decoder doesn't support audio"; qCritical() << "Decoder doesn't support audio";
return kInvalid; return kInvalid;
} }
// Get conform state from ConformManager // Get conform state from ConformManager
ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline)); ConformManager::Conform conform =
if (conform.state == ConformManager::kConformGenerating) { ConformManager::instance()->GetConformState(
// If we need the task, it's available in `conform.task` id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
return kWaitingForConform; if (conform.state == ConformManager::kConformGenerating) {
} // If we need the task, it's available in `conform.task`
return kWaitingForConform;
}
// See if we got the conform // See if we got the conform
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) { if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode,
return kOK; params)) {
} else { return kOK;
return kUnknownError; } else {
} return kUnknownError;
}
} }
qint64 Decoder::GetLastAccessedTime() qint64 Decoder::GetLastAccessedTime()
{ {
return last_accessed_; return last_accessed_;
} }
void Decoder::Close() void Decoder::Close()
{ {
QMutexLocker locker(&mutex_); QMutexLocker locker(&mutex_);
UpdateLastAccessed(); UpdateLastAccessed();
cached_texture_ = nullptr; cached_texture_ = nullptr;
if (stream_.IsValid()) { if (stream_.IsValid()) {
CloseInternal(); CloseInternal();
stream_.Reset(); stream_.Reset();
} else { } else {
qWarning() << "Tried to close a decoder that wasn't open"; qWarning() << "Tried to close a decoder that wasn't open";
} }
} }
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams &params, CancelAtom *cancelled) bool Decoder::ConformAudio(const QVector<QString> &output_filenames,
const AudioParams &params, CancelAtom *cancelled)
{ {
return ConformAudioInternal(output_filenames, params, cancelled); return ConformAudioInternal(output_filenames, params, cancelled);
} }
/* /*
@@ -186,159 +197,178 @@ bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const Audio
QVector<DecoderPtr> Decoder::ReceiveListOfAllDecoders() QVector<DecoderPtr> Decoder::ReceiveListOfAllDecoders()
{ {
QVector<DecoderPtr> decoders; QVector<DecoderPtr> decoders;
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last, // The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
// since it supports so many formats and we presumably want to override those formats with a more specific decoder. // since it supports so many formats and we presumably want to override those formats with a more specific decoder.
decoders.append(std::make_shared<OIIODecoder>()); decoders.append(std::make_shared<OIIODecoder>());
decoders.append(std::make_shared<FFmpegDecoder>()); decoders.append(std::make_shared<FFmpegDecoder>());
return decoders; return decoders;
} }
DecoderPtr Decoder::CreateFromID(const QString &id) DecoderPtr Decoder::CreateFromID(const QString &id)
{ {
if (id.isEmpty()) { if (id.isEmpty()) {
return nullptr; return nullptr;
} }
// Create list to iterate through // Create list to iterate through
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders(); QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
foreach (DecoderPtr d, decoder_list) { foreach (DecoderPtr d, decoder_list) {
if (d->id() == id) { if (d->id() == id) {
return d; return d;
} }
} }
return nullptr; return nullptr;
} }
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration) void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
{ {
if (duration != AV_NOPTS_VALUE && duration != 0) { if (duration != AV_NOPTS_VALUE && duration != 0) {
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(duration)); emit IndexProgress(static_cast<double>(ts) /
} static_cast<double>(duration));
}
} }
QString Decoder::TransformImageSequenceFileName(const QString &filename, const int64_t& number) QString Decoder::TransformImageSequenceFileName(const QString &filename,
const int64_t &number)
{ {
int digit_count = GetImageSequenceDigitCount(filename); int digit_count = GetImageSequenceDigitCount(filename);
QFileInfo file_info(filename); QFileInfo file_info(filename);
QString original_basename = file_info.completeBaseName(); QString original_basename = file_info.completeBaseName();
QString new_basename = original_basename.left(original_basename.size() - digit_count) QString new_basename =
.append(QStringLiteral("%1").arg(number, digit_count, 10, QChar('0'))); original_basename.left(original_basename.size() - digit_count)
.append(
QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
return file_info.dir().filePath(file_info.fileName().replace(original_basename, new_basename)); return file_info.dir().filePath(
file_info.fileName().replace(original_basename, new_basename));
} }
int Decoder::GetImageSequenceDigitCount(const QString &filename) int Decoder::GetImageSequenceDigitCount(const QString &filename)
{ {
QString basename = QFileInfo(filename).completeBaseName(); QString basename = QFileInfo(filename).completeBaseName();
// See if basename contains a number at the end // See if basename contains a number at the end
int digit_count = 0; int digit_count = 0;
for (int i=basename.size()-1;i>=0;i--) { for (int i = basename.size() - 1; i >= 0; i--) {
if (basename.at(i).isDigit()) { if (basename.at(i).isDigit()) {
digit_count++; digit_count++;
} else { } else {
break; break;
} }
} }
return digit_count; return digit_count;
} }
int64_t Decoder::GetImageSequenceIndex(const QString &filename) int64_t Decoder::GetImageSequenceIndex(const QString &filename)
{ {
int digit_count = GetImageSequenceDigitCount(filename); int digit_count = GetImageSequenceDigitCount(filename);
QFileInfo file_info(filename); QFileInfo file_info(filename);
QString original_basename = file_info.completeBaseName(); QString original_basename = file_info.completeBaseName();
QString number_only = original_basename.mid(original_basename.size() - digit_count); QString number_only =
original_basename.mid(original_basename.size() - digit_count);
return number_only.toLongLong(); return number_only.toLongLong();
} }
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p) TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{ {
Q_UNUSED(p) Q_UNUSED(p)
return nullptr; return nullptr;
} }
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, CancelAtom *cancelled) bool Decoder::ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled)
{ {
Q_UNUSED(filenames) Q_UNUSED(filenames)
Q_UNUSED(cancelled) Q_UNUSED(cancelled)
Q_UNUSED(params) Q_UNUSED(params)
return false; return false;
} }
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &input_params) bool Decoder::RetrieveAudioFromConform(
SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames,
TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
{ {
PlanarFileDevice input; PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) { if (input.open(conform_filenames, QFile::ReadOnly)) {
// Offset range by audio start offset // Offset range by audio start offset
range -= GetAudioStartOffset(); range -= GetAudioStartOffset();
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count(); qint64 read_index = input_params.time_to_bytes(range.in()) /
qint64 write_index = 0; input_params.channel_count();
qint64 write_index = 0;
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel(); const qint64 buffer_length_in_bytes =
sample_buffer.sample_count() *
input_params.bytes_per_sample_per_channel();
while (write_index < buffer_length_in_bytes) { while (write_index < buffer_length_in_bytes) {
if (loop_mode == LoopMode::kLoopModeLoop) { if (loop_mode == LoopMode::kLoopModeLoop) {
while (read_index >= input.size()) { while (read_index >= input.size()) {
read_index -= input.size(); read_index -= input.size();
} }
while (read_index < 0) { while (read_index < 0) {
read_index += input.size(); read_index += input.size();
} }
} }
qint64 write_count = 0; qint64 write_count = 0;
if (read_index < 0) { if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start // Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, buffer_length_in_bytes); write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer.silence_bytes(write_index, write_index + write_count); sample_buffer.silence_bytes(write_index,
} else if (read_index >= input.size()) { write_index + write_count);
// Reading after data length, write silence until the end of the buffer } else if (read_index >= input.size()) {
write_count = buffer_length_in_bytes - write_index; // Reading after data length, write silence until the end of the buffer
sample_buffer.silence_bytes(write_index, write_index + write_count); write_count = buffer_length_in_bytes - write_index;
} else { sample_buffer.silence_bytes(write_index,
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index); write_index + write_count);
input.seek(read_index); } else {
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index); write_count = qMin(input.size() - read_index,
} buffer_length_in_bytes - write_index);
input.seek(read_index);
input.read(reinterpret_cast<char **>(
sample_buffer.to_raw_ptrs().data()),
write_count, write_index);
}
read_index += write_count; read_index += write_count;
write_index += write_count; write_index += write_count;
} }
input.close(); input.close();
return true; return true;
} }
return false; return false;
} }
void Decoder::UpdateLastAccessed() void Decoder::UpdateLastAccessed()
{ {
last_accessed_ = QDateTime::currentMSecsSinceEpoch(); last_accessed_ = QDateTime::currentMSecsSinceEpoch();
} }
uint qHash(Decoder::CodecStream stream, uint seed) uint qHash(Decoder::CodecStream stream, uint seed)
{ {
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^ qHash(stream.block(), seed); return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^
qHash(stream.block(), seed);
} }
} }
+143 -126
View File
@@ -36,12 +36,17 @@ extern "C" {
#include "render/cancelatom.h" #include "render/cancelatom.h"
#include "render/rendermodes.h" #include "render/rendermodes.h"
namespace olive { namespace olive
{
class Decoder; class Decoder;
using DecoderPtr = std::shared_ptr<Decoder>; using DecoderPtr = std::shared_ptr<Decoder>;
#define DECODER_DEFAULT_DESTRUCTOR(x) virtual ~x() override {CloseInternal();} #define DECODER_DEFAULT_DESTRUCTOR(x) \
virtual ~x() override \
{ \
CloseInternal(); \
}
/** /**
* @brief A decoder's is the main class for bringing external media into Olive * @brief A decoder's is the main class for bringing external media into Olive
@@ -59,89 +64,88 @@ using DecoderPtr = std::shared_ptr<Decoder>;
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService * A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
* to be utilized in the rest of the rendering pipeline. * to be utilized in the rest of the rendering pipeline.
*/ */
class Decoder : public QObject class Decoder : public QObject {
{ Q_OBJECT
Q_OBJECT
public: public:
enum RetrieveState { enum RetrieveState { kReady, kFailedToOpen, kIndexUnavailable };
kReady,
kFailedToOpen,
kIndexUnavailable
};
Decoder(); Decoder();
/** /**
* @brief Unique decoder ID * @brief Unique decoder ID
*/ */
virtual QString id() const = 0; virtual QString id() const = 0;
virtual bool SupportsVideo(){return false;} virtual bool SupportsVideo()
virtual bool SupportsAudio(){return false;} {
return false;
}
virtual bool SupportsAudio()
{
return false;
}
void IncrementAccessTime(qint64 t); void IncrementAccessTime(qint64 t);
class CodecStream class CodecStream {
{ public:
public: CodecStream()
CodecStream() : : stream_(-1)
stream_(-1), , block_(nullptr)
block_(nullptr) {
{ }
}
CodecStream(const QString& filename, int stream, Block *block) : CodecStream(const QString &filename, int stream, Block *block)
filename_(filename), : filename_(filename)
stream_(stream), , stream_(stream)
block_(block) , block_(block)
{ {
} }
bool IsValid() const bool IsValid() const
{ {
return !filename_.isEmpty() && stream_ >= 0; return !filename_.isEmpty() && stream_ >= 0;
} }
bool Exists() const bool Exists() const
{ {
return QFileInfo::exists(filename_); return QFileInfo::exists(filename_);
} }
void Reset() void Reset()
{ {
*this = CodecStream(); *this = CodecStream();
} }
bool operator==(const CodecStream& rhs) const bool operator==(const CodecStream &rhs) const
{ {
return filename_ == rhs.filename_ && stream_ == rhs.stream_; return filename_ == rhs.filename_ && stream_ == rhs.stream_;
} }
const QString& filename() const const QString &filename() const
{ {
return filename_; return filename_;
} }
int stream() const int stream() const
{ {
return stream_; return stream_;
} }
Block *block() const Block *block() const
{ {
return block_; return block_;
} }
private: private:
QString filename_; QString filename_;
int stream_; int stream_;
Block *block_; Block *block_;
};
}; /**
/**
* @brief Open stream for decoding * @brief Open stream for decoding
* *
* This function is thread safe. * This function is thread safe.
@@ -150,22 +154,21 @@ public:
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't * already open and the stream == the stream provided. Returns FALSE if the stream couldn't
* be opened OR if already open and the stream is NOT the same. * be opened OR if already open and the stream is NOT the same.
*/ */
bool Open(const CodecStream& stream); bool Open(const CodecStream &stream);
static const rational kAnyTimecode; static const rational kAnyTimecode;
struct RetrieveVideoParams struct RetrieveVideoParams {
{ Renderer *renderer = nullptr;
Renderer *renderer = nullptr; rational time;
rational time; int divider = 1;
int divider = 1; PixelFormat maximum_format = PixelFormat::INVALID;
PixelFormat maximum_format = PixelFormat::INVALID; CancelAtom *cancelled = nullptr;
CancelAtom *cancelled = nullptr; VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault; VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone; };
};
/** /**
* @brief Retrieves a video frame from footage * @brief Retrieves a video frame from footage
* *
* This function will always return a valid frame unless a fatal error occurs (in such case, * This function will always return a valid frame unless a fatal error occurs (in such case,
@@ -175,16 +178,16 @@ public:
* *
* This function is thread safe and can only run while the decoder is open. \see Open() * This function is thread safe and can only run while the decoder is open. \see Open()
*/ */
TexturePtr RetrieveVideo(const RetrieveVideoParams& p); TexturePtr RetrieveVideo(const RetrieveVideoParams &p);
enum RetrieveAudioStatus { enum RetrieveAudioStatus {
kInvalid = -1, kInvalid = -1,
kOK, kOK,
kWaitingForConform, kWaitingForConform,
kUnknownError kUnknownError
}; };
/** /**
* @brief Retrieve audio data from footage * @brief Retrieve audio data from footage
* *
* This function will always return a sample buffer unless a fatal error occurs (in such case, * This function will always return a sample buffer unless a fatal error occurs (in such case,
@@ -192,14 +195,17 @@ public:
* *
* This function is thread safe and can only run while the decoder is open. \see Open() * This function is thread safe and can only run while the decoder is open. \see Open()
*/ */
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode); RetrieveAudioStatus
RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
const AudioParams &params, const QString &cache_path,
LoopMode loop_mode, RenderMode::Mode mode);
/** /**
* @brief Determine the last time this decoder instance was used in any way * @brief Determine the last time this decoder instance was used in any way
*/ */
qint64 GetLastAccessedTime(); qint64 GetLastAccessedTime();
/** /**
* @brief Generate a Footage object from a file * @brief Generate a Footage object from a file
* *
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it * If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
@@ -210,39 +216,43 @@ public:
* *
* This function is re-entrant. * This function is re-entrant.
*/ */
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const = 0; virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const = 0;
/** /**
* @brief Closes media/deallocates memory * @brief Closes media/deallocates memory
* *
* This function is thread safe and can only run while the decoder is open. \see Open() * This function is thread safe and can only run while the decoder is open. \see Open()
*/ */
void Close(); void Close();
/** /**
* @brief Conform audio stream * @brief Conform audio stream
*/ */
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams &params, CancelAtom *cancelled = nullptr); bool ConformAudio(const QVector<QString> &output_filenames,
const AudioParams &params,
CancelAtom *cancelled = nullptr);
/** /**
* @brief Create a Decoder instance using a Decoder ID * @brief Create a Decoder instance using a Decoder ID
* *
* @return * @return
* *
* A Decoder instance or nullptr if a Decoder with this ID does not exist * A Decoder instance or nullptr if a Decoder with this ID does not exist
*/ */
static DecoderPtr CreateFromID(const QString& id); static DecoderPtr CreateFromID(const QString &id);
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number); static QString TransformImageSequenceFileName(const QString &filename,
const int64_t &number);
static int GetImageSequenceDigitCount(const QString& filename); static int GetImageSequenceDigitCount(const QString &filename);
static int64_t GetImageSequenceIndex(const QString& filename); static int64_t GetImageSequenceIndex(const QString &filename);
static QVector<DecoderPtr> ReceiveListOfAllDecoders(); static QVector<DecoderPtr> ReceiveListOfAllDecoders();
protected: protected:
/** /**
* @brief Internal open function * @brief Internal open function
* *
* Sub-classes must override this function. Function will already be mutexed, so there is no need * Sub-classes must override this function. Function will already be mutexed, so there is no need
@@ -254,62 +264,69 @@ protected:
* return FALSE. If this function returns false, Decoder will call CloseInternal to clean any * return FALSE. If this function returns false, Decoder will call CloseInternal to clean any
* memory allocated during OpenInternal. * memory allocated during OpenInternal.
*/ */
virtual bool OpenInternal() = 0; virtual bool OpenInternal() = 0;
/** /**
* @brief Internal close function * @brief Internal close function
* *
* Sub-classes must override this function. Function should be able to safely clear all allocated * Sub-classes must override this function. Function should be able to safely clear all allocated
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called. * memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
*/ */
virtual void CloseInternal() = 0; virtual void CloseInternal() = 0;
/** /**
* @brief Internal frame retrieval function * @brief Internal frame retrieval function
* *
* Sub-classes must override this function IF they support video. Function is already mutexed * Sub-classes must override this function IF they support video. Function is already mutexed
* so sub-classes don't need to worry about thread safety. * so sub-classes don't need to worry about thread safety.
*/ */
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p); virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p);
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled); virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled);
void SignalProcessingProgress(int64_t ts, int64_t duration); void SignalProcessingProgress(int64_t ts, int64_t duration);
/** /**
* @brief Return currently open stream * @brief Return currently open stream
* *
* This function is NOT thread safe and should therefore only be called by thread safe functions. * This function is NOT thread safe and should therefore only be called by thread safe functions.
*/ */
const CodecStream& stream() const const CodecStream &stream() const
{ {
return stream_; return stream_;
} }
virtual rational GetAudioStartOffset() const { return 0; } virtual rational GetAudioStartOffset() const
{
return 0;
}
signals: signals:
/** /**
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if * @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
* available * available
*/ */
void IndexProgress(double); void IndexProgress(double);
private: private:
void UpdateLastAccessed(); void UpdateLastAccessed();
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &params); bool RetrieveAudioFromConform(SampleBuffer &sample_buffer,
const QVector<QString> &conform_filenames,
TimeRange range, LoopMode loop_mode,
const AudioParams &params);
CodecStream stream_; CodecStream stream_;
QMutex mutex_; QMutex mutex_;
std::atomic_int64_t last_accessed_; std::atomic_int64_t last_accessed_;
TexturePtr cached_texture_;
rational cached_time_;
int cached_divider_;
TexturePtr cached_texture_;
rational cached_time_;
int cached_divider_;
}; };
uint qHash(Decoder::CodecStream stream, uint seed = 0); uint qHash(Decoder::CodecStream stream, uint seed = 0);
+415 -339
View File
@@ -26,492 +26,568 @@
#include "ffmpeg/ffmpegencoder.h" #include "ffmpeg/ffmpegencoder.h"
#include "oiio/oiioencoder.h" #include "oiio/oiioencoder.h"
namespace olive { namespace olive
{
const QRegularExpression Encoder::kImageSequenceContainsDigits = QRegularExpression(QStringLiteral("\\[[#]+\\]")); const QRegularExpression Encoder::kImageSequenceContainsDigits =
const QRegularExpression Encoder::kImageSequenceRemoveDigits = QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]")); QRegularExpression(QStringLiteral("\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceRemoveDigits =
QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
Encoder::Encoder(const EncodingParams &params) : Encoder::Encoder(const EncodingParams &params)
params_(params) : params_(params)
{ {
} }
const EncodingParams &Encoder::params() const const EncodingParams &Encoder::params() const
{ {
return params_; return params_;
} }
QString Encoder::GetFilenameForFrame(const rational &frame) QString Encoder::GetFilenameForFrame(const rational &frame)
{ {
if (params().video_is_image_sequence()) { if (params().video_is_image_sequence()) {
// Transform! // Transform!
int64_t frame_index = Timecode::time_to_timestamp(frame, params().video_params().frame_rate_as_time_base()); int64_t frame_index = Timecode::time_to_timestamp(
int digits = GetImageSequencePlaceholderDigitCount(params().filename()); frame, params().video_params().frame_rate_as_time_base());
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0')); int digits = GetImageSequencePlaceholderDigitCount(params().filename());
QString frame_index_str =
QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
QString f = params_.filename(); QString f = params_.filename();
f.replace(kImageSequenceContainsDigits, frame_index_str); f.replace(kImageSequenceContainsDigits, frame_index_str);
return f; return f;
} else { } else {
// Keep filename // Keep filename
return params_.filename(); return params_.filename();
} }
} }
int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename) int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
{ {
int start = filename.indexOf(kImageSequenceContainsDigits); int start = filename.indexOf(kImageSequenceContainsDigits);
int digit_count = 0; int digit_count = 0;
for (int i=start+1; i<filename.size(); i++) { for (int i = start + 1; i < filename.size(); i++) {
if (filename.at(i) == '#') { if (filename.at(i) == '#') {
digit_count++; digit_count++;
} else { } else {
break; break;
} }
} }
return digit_count; return digit_count;
} }
bool Encoder::FilenameContainsDigitPlaceholder(const QString& filename) bool Encoder::FilenameContainsDigitPlaceholder(const QString &filename)
{ {
return filename.contains(kImageSequenceContainsDigits); return filename.contains(kImageSequenceContainsDigits);
} }
QString Encoder::FilenameRemoveDigitPlaceholder(QString filename) QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
{ {
return filename.remove(kImageSequenceRemoveDigits); return filename.remove(kImageSequenceRemoveDigits);
} }
EncodingParams::EncodingParams() : EncodingParams::EncodingParams()
video_enabled_(false), : video_enabled_(false)
video_bit_rate_(0), , video_bit_rate_(0)
video_min_bit_rate_(0), , video_min_bit_rate_(0)
video_max_bit_rate_(0), , video_max_bit_rate_(0)
video_buffer_size_(0), , video_buffer_size_(0)
video_threads_(0), , video_threads_(0)
video_is_image_sequence_(false), , video_is_image_sequence_(false)
audio_enabled_(false), , audio_enabled_(false)
audio_bit_rate_(0), , audio_bit_rate_(0)
subtitles_enabled_(false), , subtitles_enabled_(false)
subtitles_are_sidecar_(false), , subtitles_are_sidecar_(false)
video_scaling_method_(kStretch), , video_scaling_method_(kStretch)
has_custom_range_(false) , has_custom_range_(false)
{ {
} }
QDir EncodingParams::GetPresetPath() QDir EncodingParams::GetPresetPath()
{ {
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets")); return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("exportpresets"));
} }
QStringList EncodingParams::GetListOfPresets() QStringList EncodingParams::GetListOfPresets()
{ {
QDir d = EncodingParams::GetPresetPath(); QDir d = EncodingParams::GetPresetPath();
return d.entryList(QDir::Files); return d.entryList(QDir::Files);
} }
void EncodingParams::EnableVideo(const VideoParams &video_params, const ExportCodec::Codec &vcodec) void EncodingParams::EnableVideo(const VideoParams &video_params,
const ExportCodec::Codec &vcodec)
{ {
video_enabled_ = true; video_enabled_ = true;
video_params_ = video_params; video_params_ = video_params;
video_codec_ = vcodec; video_codec_ = vcodec;
} }
void EncodingParams::EnableAudio(const AudioParams &audio_params, const ExportCodec::Codec &acodec) void EncodingParams::EnableAudio(const AudioParams &audio_params,
const ExportCodec::Codec &acodec)
{ {
audio_enabled_ = true; audio_enabled_ = true;
audio_params_ = audio_params; audio_params_ = audio_params;
audio_codec_ = acodec; audio_codec_ = acodec;
} }
void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec) void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
{ {
subtitles_enabled_ = true; subtitles_enabled_ = true;
subtitles_codec_ = scodec; subtitles_codec_ = scodec;
} }
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec) void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec)
{ {
subtitles_enabled_ = true; subtitles_enabled_ = true;
subtitles_are_sidecar_ = true; subtitles_are_sidecar_ = true;
subtitle_sidecar_fmt_ = sfmt; subtitle_sidecar_fmt_ = sfmt;
subtitles_codec_ = scodec; subtitles_codec_ = scodec;
} }
void EncodingParams::DisableVideo() void EncodingParams::DisableVideo()
{ {
video_enabled_ = false; video_enabled_ = false;
} }
void EncodingParams::DisableAudio() void EncodingParams::DisableAudio()
{ {
audio_enabled_ = false; audio_enabled_ = false;
} }
void EncodingParams::DisableSubtitles() void EncodingParams::DisableSubtitles()
{ {
subtitles_enabled_ = false; subtitles_enabled_ = false;
} }
bool EncodingParams::Load(QXmlStreamReader *reader) bool EncodingParams::Load(QXmlStreamReader *reader)
{ {
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("export")) { if (reader->name() == QStringLiteral("export")) {
int version = 0; int version = 0;
XMLAttributeLoop(reader, attr) { XMLAttributeLoop(reader, attr)
if (attr.name() == QStringLiteral("version")) { {
version = attr.value().toInt(); if (attr.name() == QStringLiteral("version")) {
} version = attr.value().toInt();
} }
}
switch (version) { switch (version) {
case 1: case 1:
return LoadV1(reader); return LoadV1(reader);
} }
} else { } else {
reader->skipCurrentElement(); reader->skipCurrentElement();
} }
} }
return false; return false;
} }
bool EncodingParams::Load(QIODevice *device) bool EncodingParams::Load(QIODevice *device)
{ {
QXmlStreamReader reader(device); QXmlStreamReader reader(device);
return Load(&reader); return Load(&reader);
} }
void EncodingParams::Save(QIODevice *device) const void EncodingParams::Save(QIODevice *device) const
{ {
QXmlStreamWriter writer(device); QXmlStreamWriter writer(device);
Save(&writer); Save(&writer);
} }
void EncodingParams::Save(QXmlStreamWriter *writer) const void EncodingParams::Save(QXmlStreamWriter *writer) const
{ {
writer->writeStartDocument(); writer->writeStartDocument();
writer->writeStartElement(QStringLiteral("export")); writer->writeStartElement(QStringLiteral("export"));
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion)); writer->writeAttribute(QStringLiteral("version"),
QString::number(kEncoderParamsVersion));
writer->writeTextElement(QStringLiteral("filename"), filename_); writer->writeTextElement(QStringLiteral("filename"), filename_);
writer->writeTextElement(QStringLiteral("format"), QString::number(format_)); writer->writeTextElement(QStringLiteral("format"),
QString::number(format_));
writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_)); writer->writeTextElement(QStringLiteral("range"),
writer->writeTextElement(QStringLiteral("customrangein"), QString::fromStdString(custom_range_.in().toString())); QString::number(has_custom_range_));
writer->writeTextElement(QStringLiteral("customrangeout"), QString::fromStdString(custom_range_.out().toString())); writer->writeTextElement(
QStringLiteral("customrangein"),
QString::fromStdString(custom_range_.in().toString()));
writer->writeTextElement(
QStringLiteral("customrangeout"),
QString::fromStdString(custom_range_.out().toString()));
writer->writeStartElement(QStringLiteral("video")); writer->writeStartElement(QStringLiteral("video"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(video_enabled_)); writer->writeAttribute(QStringLiteral("enabled"),
QString::number(video_enabled_));
if (video_enabled_) { if (video_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_)); writer->writeTextElement(QStringLiteral("codec"),
writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width())); QString::number(video_codec_));
writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height())); writer->writeTextElement(QStringLiteral("width"),
writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format())); QString::number(video_params_.width()));
writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(video_params_.pixel_aspect_ratio().toString())); writer->writeTextElement(QStringLiteral("height"),
writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(video_params_.time_base().toString())); QString::number(video_params_.height()));
writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider())); writer->writeTextElement(QStringLiteral("format"),
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_)); QString::number(video_params_.format()));
writer->writeTextElement(QStringLiteral("minbitrate"), QString::number(video_min_bit_rate_)); writer->writeTextElement(
writer->writeTextElement(QStringLiteral("maxbitrate"), QString::number(video_max_bit_rate_)); QStringLiteral("pixelaspect"),
writer->writeTextElement(QStringLiteral("bufsize"), QString::number(video_buffer_size_)); QString::fromStdString(
writer->writeTextElement(QStringLiteral("threads"), QString::number(video_threads_)); video_params_.pixel_aspect_ratio().toString()));
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_); writer->writeTextElement(
writer->writeTextElement(QStringLiteral("imgseq"), QString::number(video_is_image_sequence_)); QStringLiteral("timebase"),
QString::fromStdString(video_params_.time_base().toString()));
writer->writeTextElement(QStringLiteral("divider"),
QString::number(video_params_.divider()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(video_bit_rate_));
writer->writeTextElement(QStringLiteral("minbitrate"),
QString::number(video_min_bit_rate_));
writer->writeTextElement(QStringLiteral("maxbitrate"),
QString::number(video_max_bit_rate_));
writer->writeTextElement(QStringLiteral("bufsize"),
QString::number(video_buffer_size_));
writer->writeTextElement(QStringLiteral("threads"),
QString::number(video_threads_));
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
writer->writeTextElement(QStringLiteral("imgseq"),
QString::number(video_is_image_sequence_));
writer->writeStartElement(QStringLiteral("color")); writer->writeStartElement(QStringLiteral("color"));
writer->writeTextElement(QStringLiteral("output"), color_transform_.output()); writer->writeTextElement(QStringLiteral("output"),
writer->writeEndElement(); // colortransform color_transform_.output());
writer->writeEndElement(); // colortransform
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_)); writer->writeTextElement(QStringLiteral("vscale"),
QString::number(video_scaling_method_));
if (!video_opts_.isEmpty()) { if (!video_opts_.isEmpty()) {
writer->writeStartElement(QStringLiteral("opts")); writer->writeStartElement(QStringLiteral("opts"));
QHash<QString, QString>::const_iterator i; QHash<QString, QString>::const_iterator i;
for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) { for (i = video_opts_.constBegin(); i != video_opts_.constEnd();
writer->writeStartElement(QStringLiteral("entry")); i++) {
writer->writeStartElement(QStringLiteral("entry"));
writer->writeTextElement(QStringLiteral("key"), i.key()); writer->writeTextElement(QStringLiteral("key"), i.key());
writer->writeTextElement(QStringLiteral("value"), i.value()); writer->writeTextElement(QStringLiteral("value"), i.value());
writer->writeEndElement(); // entry writer->writeEndElement(); // entry
} }
writer->writeEndElement(); // opts writer->writeEndElement(); // opts
} }
} }
writer->writeEndElement(); // video writer->writeEndElement(); // video
writer->writeStartElement(QStringLiteral("audio")); writer->writeStartElement(QStringLiteral("audio"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_)); writer->writeAttribute(QStringLiteral("enabled"),
QString::number(audio_enabled_));
if (audio_enabled_) { if (audio_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_)); writer->writeTextElement(QStringLiteral("codec"),
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate())); QString::number(audio_codec_));
writer->writeTextElement(QStringLiteral("samplerate"),
QString::number(audio_params_.sample_rate()));
writer->writeTextElement(QStringLiteral("channellayout"), writer->writeTextElement(
QString::number(audio_params().channel_layout().u.mask)); QStringLiteral("channellayout"),
writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string())); QString::number(audio_params().channel_layout().u.mask));
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_)); writer->writeTextElement(
} QStringLiteral("format"),
QString::fromStdString(audio_params_.format().to_string()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(audio_bit_rate_));
}
writer->writeStartElement(QStringLiteral("subtitles")); writer->writeStartElement(QStringLiteral("subtitles"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(subtitles_enabled_)); writer->writeAttribute(QStringLiteral("enabled"),
QString::number(subtitles_enabled_));
if (subtitles_enabled_) { if (subtitles_enabled_) {
writer->writeTextElement(QStringLiteral("sidecar"), QString::number(subtitles_are_sidecar_)); writer->writeTextElement(QStringLiteral("sidecar"),
writer->writeTextElement(QStringLiteral("sidecarformat"), QString::number(subtitle_sidecar_fmt_)); QString::number(subtitles_are_sidecar_));
writer->writeTextElement(QStringLiteral("sidecarformat"),
QString::number(subtitle_sidecar_fmt_));
writer->writeTextElement(QStringLiteral("codec"), QString::number(subtitles_codec_)); writer->writeTextElement(QStringLiteral("codec"),
} QString::number(subtitles_codec_));
}
writer->writeEndElement(); // subtitles writer->writeEndElement(); // subtitles
writer->writeEndElement(); // audio writer->writeEndElement(); // audio
writer->writeEndElement(); // export writer->writeEndElement(); // export
writer->writeEndDocument(); writer->writeEndDocument();
} }
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params) Encoder *Encoder::CreateFromID(Type id, const EncodingParams &params)
{ {
switch (id) { switch (id) {
case kEncoderTypeNone: case kEncoderTypeNone:
break; break;
case kEncoderTypeFFmpeg: case kEncoderTypeFFmpeg:
return new FFmpegEncoder(params); return new FFmpegEncoder(params);
case kEncoderTypeOIIO: case kEncoderTypeOIIO:
return new OIIOEncoder(params); return new OIIOEncoder(params);
} }
return nullptr; return nullptr;
} }
Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f) Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
{ {
switch (f) { switch (f) {
case ExportFormat::kFormatDNxHD: case ExportFormat::kFormatDNxHD:
case ExportFormat::kFormatMatroska: case ExportFormat::kFormatMatroska:
case ExportFormat::kFormatQuickTime: case ExportFormat::kFormatQuickTime:
case ExportFormat::kFormatMPEG4Video: case ExportFormat::kFormatMPEG4Video:
case ExportFormat::kFormatMPEG4Audio: case ExportFormat::kFormatMPEG4Audio:
case ExportFormat::kFormatWAV: case ExportFormat::kFormatWAV:
case ExportFormat::kFormatAIFF: case ExportFormat::kFormatAIFF:
case ExportFormat::kFormatMP3: case ExportFormat::kFormatMP3:
case ExportFormat::kFormatFLAC: case ExportFormat::kFormatFLAC:
case ExportFormat::kFormatOgg: case ExportFormat::kFormatOgg:
case ExportFormat::kFormatWebM: case ExportFormat::kFormatWebM:
case ExportFormat::kFormatSRT: case ExportFormat::kFormatSRT:
return kEncoderTypeFFmpeg; return kEncoderTypeFFmpeg;
case ExportFormat::kFormatOpenEXR: case ExportFormat::kFormatOpenEXR:
case ExportFormat::kFormatPNG: case ExportFormat::kFormatPNG:
case ExportFormat::kFormatTIFF: case ExportFormat::kFormatTIFF:
return kEncoderTypeOIIO; return kEncoderTypeOIIO;
case ExportFormat::kFormatCount: case ExportFormat::kFormatCount:
break; break;
} }
return kEncoderTypeNone; return kEncoderTypeNone;
} }
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams &params) Encoder *Encoder::CreateFromFormat(ExportFormat::Format f,
const EncodingParams &params)
{ {
return CreateFromID(GetTypeFromFormat(f), params); return CreateFromID(GetTypeFromFormat(f), params);
} }
Encoder *Encoder::CreateFromParams(const EncodingParams &params) Encoder *Encoder::CreateFromParams(const EncodingParams &params)
{ {
return CreateFromFormat(params.format(), params); return CreateFromFormat(params.format(), params);
} }
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
{ {
return QStringList(); return QStringList();
} }
std::vector<SampleFormat> Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const std::vector<SampleFormat>
Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
{ {
return std::vector<SampleFormat>(); return std::vector<SampleFormat>();
} }
QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method, QMatrix4x4
int source_width, int source_height, EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
int dest_width, int dest_height) int source_width, int source_height,
int dest_width, int dest_height)
{ {
QMatrix4x4 preview_matrix; QMatrix4x4 preview_matrix;
if (method == EncodingParams::kStretch) { if (method == EncodingParams::kStretch) {
return preview_matrix; return preview_matrix;
} }
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height); float export_ar =
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height); static_cast<float>(dest_width) / static_cast<float>(dest_height);
float source_ar =
static_cast<float>(source_width) / static_cast<float>(source_height);
if (qFuzzyCompare(export_ar, source_ar)) { if (qFuzzyCompare(export_ar, source_ar)) {
return preview_matrix; return preview_matrix;
} }
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) { if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
preview_matrix.scale(source_ar / export_ar, 1.0F); preview_matrix.scale(source_ar / export_ar, 1.0F);
} else { } else {
preview_matrix.scale(1.0F, export_ar / source_ar); preview_matrix.scale(1.0F, export_ar / source_ar);
} }
return preview_matrix; return preview_matrix;
} }
bool EncodingParams::LoadV1(QXmlStreamReader *reader) bool EncodingParams::LoadV1(QXmlStreamReader *reader)
{ {
rational custom_range_in, custom_range_out; rational custom_range_in, custom_range_out;
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("filename")) { if (reader->name() == QStringLiteral("filename")) {
filename_ = reader->readElementText(); filename_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("format")) { } else if (reader->name() == QStringLiteral("format")) {
format_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt()); format_ = static_cast<ExportFormat::Format>(
} else if (reader->name() == QStringLiteral("range")) { reader->readElementText().toInt());
has_custom_range_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("range")) {
} else if (reader->name() == QStringLiteral("customrangein")) { has_custom_range_ = reader->readElementText().toInt();
custom_range_in = rational::fromString(reader->readElementText().toStdString()); } else if (reader->name() == QStringLiteral("customrangein")) {
} else if (reader->name() == QStringLiteral("customrangeout")) { custom_range_in =
custom_range_out = rational::fromString(reader->readElementText().toStdString()); rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("video")) { } else if (reader->name() == QStringLiteral("customrangeout")) {
XMLAttributeLoop(reader, attr) { custom_range_out =
if (attr.name() == QStringLiteral("enabled")) { rational::fromString(reader->readElementText().toStdString());
video_enabled_ = attr.value().toInt(); } else if (reader->name() == QStringLiteral("video")) {
} XMLAttributeLoop(reader, attr)
} {
if (attr.name() == QStringLiteral("enabled")) {
video_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) { if (reader->name() == QStringLiteral("codec")) {
video_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt()); video_codec_ = static_cast<ExportCodec::Codec>(
} else if (reader->name() == QStringLiteral("width")) { reader->readElementText().toInt());
video_params_.set_width(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("width")) {
} else if (reader->name() == QStringLiteral("height")) { video_params_.set_width(reader->readElementText().toInt());
video_params_.set_height(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("height")) {
} else if (reader->name() == QStringLiteral("format")) { video_params_.set_height(reader->readElementText().toInt());
video_params_.set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt())); } else if (reader->name() == QStringLiteral("format")) {
} else if (reader->name() == QStringLiteral("pixelaspect")) { video_params_.set_format(static_cast<PixelFormat::Format>(
video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString())); reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("timebase")) { } else if (reader->name() == QStringLiteral("pixelaspect")) {
video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString())); video_params_.set_pixel_aspect_ratio(rational::fromString(
} else if (reader->name() == QStringLiteral("divider")) { reader->readElementText().toStdString()));
video_params_.set_divider(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("timebase")) {
} else if (reader->name() == QStringLiteral("bitrate")) { video_params_.set_time_base(rational::fromString(
video_bit_rate_ = reader->readElementText().toLongLong(); reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("minbitrate")) { } else if (reader->name() == QStringLiteral("divider")) {
video_min_bit_rate_ = reader->readElementText().toLongLong(); video_params_.set_divider(
} else if (reader->name() == QStringLiteral("maxbitrate")) { reader->readElementText().toInt());
video_max_bit_rate_ = reader->readElementText().toLongLong(); } else if (reader->name() == QStringLiteral("bitrate")) {
} else if (reader->name() == QStringLiteral("bufsize")) { video_bit_rate_ = reader->readElementText().toLongLong();
video_buffer_size_ = reader->readElementText().toLongLong(); } else if (reader->name() == QStringLiteral("minbitrate")) {
} else if (reader->name() == QStringLiteral("threads")) { video_min_bit_rate_ =
video_threads_ = reader->readElementText().toInt(); reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("pixfmt")) { } else if (reader->name() == QStringLiteral("maxbitrate")) {
video_pix_fmt_ = reader->readElementText(); video_max_bit_rate_ =
} else if (reader->name() == QStringLiteral("imgseq")) { reader->readElementText().toLongLong();
video_is_image_sequence_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("bufsize")) {
} else if (reader->name() == QStringLiteral("color")) { video_buffer_size_ = reader->readElementText().toLongLong();
while (XMLReadNextStartElement(reader)) { } else if (reader->name() == QStringLiteral("threads")) {
if (reader->name() == QStringLiteral("output")) { video_threads_ = reader->readElementText().toInt();
color_transform_ = reader->readElementText(); } else if (reader->name() == QStringLiteral("pixfmt")) {
} else { video_pix_fmt_ = reader->readElementText();
reader->skipCurrentElement(); } else if (reader->name() == QStringLiteral("imgseq")) {
} video_is_image_sequence_ =
} reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("vscale")) { } else if (reader->name() == QStringLiteral("color")) {
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt()); while (XMLReadNextStartElement(reader)) {
} else if (reader->name() == QStringLiteral("opts")) { if (reader->name() == QStringLiteral("output")) {
while (XMLReadNextStartElement(reader)) { color_transform_ = reader->readElementText();
if (reader->name() == QStringLiteral("entry")) { } else {
QString key, value; reader->skipCurrentElement();
while (XMLReadNextStartElement(reader)) { }
if (reader->name() == QStringLiteral("key")) { }
key = reader->readElementText(); } else if (reader->name() == QStringLiteral("vscale")) {
} else if (reader->name() == QStringLiteral("value")) { video_scaling_method_ = static_cast<VideoScalingMethod>(
value = reader->readElementText(); reader->readElementText().toInt());
} else { } else if (reader->name() == QStringLiteral("opts")) {
reader->skipCurrentElement(); while (XMLReadNextStartElement(reader)) {
} if (reader->name() == QStringLiteral("entry")) {
} QString key, value;
set_video_option(key, value); while (XMLReadNextStartElement(reader)) {
} else { if (reader->name() == QStringLiteral("key")) {
reader->skipCurrentElement(); key = reader->readElementText();
} } else if (reader->name() ==
} QStringLiteral("value")) {
} else { value = reader->readElementText();
reader->skipCurrentElement(); } else {
} reader->skipCurrentElement();
} }
}
set_video_option(key, value);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio // HACK: Resolve bug where I forgot to serialize pixel aspect ratio
if (video_params_.pixel_aspect_ratio().isNull()) { if (video_params_.pixel_aspect_ratio().isNull()) {
video_params_.set_pixel_aspect_ratio(1); video_params_.set_pixel_aspect_ratio(1);
} }
} else if (reader->name() == QStringLiteral("audio")) { } else if (reader->name() == QStringLiteral("audio")) {
XMLAttributeLoop(reader, attr) { XMLAttributeLoop(reader, attr)
if (attr.name() == QStringLiteral("enabled")) { {
audio_enabled_ = attr.value().toInt(); if (attr.name() == QStringLiteral("enabled")) {
} audio_enabled_ = attr.value().toInt();
} }
}
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) { if (reader->name() == QStringLiteral("codec")) {
audio_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt()); audio_codec_ = static_cast<ExportCodec::Codec>(
} else if (reader->name() == QStringLiteral("samplerate")) { reader->readElementText().toInt());
audio_params_.set_sample_rate(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("samplerate")) {
} else if (reader->name() == QStringLiteral("channellayout")) { audio_params_.set_sample_rate(
AVChannelLayout av_channel_layout; reader->readElementText().toInt());
av_channel_layout_from_mask(&av_channel_layout, reader->readElementText().toLongLong()); } else if (reader->name() == QStringLiteral("channellayout")) {
audio_params_.set_channel_layout(av_channel_layout); AVChannelLayout av_channel_layout;
av_channel_layout_uninit(&av_channel_layout); av_channel_layout_from_mask(
} else if (reader->name() == QStringLiteral("format")) { &av_channel_layout,
audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString())); reader->readElementText().toLongLong());
} else if (reader->name() == QStringLiteral("bitrate")) { audio_params_.set_channel_layout(av_channel_layout);
audio_bit_rate_ = reader->readElementText().toLongLong(); av_channel_layout_uninit(&av_channel_layout);
} else { } else if (reader->name() == QStringLiteral("format")) {
reader->skipCurrentElement(); audio_params_.set_format(SampleFormat::from_string(
} reader->readElementText().toStdString()));
} } else if (reader->name() == QStringLiteral("bitrate")) {
audio_bit_rate_ = reader->readElementText().toLongLong();
} else {
reader->skipCurrentElement();
}
}
// HACK: Resolve bug where I forgot to serialize the audio bit rate // HACK: Resolve bug where I forgot to serialize the audio bit rate
if (!audio_bit_rate_) { if (!audio_bit_rate_) {
audio_bit_rate_ = 320000; audio_bit_rate_ = 320000;
} }
} else if (reader->name() == QStringLiteral("subtitles")) { } else if (reader->name() == QStringLiteral("subtitles")) {
XMLAttributeLoop(reader, attr) { XMLAttributeLoop(reader, attr)
if (attr.name() == QStringLiteral("enabled")) { {
subtitles_enabled_ = attr.value().toInt(); if (attr.name() == QStringLiteral("enabled")) {
} subtitles_enabled_ = attr.value().toInt();
} }
}
while (XMLReadNextStartElement(reader)) { while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sidecar")) { if (reader->name() == QStringLiteral("sidecar")) {
subtitles_are_sidecar_ = reader->readElementText().toInt(); subtitles_are_sidecar_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("sidecarformat")) { } else if (reader->name() == QStringLiteral("sidecarformat")) {
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt()); subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(
} else if (reader->name() == QStringLiteral("codec")) { reader->readElementText().toInt());
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("codec")) {
} else { subtitles_codec_ = static_cast<ExportCodec::Codec>(
reader->skipCurrentElement(); reader->readElementText().toInt());
} } else {
} reader->skipCurrentElement();
} else { }
reader->skipCurrentElement(); }
} } else {
} reader->skipCurrentElement();
}
}
return true; return true;
} }
} }
+270 -148
View File
@@ -34,218 +34,340 @@
#include "render/subtitleparams.h" #include "render/subtitleparams.h"
#include "render/videoparams.h" #include "render/videoparams.h"
//这个代码也许是导出编码视频用的? //这个代码也许是导出编码视频用的?
namespace olive { namespace olive
{
class Encoder; class Encoder;
using EncoderPtr = std::shared_ptr<Encoder>; using EncoderPtr = std::shared_ptr<Encoder>;
class EncodingParams class EncodingParams {
{
public: public:
enum VideoScalingMethod { enum VideoScalingMethod { kFit, kStretch, kCrop };
kFit,
kStretch,
kCrop
};
EncodingParams(); EncodingParams();
static QDir GetPresetPath(); static QDir GetPresetPath();
static QStringList GetListOfPresets(); static QStringList GetListOfPresets();
bool IsValid() const bool IsValid() const
{ {
return video_enabled_ || audio_enabled_ || subtitles_enabled_; return video_enabled_ || audio_enabled_ || subtitles_enabled_;
} }
void SetFilename(const QString& filename) { filename_ = filename; } void SetFilename(const QString &filename)
{
filename_ = filename;
}
void EnableVideo(const VideoParams& video_params, const ExportCodec::Codec& vcodec); void EnableVideo(const VideoParams &video_params,
void EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec); const ExportCodec::Codec &vcodec);
void EnableSubtitles(const ExportCodec::Codec &scodec); void EnableAudio(const AudioParams &audio_params,
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec); const ExportCodec::Codec &acodec);
void EnableSubtitles(const ExportCodec::Codec &scodec);
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec);
void DisableVideo(); void DisableVideo();
void DisableAudio(); void DisableAudio();
void DisableSubtitles(); void DisableSubtitles();
const ExportFormat::Format &format() const { return format_; } const ExportFormat::Format &format() const
void set_format(const ExportFormat::Format &format) { format_ = format; } {
return format_;
}
void set_format(const ExportFormat::Format &format)
{
format_ = format;
}
void set_video_option(const QString& key, const QString& value) { video_opts_.insert(key, value); } void set_video_option(const QString &key, const QString &value)
void set_video_bit_rate(const int64_t& rate) { video_bit_rate_ = rate; } {
void set_video_min_bit_rate(const int64_t& rate) { video_min_bit_rate_ = rate; } video_opts_.insert(key, value);
void set_video_max_bit_rate(const int64_t& rate) { video_max_bit_rate_ = rate; } }
void set_video_buffer_size(const int64_t& sz) { video_buffer_size_ = sz; } void set_video_bit_rate(const int64_t &rate)
void set_video_threads(const int& threads) { video_threads_ = threads; } {
void set_video_pix_fmt(const QString& s) { video_pix_fmt_ = s; } video_bit_rate_ = rate;
void set_video_is_image_sequence(bool s) { video_is_image_sequence_ = s; } }
void set_color_transform(const ColorTransform& color_transform) { color_transform_ = color_transform; } void set_video_min_bit_rate(const int64_t &rate)
{
video_min_bit_rate_ = rate;
}
void set_video_max_bit_rate(const int64_t &rate)
{
video_max_bit_rate_ = rate;
}
void set_video_buffer_size(const int64_t &sz)
{
video_buffer_size_ = sz;
}
void set_video_threads(const int &threads)
{
video_threads_ = threads;
}
void set_video_pix_fmt(const QString &s)
{
video_pix_fmt_ = s;
}
void set_video_is_image_sequence(bool s)
{
video_is_image_sequence_ = s;
}
void set_color_transform(const ColorTransform &color_transform)
{
color_transform_ = color_transform;
}
const QString& filename() const { return filename_; } const QString &filename() const
{
return filename_;
}
bool video_enabled() const { return video_enabled_; } bool video_enabled() const
const ExportCodec::Codec& video_codec() const { return video_codec_; } {
const VideoParams& video_params() const { return video_params_; } return video_enabled_;
const QHash<QString, QString>& video_opts() const { return video_opts_; } }
QString video_option(const QString &key) const { return video_opts_.value(key); } const ExportCodec::Codec &video_codec() const
bool has_video_opt(const QString &key) const { return video_opts_.contains(key); } {
const int64_t& video_bit_rate() const { return video_bit_rate_; } return video_codec_;
const int64_t& video_min_bit_rate() const { return video_min_bit_rate_; } }
const int64_t& video_max_bit_rate() const { return video_max_bit_rate_; } const VideoParams &video_params() const
const int64_t& video_buffer_size() const { return video_buffer_size_; } {
const int& video_threads() const { return video_threads_; } return video_params_;
const QString& video_pix_fmt() const { return video_pix_fmt_; } }
bool video_is_image_sequence() const { return video_is_image_sequence_; } const QHash<QString, QString> &video_opts() const
const ColorTransform& color_transform() const { return color_transform_; } {
return video_opts_;
}
QString video_option(const QString &key) const
{
return video_opts_.value(key);
}
bool has_video_opt(const QString &key) const
{
return video_opts_.contains(key);
}
const int64_t &video_bit_rate() const
{
return video_bit_rate_;
}
const int64_t &video_min_bit_rate() const
{
return video_min_bit_rate_;
}
const int64_t &video_max_bit_rate() const
{
return video_max_bit_rate_;
}
const int64_t &video_buffer_size() const
{
return video_buffer_size_;
}
const int &video_threads() const
{
return video_threads_;
}
const QString &video_pix_fmt() const
{
return video_pix_fmt_;
}
bool video_is_image_sequence() const
{
return video_is_image_sequence_;
}
const ColorTransform &color_transform() const
{
return color_transform_;
}
bool audio_enabled() const { return audio_enabled_; } bool audio_enabled() const
const ExportCodec::Codec &audio_codec() const { return audio_codec_; } {
const AudioParams& audio_params() const { return audio_params_; } return audio_enabled_;
const int64_t& audio_bit_rate() const { return audio_bit_rate_; } }
const ExportCodec::Codec &audio_codec() const
{
return audio_codec_;
}
const AudioParams &audio_params() const
{
return audio_params_;
}
const int64_t &audio_bit_rate() const
{
return audio_bit_rate_;
}
void set_audio_bit_rate(const int64_t& b) { audio_bit_rate_ = b; } void set_audio_bit_rate(const int64_t &b)
{
audio_bit_rate_ = b;
}
bool subtitles_enabled() const { return subtitles_enabled_; } bool subtitles_enabled() const
bool subtitles_are_sidecar() const { return subtitles_are_sidecar_; } {
ExportFormat::Format subtitle_sidecar_fmt() const { return subtitle_sidecar_fmt_; } return subtitles_enabled_;
ExportCodec::Codec subtitles_codec() const { return subtitles_codec_; } }
bool subtitles_are_sidecar() const
{
return subtitles_are_sidecar_;
}
ExportFormat::Format subtitle_sidecar_fmt() const
{
return subtitle_sidecar_fmt_;
}
ExportCodec::Codec subtitles_codec() const
{
return subtitles_codec_;
}
const rational& GetExportLength() const { return export_length_; } const rational &GetExportLength() const
void SetExportLength(const rational& export_length) { export_length_ = export_length; } {
return export_length_;
}
void SetExportLength(const rational &export_length)
{
export_length_ = export_length;
}
bool Load(QIODevice *device); bool Load(QIODevice *device);
bool Load(QXmlStreamReader *reader); bool Load(QXmlStreamReader *reader);
void Save(QIODevice *device) const; void Save(QIODevice *device) const;
void Save(QXmlStreamWriter* writer) const; void Save(QXmlStreamWriter *writer) const;
bool has_custom_range() const { return has_custom_range_; } bool has_custom_range() const
const TimeRange& custom_range() const { return custom_range_; } {
void set_custom_range(const TimeRange& custom_range) return has_custom_range_;
{ }
has_custom_range_ = true; const TimeRange &custom_range() const
custom_range_ = custom_range; {
} return custom_range_;
}
void set_custom_range(const TimeRange &custom_range)
{
has_custom_range_ = true;
custom_range_ = custom_range;
}
const VideoScalingMethod& video_scaling_method() const { return video_scaling_method_; } const VideoScalingMethod &video_scaling_method() const
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method) { video_scaling_method_ = video_scaling_method; } {
return video_scaling_method_;
}
void
set_video_scaling_method(const VideoScalingMethod &video_scaling_method)
{
video_scaling_method_ = video_scaling_method;
}
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method, static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
int source_width, int source_height, int source_width, int source_height,
int dest_width, int dest_height); int dest_width, int dest_height);
private: private:
static const int kEncoderParamsVersion = 1; static const int kEncoderParamsVersion = 1;
bool LoadV1(QXmlStreamReader *reader); bool LoadV1(QXmlStreamReader *reader);
QString filename_; QString filename_;
ExportFormat::Format format_; ExportFormat::Format format_;
bool video_enabled_; bool video_enabled_;
ExportCodec::Codec video_codec_; ExportCodec::Codec video_codec_;
VideoParams video_params_; VideoParams video_params_;
QHash<QString, QString> video_opts_; QHash<QString, QString> video_opts_;
int64_t video_bit_rate_; int64_t video_bit_rate_;
int64_t video_min_bit_rate_; int64_t video_min_bit_rate_;
int64_t video_max_bit_rate_; int64_t video_max_bit_rate_;
int64_t video_buffer_size_; int64_t video_buffer_size_;
int video_threads_; int video_threads_;
QString video_pix_fmt_; QString video_pix_fmt_;
bool video_is_image_sequence_; bool video_is_image_sequence_;
ColorTransform color_transform_; ColorTransform color_transform_;
bool audio_enabled_; bool audio_enabled_;
ExportCodec::Codec audio_codec_; ExportCodec::Codec audio_codec_;
AudioParams audio_params_; AudioParams audio_params_;
int64_t audio_bit_rate_; int64_t audio_bit_rate_;
bool subtitles_enabled_; bool subtitles_enabled_;
bool subtitles_are_sidecar_; bool subtitles_are_sidecar_;
ExportFormat::Format subtitle_sidecar_fmt_; ExportFormat::Format subtitle_sidecar_fmt_;
ExportCodec::Codec subtitles_codec_; ExportCodec::Codec subtitles_codec_;
rational export_length_; rational export_length_;
VideoScalingMethod video_scaling_method_; VideoScalingMethod video_scaling_method_;
bool has_custom_range_;
TimeRange custom_range_;
bool has_custom_range_;
TimeRange custom_range_;
}; };
class Encoder : public QObject class Encoder : public QObject {
{ Q_OBJECT
Q_OBJECT
public: public:
Encoder(const EncodingParams& params); Encoder(const EncodingParams &params);
enum Type { enum Type { kEncoderTypeNone = -1, kEncoderTypeFFmpeg, kEncoderTypeOIIO };
kEncoderTypeNone = -1,
kEncoderTypeFFmpeg,
kEncoderTypeOIIO
};
/** /**
* @brief Create a Encoder instance using a Encoder ID * @brief Create a Encoder instance using a Encoder ID
* *
* @return * @return
* *
* A Encoder instance or nullptr if a Decoder with this ID does not exist * A Encoder instance or nullptr if a Decoder with this ID does not exist
*/ */
static Encoder *CreateFromID(Type id, const EncodingParams &params); static Encoder *CreateFromID(Type id, const EncodingParams &params);
static Type GetTypeFromFormat(ExportFormat::Format f); static Type GetTypeFromFormat(ExportFormat::Format f);
static Encoder *CreateFromFormat(ExportFormat::Format f, const EncodingParams &params); static Encoder *CreateFromFormat(ExportFormat::Format f,
const EncodingParams &params);
static Encoder *CreateFromParams(const EncodingParams &params); static Encoder *CreateFromParams(const EncodingParams &params);
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const; virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const; virtual std::vector<SampleFormat>
GetSampleFormatsForCodec(ExportCodec::Codec c) const;
const EncodingParams& params() const; const EncodingParams &params() const;
virtual PixelFormat GetDesiredPixelFormat() const virtual PixelFormat GetDesiredPixelFormat() const
{ {
return PixelFormat::INVALID; return PixelFormat::INVALID;
} }
const QString& GetError() const const QString &GetError() const
{ {
return error_; return error_;
} }
QString GetFilenameForFrame(const rational& frame); QString GetFilenameForFrame(const rational &frame);
static int GetImageSequencePlaceholderDigitCount(const QString& filename); static int GetImageSequencePlaceholderDigitCount(const QString &filename);
static bool FilenameContainsDigitPlaceholder(const QString &filename); static bool FilenameContainsDigitPlaceholder(const QString &filename);
static QString FilenameRemoveDigitPlaceholder(QString filename); static QString FilenameRemoveDigitPlaceholder(QString filename);
static const QRegularExpression kImageSequenceContainsDigits; static const QRegularExpression kImageSequenceContainsDigits;
static const QRegularExpression kImageSequenceRemoveDigits; static const QRegularExpression kImageSequenceRemoveDigits;
public slots: public slots:
virtual bool Open() = 0; virtual bool Open() = 0;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) = 0; virtual bool WriteFrame(olive::FramePtr frame,
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0; olive::core::rational time) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0; virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
virtual void Close() = 0; virtual void Close() = 0;
protected: protected:
void SetError(const QString& err) void SetError(const QString &err)
{ {
error_ = err; error_ = err;
} }
private: private:
EncodingParams params_; EncodingParams params_;
QString error_;
QString error_;
}; };
} }
+96 -95
View File
@@ -25,115 +25,116 @@ extern "C" {
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
} }
namespace olive { namespace olive
{
QString ExportCodec::GetCodecName(ExportCodec::Codec c) QString ExportCodec::GetCodecName(ExportCodec::Codec c)
{ {
switch (c) { switch (c) {
case kCodecDNxHD: case kCodecDNxHD:
return tr("DNxHD"); return tr("DNxHD");
case kCodecH264: case kCodecH264:
return tr("H.264"); return tr("H.264");
case kCodecH264rgb: case kCodecH264rgb:
return tr("H.264 RGB"); return tr("H.264 RGB");
case kCodecH265: case kCodecH265:
return tr("H.265"); return tr("H.265");
case kCodecOpenEXR: case kCodecOpenEXR:
return tr("OpenEXR"); return tr("OpenEXR");
case kCodecPNG: case kCodecPNG:
return tr("PNG"); return tr("PNG");
case kCodecProRes: case kCodecProRes:
return tr("ProRes"); return tr("ProRes");
case kCodecCineform: case kCodecCineform:
return tr("Cineform"); return tr("Cineform");
case kCodecTIFF: case kCodecTIFF:
return tr("TIFF"); return tr("TIFF");
case kCodecMP2: case kCodecMP2:
return tr("MP2"); return tr("MP2");
case kCodecMP3: case kCodecMP3:
return tr("MP3"); return tr("MP3");
case kCodecAAC: case kCodecAAC:
return tr("AAC"); return tr("AAC");
case kCodecPCM: case kCodecPCM:
return tr("PCM (Uncompressed)"); return tr("PCM (Uncompressed)");
case kCodecFLAC: case kCodecFLAC:
return tr("FLAC"); return tr("FLAC");
case kCodecOpus: case kCodecOpus:
return tr("Opus"); return tr("Opus");
case kCodecVorbis: case kCodecVorbis:
return tr("Vorbis"); return tr("Vorbis");
case kCodecVP9: case kCodecVP9:
return tr("VP9"); return tr("VP9");
case kCodecAV1: case kCodecAV1:
return tr("AV1"); return tr("AV1");
case kCodecSRT: case kCodecSRT:
return tr("SubRip SRT"); return tr("SubRip SRT");
case kCodecCount: case kCodecCount:
break; break;
} }
return tr("Unknown"); return tr("Unknown");
} }
bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c) bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
{ {
switch (c) { switch (c) {
case kCodecDNxHD: case kCodecDNxHD:
case kCodecH264: case kCodecH264:
case kCodecH264rgb: case kCodecH264rgb:
case kCodecH265: case kCodecH265:
case kCodecProRes: case kCodecProRes:
case kCodecCineform: case kCodecCineform:
case kCodecMP2: case kCodecMP2:
case kCodecMP3: case kCodecMP3:
case kCodecAAC: case kCodecAAC:
case kCodecPCM: case kCodecPCM:
case kCodecVorbis: case kCodecVorbis:
case kCodecOpus: case kCodecOpus:
case kCodecFLAC: case kCodecFLAC:
case kCodecVP9: case kCodecVP9:
case kCodecAV1: case kCodecAV1:
case kCodecSRT: case kCodecSRT:
return false; return false;
case kCodecOpenEXR: case kCodecOpenEXR:
case kCodecPNG: case kCodecPNG:
case kCodecTIFF: case kCodecTIFF:
return true; return true;
case kCodecCount: case kCodecCount:
break; break;
} }
return false; return false;
} }
bool ExportCodec::IsCodecLossless(Codec c) bool ExportCodec::IsCodecLossless(Codec c)
{ {
switch (c) { switch (c) {
case kCodecPCM: case kCodecPCM:
case kCodecFLAC: case kCodecFLAC:
return true; return true;
case kCodecDNxHD: case kCodecDNxHD:
case kCodecH264: case kCodecH264:
case kCodecH264rgb: case kCodecH264rgb:
case kCodecH265: case kCodecH265:
case kCodecProRes: case kCodecProRes:
case kCodecCineform: case kCodecCineform:
case kCodecMP2: case kCodecMP2:
case kCodecMP3: case kCodecMP3:
case kCodecAAC: case kCodecAAC:
case kCodecVorbis: case kCodecVorbis:
case kCodecOpus: case kCodecOpus:
case kCodecVP9: case kCodecVP9:
case kCodecAV1: case kCodecAV1:
case kCodecSRT: case kCodecSRT:
case kCodecOpenEXR: case kCodecOpenEXR:
case kCodecPNG: case kCodecPNG:
case kCodecTIFF: case kCodecTIFF:
case kCodecCount: case kCodecCount:
break; break;
} }
return false; return false;
} }
} }
+30 -31
View File
@@ -27,43 +27,42 @@
#include "common/define.h" #include "common/define.h"
#include "render/subtitleparams.h" #include "render/subtitleparams.h"
namespace olive { namespace olive
class ExportCodec : public QObject
{ {
Q_OBJECT
class ExportCodec : public QObject {
Q_OBJECT
public: public:
// Only append to this list (never insert) because indexes are used in serialized files // Only append to this list (never insert) because indexes are used in serialized files
enum Codec { enum Codec {
kCodecDNxHD, kCodecDNxHD,
kCodecH264, kCodecH264,
kCodecH264rgb, kCodecH264rgb,
kCodecH265, kCodecH265,
kCodecOpenEXR, kCodecOpenEXR,
kCodecPNG, kCodecPNG,
kCodecProRes, kCodecProRes,
kCodecCineform, kCodecCineform,
kCodecTIFF, kCodecTIFF,
kCodecVP9, kCodecVP9,
kCodecMP2, kCodecMP2,
kCodecMP3, kCodecMP3,
kCodecAAC, kCodecAAC,
kCodecPCM, kCodecPCM,
kCodecOpus, kCodecOpus,
kCodecVorbis, kCodecVorbis,
kCodecFLAC, kCodecFLAC,
kCodecSRT, kCodecSRT,
kCodecAV1, kCodecAV1,
kCodecCount kCodecCount
}; };
static QString GetCodecName(Codec c); static QString GetCodecName(Codec c);
static bool IsCodecAStillImage(Codec c); static bool IsCodecAStillImage(Codec c);
static bool IsCodecLossless(Codec c);
static bool IsCodecLossless(Codec c);
}; };
} }
+184 -171
View File
@@ -22,214 +22,227 @@
#include "encoder.h" #include "encoder.h"
namespace olive { namespace olive
{
QString ExportFormat::GetName(olive::ExportFormat::Format f) QString ExportFormat::GetName(olive::ExportFormat::Format f)
{ {
switch (f) { switch (f) {
case kFormatDNxHD: case kFormatDNxHD:
return tr("DNxHD"); return tr("DNxHD");
case kFormatMatroska: case kFormatMatroska:
return tr("Matroska Video"); return tr("Matroska Video");
case kFormatMPEG4Video: case kFormatMPEG4Video:
return tr("MPEG-4 Video"); return tr("MPEG-4 Video");
case kFormatMPEG4Audio: case kFormatMPEG4Audio:
return tr("MPEG-4 Audio"); return tr("MPEG-4 Audio");
case kFormatOpenEXR: case kFormatOpenEXR:
return tr("OpenEXR"); return tr("OpenEXR");
case kFormatPNG: case kFormatPNG:
return tr("PNG"); return tr("PNG");
case kFormatTIFF: case kFormatTIFF:
return tr("TIFF"); return tr("TIFF");
case kFormatQuickTime: case kFormatQuickTime:
return tr("QuickTime"); return tr("QuickTime");
case kFormatWAV: case kFormatWAV:
return tr("Wave Audio"); return tr("Wave Audio");
case kFormatAIFF: case kFormatAIFF:
return tr("AIFF"); return tr("AIFF");
case kFormatMP3: case kFormatMP3:
return tr("MP3"); return tr("MP3");
case kFormatFLAC: case kFormatFLAC:
return tr("FLAC"); return tr("FLAC");
case kFormatOgg: case kFormatOgg:
return tr("Ogg"); return tr("Ogg");
case kFormatWebM: case kFormatWebM:
return tr("WebM"); return tr("WebM");
case kFormatSRT: case kFormatSRT:
return tr("SubRip SRT"); return tr("SubRip SRT");
case kFormatCount: case kFormatCount:
break; break;
} }
return tr("Unknown"); return tr("Unknown");
} }
QString ExportFormat::GetExtension(ExportFormat::Format f) QString ExportFormat::GetExtension(ExportFormat::Format f)
{ {
switch (f) { switch (f) {
case kFormatDNxHD: case kFormatDNxHD:
return QStringLiteral("mxf"); return QStringLiteral("mxf");
case kFormatMatroska: case kFormatMatroska:
return QStringLiteral("mkv"); return QStringLiteral("mkv");
case kFormatMPEG4Video: case kFormatMPEG4Video:
return QStringLiteral("mp4"); return QStringLiteral("mp4");
case kFormatMPEG4Audio: case kFormatMPEG4Audio:
return QStringLiteral("m4a"); return QStringLiteral("m4a");
case kFormatOpenEXR: case kFormatOpenEXR:
return QStringLiteral("exr"); return QStringLiteral("exr");
case kFormatPNG: case kFormatPNG:
return QStringLiteral("png"); return QStringLiteral("png");
case kFormatTIFF: case kFormatTIFF:
return QStringLiteral("tiff"); return QStringLiteral("tiff");
case kFormatQuickTime: case kFormatQuickTime:
return QStringLiteral("mov"); return QStringLiteral("mov");
case kFormatWAV: case kFormatWAV:
return QStringLiteral("wav"); return QStringLiteral("wav");
case kFormatAIFF: case kFormatAIFF:
return QStringLiteral("aiff"); return QStringLiteral("aiff");
case kFormatMP3: case kFormatMP3:
return QStringLiteral("mp3"); return QStringLiteral("mp3");
case kFormatFLAC: case kFormatFLAC:
return QStringLiteral("flac"); return QStringLiteral("flac");
case kFormatOgg: case kFormatOgg:
return QStringLiteral("ogg"); return QStringLiteral("ogg");
case kFormatWebM: case kFormatWebM:
return QStringLiteral("webm"); return QStringLiteral("webm");
case kFormatSRT: case kFormatSRT:
return QStringLiteral("srt"); return QStringLiteral("srt");
case kFormatCount: case kFormatCount:
break; break;
} }
return QString(); return QString();
} }
QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f) QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
{ {
switch (f) { switch (f) {
case kFormatDNxHD: case kFormatDNxHD:
return {ExportCodec::kCodecDNxHD}; return { ExportCodec::kCodecDNxHD };
case kFormatMatroska: case kFormatMatroska:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9}; return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
case kFormatMPEG4Video: ExportCodec::kCodecH265, ExportCodec::kCodecVP9 };
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265}; case kFormatMPEG4Video:
case kFormatOpenEXR: return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
return {ExportCodec::kCodecOpenEXR}; ExportCodec::kCodecH265 };
case kFormatPNG: case kFormatOpenEXR:
return {ExportCodec::kCodecPNG}; return { ExportCodec::kCodecOpenEXR };
case kFormatTIFF: case kFormatPNG:
return {ExportCodec::kCodecTIFF}; return { ExportCodec::kCodecPNG };
case kFormatQuickTime: case kFormatTIFF:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform}; return { ExportCodec::kCodecTIFF };
case kFormatWebM: case kFormatQuickTime:
return {ExportCodec::kCodecAV1, ExportCodec::kCodecVP9}; return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
case kFormatOgg: ExportCodec::kCodecH265, ExportCodec::kCodecProRes,
case kFormatWAV: ExportCodec::kCodecCineform };
case kFormatMPEG4Audio: case kFormatWebM:
case kFormatAIFF: return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
case kFormatMP3: case kFormatOgg:
case kFormatFLAC: case kFormatWAV:
case kFormatSRT: case kFormatMPEG4Audio:
case kFormatCount: case kFormatAIFF:
break; case kFormatMP3:
} case kFormatFLAC:
case kFormatSRT:
case kFormatCount:
break;
}
return {}; return {};
} }
QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f) QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
{ {
switch (f) { switch (f) {
// Video/audio formats // Video/audio formats
case kFormatDNxHD: case kFormatDNxHD:
return {ExportCodec::kCodecPCM}; return { ExportCodec::kCodecPCM };
case kFormatMatroska: case kFormatMatroska:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC}; return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
case kFormatMPEG4Video: ExportCodec::kCodecMP3, ExportCodec::kCodecPCM,
case kFormatMPEG4Audio: ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus,
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3}; ExportCodec::kCodecFLAC };
case kFormatQuickTime: case kFormatMPEG4Video:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM}; case kFormatMPEG4Audio:
case kFormatWebM: return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis}; ExportCodec::kCodecMP3 };
case kFormatQuickTime:
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM };
case kFormatWebM:
return { ExportCodec::kCodecOpus, ExportCodec::kCodecAAC,
ExportCodec::kCodecMP2, ExportCodec::kCodecMP3,
ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis };
// Audio only formats // Audio only formats
case kFormatWAV: case kFormatWAV:
return {ExportCodec::kCodecPCM}; return { ExportCodec::kCodecPCM };
case kFormatAIFF: case kFormatAIFF:
return {ExportCodec::kCodecPCM}; return { ExportCodec::kCodecPCM };
case kFormatMP3: case kFormatMP3:
return {ExportCodec::kCodecMP3}; return { ExportCodec::kCodecMP3 };
case kFormatFLAC: case kFormatFLAC:
return {ExportCodec::kCodecFLAC}; return { ExportCodec::kCodecFLAC };
case kFormatOgg: case kFormatOgg:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM}; return { ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis,
ExportCodec::kCodecPCM };
// Video only formats // Video only formats
case kFormatOpenEXR: case kFormatOpenEXR:
case kFormatPNG: case kFormatPNG:
case kFormatTIFF: case kFormatTIFF:
case kFormatSRT: case kFormatSRT:
case kFormatCount: case kFormatCount:
break; break;
}
} return {};
return {};
} }
QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f) QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
{ {
switch (f) { switch (f) {
case kFormatDNxHD: case kFormatDNxHD:
case kFormatMPEG4Video: case kFormatMPEG4Video:
case kFormatMPEG4Audio: case kFormatMPEG4Audio:
case kFormatOpenEXR: case kFormatOpenEXR:
case kFormatQuickTime: case kFormatQuickTime:
case kFormatPNG: case kFormatPNG:
case kFormatTIFF: case kFormatTIFF:
case kFormatWAV: case kFormatWAV:
case kFormatAIFF: case kFormatAIFF:
case kFormatMP3: case kFormatMP3:
case kFormatFLAC: case kFormatFLAC:
case kFormatOgg: case kFormatOgg:
case kFormatWebM: case kFormatWebM:
case kFormatCount: case kFormatCount:
break; break;
case kFormatMatroska: case kFormatMatroska:
case kFormatSRT: case kFormatSRT:
return {ExportCodec::kCodecSRT}; return { ExportCodec::kCodecSRT };
} }
return {}; return {};
} }
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c) QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f,
ExportCodec::Codec c)
{ {
Encoder* e = Encoder::CreateFromFormat(f, EncodingParams()); Encoder *e = Encoder::CreateFromFormat(f, EncodingParams());
QStringList list; QStringList list;
if (e) { if (e) {
list = e->GetPixelFormatsForCodec(c); list = e->GetPixelFormatsForCodec(c);
delete e; delete e;
} }
return list; return list;
} }
std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c) std::vector<SampleFormat>
ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
{ {
std::vector<SampleFormat> f; std::vector<SampleFormat> f;
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams()); Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
if (e) { if (e) {
f = e->GetSampleFormatsForCodec(c); f = e->GetSampleFormatsForCodec(c);
delete e; delete e;
} }
return f;
return f;
} }
} }
+31 -31
View File
@@ -27,42 +27,42 @@
#include "common/define.h" #include "common/define.h"
#include "exportcodec.h" #include "exportcodec.h"
namespace olive { namespace olive
class ExportFormat : public QObject
{ {
Q_OBJECT
class ExportFormat : public QObject {
Q_OBJECT
public: public:
// Only append to this list (never insert) because indexes are used in serialized files // Only append to this list (never insert) because indexes are used in serialized files
enum Format { enum Format {
kFormatDNxHD, kFormatDNxHD,
kFormatMatroska, kFormatMatroska,
kFormatMPEG4Video, kFormatMPEG4Video,
kFormatOpenEXR, kFormatOpenEXR,
kFormatQuickTime, kFormatQuickTime,
kFormatPNG, kFormatPNG,
kFormatTIFF, kFormatTIFF,
kFormatWAV, kFormatWAV,
kFormatAIFF, kFormatAIFF,
kFormatMP3, kFormatMP3,
kFormatFLAC, kFormatFLAC,
kFormatOgg, kFormatOgg,
kFormatWebM, kFormatWebM,
kFormatSRT, kFormatSRT,
kFormatMPEG4Audio, kFormatMPEG4Audio,
kFormatCount kFormatCount
}; };
static QString GetName(Format f); static QString GetName(Format f);
static QString GetExtension(Format f); static QString GetExtension(Format f);
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f); static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f); static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f); static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
static std::vector<SampleFormat> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
static std::vector<SampleFormat>
GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
}; };
} }
File diff suppressed because it is too large Load Diff
+93 -83
View File
@@ -39,92 +39,100 @@ extern "C" {
#include "codec/decoder.h" #include "codec/decoder.h"
#include "common/ffmpegutils.h" #include "common/ffmpegutils.h"
namespace olive { namespace olive
{
/** /**
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder * @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
*/ */
class FFmpegDecoder : public Decoder class FFmpegDecoder : public Decoder {
{ Q_OBJECT
Q_OBJECT
public: public:
// Constructor // Constructor
FFmpegDecoder(); FFmpegDecoder();
// Destructor // Destructor
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder) DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
virtual QString id() const override; virtual QString id() const override;
virtual bool SupportsVideo() override{return true;} virtual bool SupportsVideo() override
virtual bool SupportsAudio() override{return true;} {
return true;
}
virtual bool SupportsAudio() override
{
return true;
}
virtual FootageDescription Probe(const QString &filename, CancelAtom *cancelled) const override; virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected: protected:
virtual bool OpenInternal() override; virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override; virtual TexturePtr
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled) override; RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual void CloseInternal() override; virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled) override;
virtual void CloseInternal() override;
virtual rational GetAudioStartOffset() const override; virtual rational GetAudioStartOffset() const override;
private: private:
class Instance class Instance {
{ public:
public: Instance();
Instance();
~Instance() ~Instance()
{ {
Close(); Close();
} }
bool Open(const char* filename, int stream_index); bool Open(const char *filename, int stream_index);
bool IsOpen() const bool IsOpen() const
{ {
return fmt_ctx_; return fmt_ctx_;
} }
void Close(); void Close();
/** /**
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_) * @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
* *
* @return * @return
* *
* An FFmpeg error code, or >= 0 on success * An FFmpeg error code, or >= 0 on success
*/ */
int GetFrame(AVPacket* pkt, AVFrame* frame); int GetFrame(AVPacket *pkt, AVFrame *frame);
const char *GetSubtitleHeader() const; const char *GetSubtitleHeader() const;
int GetSubtitle(AVPacket* pkt, AVSubtitle* sub); int GetSubtitle(AVPacket *pkt, AVSubtitle *sub);
int GetPacket(AVPacket *pkt); int GetPacket(AVPacket *pkt);
void Seek(int64_t timestamp); void Seek(int64_t timestamp);
AVFormatContext* fmt_ctx() const AVFormatContext *fmt_ctx() const
{ {
return fmt_ctx_; return fmt_ctx_;
} }
AVStream* avstream() const AVStream *avstream() const
{ {
return avstream_; return avstream_;
} }
private: private:
AVFormatContext* fmt_ctx_; AVFormatContext *fmt_ctx_;
AVCodecContext* codec_ctx_; AVCodecContext *codec_ctx_;
AVStream* avstream_; AVStream *avstream_;
AVDictionary* opts_; AVDictionary *opts_;
};
}; /**
/**
* @brief Handle an FFmpeg error code * @brief Handle an FFmpeg error code
* *
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this * Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
@@ -132,54 +140,56 @@ private:
* *
* @param error_code * @param error_code
*/ */
static QString FFmpegError(int error_code); static QString FFmpegError(int error_code);
void FreeScaler(); void FreeScaler();
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt); static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
static int GetNativeChannelCount(AVPixelFormat pix_fmt); static int GetNativeChannelCount(AVPixelFormat pix_fmt);
static AVChannelLayout ValidateChannelLayout(AVStream *stream); static AVChannelLayout ValidateChannelLayout(AVStream *stream);
static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing); static const char *
GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f); static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
AVFramePtr GetFrameFromCache(const int64_t &t) const; AVFramePtr GetFrameFromCache(const int64_t &t) const;
void ClearFrameCache(); void ClearFrameCache();
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p); AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
TexturePtr ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original); TexturePtr ProcessFrameIntoTexture(AVFramePtr f,
const RetrieveVideoParams &p,
const AVFramePtr original);
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled); AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
void RemoveFirstFrame(); void RemoveFirstFrame();
static int MaximumQueueSize(); static int MaximumQueueSize();
SwsContext *sws_ctx_; SwsContext *sws_ctx_;
int sws_src_width_; int sws_src_width_;
int sws_src_height_; int sws_src_height_;
AVPixelFormat sws_src_format_; AVPixelFormat sws_src_format_;
int sws_dst_width_; int sws_dst_width_;
int sws_dst_height_; int sws_dst_height_;
AVPixelFormat sws_dst_format_; AVPixelFormat sws_dst_format_;
AVColorRange sws_colrange_; AVColorRange sws_colrange_;
AVColorSpace sws_colspace_; AVColorSpace sws_colspace_;
AVPacket *working_packet_; AVPacket *working_packet_;
int64_t second_ts_; int64_t second_ts_;
std::list<AVFramePtr> cached_frames_; std::list<AVFramePtr> cached_frames_;
bool cache_at_zero_; bool cache_at_zero_;
bool cache_at_eof_; bool cache_at_eof_;
Instance instance_;
Instance instance_;
}; };
} }
File diff suppressed because it is too large Load Diff
+54 -45
View File
@@ -31,37 +31,41 @@ extern "C" {
#include "codec/encoder.h" #include "codec/encoder.h"
namespace olive { namespace olive
class FFmpegEncoder : public Encoder
{ {
Q_OBJECT
class FFmpegEncoder : public Encoder {
Q_OBJECT
public: public:
FFmpegEncoder(const EncodingParams &params); FFmpegEncoder(const EncodingParams &params);
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const override; virtual QStringList
GetPixelFormatsForCodec(ExportCodec::Codec c) const override;
virtual std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const override; virtual std::vector<SampleFormat>
GetSampleFormatsForCodec(ExportCodec::Codec c) const override;
virtual bool Open() override; virtual bool Open() override;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override; virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) override;
virtual bool WriteAudio(const olive::SampleBuffer &audio) override; virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count); bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data,
int input_sample_count);
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override; virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override; virtual void Close() override;
virtual PixelFormat GetDesiredPixelFormat() const override virtual PixelFormat GetDesiredPixelFormat() const override
{ {
return video_conversion_fmt_; return video_conversion_fmt_;
} }
private: private:
/** /**
* @brief Handle an FFmpeg error code * @brief Handle an FFmpeg error code
* *
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this * Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
@@ -69,43 +73,48 @@ private:
* *
* @param error_code * @param error_code
*/ */
void FFmpegError(const QString &context, int error_code); void FFmpegError(const QString &context, int error_code);
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream); bool WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
AVStream *stream);
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec); bool InitializeStream(enum AVMediaType type, AVStream **stream,
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, const AVCodec* codec); AVCodecContext **codec_ctx,
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, const AVCodec *codec); const ExportCodec::Codec &codec);
bool InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx,
const AVCodec *codec);
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx,
const AVCodec *codec);
void FlushEncoders(); void FlushEncoders();
void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream); void FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream);
bool InitializeResampleContext(const AudioParams &audio); bool InitializeResampleContext(const AudioParams &audio);
static const AVCodec *GetEncoder(ExportCodec::Codec c, SampleFormat aformat); static const AVCodec *GetEncoder(ExportCodec::Codec c,
SampleFormat aformat);
AVFormatContext* fmt_ctx_; AVFormatContext *fmt_ctx_;
AVStream* video_stream_; AVStream *video_stream_;
AVCodecContext* video_codec_ctx_; AVCodecContext *video_codec_ctx_;
AVFilterGraph *video_scale_ctx_; AVFilterGraph *video_scale_ctx_;
AVFilterContext *video_buffersrc_ctx_; AVFilterContext *video_buffersrc_ctx_;
AVFilterContext *video_buffersink_ctx_; AVFilterContext *video_buffersink_ctx_;
PixelFormat video_conversion_fmt_; PixelFormat video_conversion_fmt_;
AVStream* audio_stream_; AVStream *audio_stream_;
AVCodecContext* audio_codec_ctx_; AVCodecContext *audio_codec_ctx_;
SwrContext* audio_resample_ctx_; SwrContext *audio_resample_ctx_;
AVFrame* audio_frame_; AVFrame *audio_frame_;
int audio_max_samples_; int audio_max_samples_;
int audio_frame_offset_; int audio_frame_offset_;
int audio_write_count_; int audio_write_count_;
AVStream* subtitle_stream_; AVStream *subtitle_stream_;
AVCodecContext* subtitle_codec_ctx_; AVCodecContext *subtitle_codec_ctx_;
bool open_;
bool open_;
}; };
} }
+87 -79
View File
@@ -28,153 +28,161 @@
#include "common/oiioutils.h" #include "common/oiioutils.h"
#include "render/framemanager.h" #include "render/framemanager.h"
namespace olive { namespace olive
{
Frame::Frame() : Frame::Frame()
data_(nullptr), : data_(nullptr)
data_size_(0), , data_size_(0)
timestamp_(0) , timestamp_(0)
{ {
} }
Frame::~Frame() Frame::~Frame()
{ {
destroy(); destroy();
} }
FramePtr Frame::Create() FramePtr Frame::Create()
{ {
return std::make_shared<Frame>(); return std::make_shared<Frame>();
} }
const VideoParams &Frame::video_params() const const VideoParams &Frame::video_params() const
{ {
return params_; return params_;
} }
void Frame::set_video_params(const VideoParams &params) void Frame::set_video_params(const VideoParams &params)
{ {
params_ = params; params_ = params;
linesize_ = generate_linesize_bytes(width(), params_.format(), params_.channel_count()); linesize_ = generate_linesize_bytes(width(), params_.format(),
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel(); params_.channel_count());
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
} }
FramePtr Frame::Interlace(FramePtr top, FramePtr bottom) FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
{ {
if (top->video_params() != bottom->video_params()) { if (top->video_params() != bottom->video_params()) {
qCritical() << "Tried to interlace two frames that had incompatible parameters"; qCritical()
return nullptr; << "Tried to interlace two frames that had incompatible parameters";
} return nullptr;
}
FramePtr interlaced = Frame::Create(); FramePtr interlaced = Frame::Create();
interlaced->set_video_params(top->video_params()); interlaced->set_video_params(top->video_params());
interlaced->allocate(); interlaced->allocate();
int linesize = interlaced->linesize_bytes(); int linesize = interlaced->linesize_bytes();
for (int i=0; i<interlaced->height(); i++) { for (int i = 0; i < interlaced->height(); i++) {
FramePtr which = (i%2 == 0) ? top : bottom; FramePtr which = (i % 2 == 0) ? top : bottom;
memcpy(interlaced->data() + i*linesize, memcpy(interlaced->data() + i * linesize,
which->const_data() + i*linesize, which->const_data() + i * linesize, linesize);
linesize); }
}
return interlaced; return interlaced;
} }
int Frame::generate_linesize_bytes(int width, PixelFormat format, int channel_count) int Frame::generate_linesize_bytes(int width, PixelFormat format,
int channel_count)
{ {
// Align to 32 bytes (not sure if this is necessary?) // Align to 32 bytes (not sure if this is necessary?)
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31); return VideoParams::GetBytesPerPixel(format, channel_count) *
((width + 31) & ~31);
} }
Color Frame::get_pixel(int x, int y) const Color Frame::get_pixel(int x, int y) const
{ {
if (!contains_pixel(x, y)) { if (!contains_pixel(x, y)) {
return Color(); return Color();
} }
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel(); int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
return Color(reinterpret_cast<const char*>(data_ + byte_offset), video_params().format(), video_params().channel_count()); return Color(reinterpret_cast<const char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
} }
bool Frame::contains_pixel(int x, int y) const bool Frame::contains_pixel(int x, int y) const
{ {
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height()); return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
} }
void Frame::set_pixel(int x, int y, const Color &c) void Frame::set_pixel(int x, int y, const Color &c)
{ {
if (!contains_pixel(x, y)) { if (!contains_pixel(x, y)) {
return; return;
} }
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel(); int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
c.toData(reinterpret_cast<char*>(data_ + byte_offset), video_params().format(), video_params().channel_count()); c.toData(reinterpret_cast<char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
} }
bool Frame::allocate() bool Frame::allocate()
{ {
// Assume this frame is intended to be a video frame // Assume this frame is intended to be a video frame
if (!params_.is_valid()) { if (!params_.is_valid()) {
qWarning() << "Tried to allocate a frame with invalid parameters"; qWarning() << "Tried to allocate a frame with invalid parameters";
return false; return false;
} }
if (is_allocated()) { if (is_allocated()) {
// Already allocated // Already allocated
return true; return true;
} }
data_size_ = linesize_ * height(); data_size_ = linesize_ * height();
data_ = FrameManager::Allocate(data_size_); data_ = FrameManager::Allocate(data_size_);
return true; return true;
} }
void Frame::destroy() void Frame::destroy()
{ {
if (is_allocated()) { if (is_allocated()) {
FrameManager::Deallocate(data_size_, data_); FrameManager::Deallocate(data_size_, data_);
data_size_ = 0; data_size_ = 0;
data_ = nullptr; data_ = nullptr;
} }
} }
FramePtr Frame::convert(PixelFormat format) const FramePtr Frame::convert(PixelFormat format) const
{ {
// Create new params with destination format // Create new params with destination format
VideoParams params = params_; VideoParams params = params_;
params.set_format(format); params.set_format(format);
// Create new frame // Create new frame
FramePtr converted = Frame::Create(); FramePtr converted = Frame::Create();
converted->set_video_params(params); converted->set_video_params(params);
converted->set_timestamp(timestamp_); converted->set_timestamp(timestamp_);
converted->allocate(); converted->allocate();
// Do the conversion through OIIO for convenience // Do the conversion through OIIO for convenience
OIIO::ImageBuf src(OIIO::ImageSpec(width(), height(), OIIO::ImageBuf src(
channel_count(), OIIO::ImageSpec(width(), height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format()))); OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
OIIOUtils::FrameToBuffer(this, &src); OIIOUtils::FrameToBuffer(this, &src);
OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(), OIIO::ImageBuf dst(OIIO::ImageSpec(
channel_count(), converted->width(), converted->height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(format))); OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
if (dst.copy_pixels(src)) { if (dst.copy_pixels(src)) {
OIIOUtils::BufferToFrame(&dst, converted.get()); OIIOUtils::BufferToFrame(&dst, converted.get());
return converted; return converted;
} else { } else {
return nullptr; return nullptr;
} }
} }
} }
+79 -79
View File
@@ -28,7 +28,8 @@
#include "common/define.h" #include "common/define.h"
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
{
class Frame; class Frame;
using FramePtr = std::shared_ptr<Frame>; using FramePtr = std::shared_ptr<Frame>;
@@ -36,135 +37,134 @@ using FramePtr = std::shared_ptr<Frame>;
/** /**
* @brief Video frame data or audio sample data from a Decoder * @brief Video frame data or audio sample data from a Decoder
*/ */
class Frame class Frame {
{
public: public:
Frame(); Frame();
~Frame(); ~Frame();
DISABLE_COPY_MOVE(Frame) DISABLE_COPY_MOVE(Frame)
static FramePtr Create(); static FramePtr Create();
const VideoParams& video_params() const; const VideoParams &video_params() const;
void set_video_params(const VideoParams& params); void set_video_params(const VideoParams &params);
static FramePtr Interlace(FramePtr top, FramePtr bottom); static FramePtr Interlace(FramePtr top, FramePtr bottom);
static int generate_linesize_bytes(int width, PixelFormat format, int channel_count); static int generate_linesize_bytes(int width, PixelFormat format,
int channel_count);
int linesize_pixels() const int linesize_pixels() const
{ {
return linesize_pixels_; return linesize_pixels_;
} }
int linesize_bytes() const int linesize_bytes() const
{ {
return linesize_; return linesize_;
} }
int width() const int width() const
{ {
return params_.effective_width(); return params_.effective_width();
} }
int height() const int height() const
{ {
return params_.effective_height(); return params_.effective_height();
} }
PixelFormat format() const PixelFormat format() const
{ {
return params_.format(); return params_.format();
} }
int channel_count() const int channel_count() const
{ {
return params_.channel_count(); return params_.channel_count();
} }
Color get_pixel(int x, int y) const; Color get_pixel(int x, int y) const;
bool contains_pixel(int x, int y) const; bool contains_pixel(int x, int y) const;
void set_pixel(int x, int y, const Color& c); void set_pixel(int x, int y, const Color &c);
/** /**
* @brief Get frame's timestamp. * @brief Get frame's timestamp.
* *
* This timestamp is always a rational that will equate to the time in seconds. * This timestamp is always a rational that will equate to the time in seconds.
*/ */
const rational& timestamp() const const rational &timestamp() const
{ {
return timestamp_; return timestamp_;
} }
void set_timestamp(const rational& timestamp) void set_timestamp(const rational &timestamp)
{ {
timestamp_ = timestamp; timestamp_ = timestamp;
} }
/** /**
* @brief Get the data buffer of this frame * @brief Get the data buffer of this frame
*/ */
char* data() char *data()
{ {
return data_; return data_;
} }
/** /**
* @brief Get the const data buffer of this frame * @brief Get the const data buffer of this frame
*/ */
const char* const_data() const const char *const_data() const
{ {
return data_; return data_;
} }
/** /**
* @brief Allocate memory buffer to store data based on parameters * @brief Allocate memory buffer to store data based on parameters
* *
* For video frames, the width(), height(), and format() must be set for this function to work. * For video frames, the width(), height(), and format() must be set for this function to work.
* *
* If a memory buffer has been previously allocated without destroying, this function will destroy it. * If a memory buffer has been previously allocated without destroying, this function will destroy it.
*/ */
bool allocate(); bool allocate();
/** /**
* @brief Return whether the frame is allocated or not * @brief Return whether the frame is allocated or not
*/ */
bool is_allocated() const bool is_allocated() const
{ {
return data_; return data_;
} }
/** /**
* @brief Destroy a memory buffer allocated with allocate() * @brief Destroy a memory buffer allocated with allocate()
*/ */
void destroy(); void destroy();
/** /**
* @brief Returns the size of the array returned in data() in bytes * @brief Returns the size of the array returned in data() in bytes
* *
* Returns 0 if nothing is allocated. * Returns 0 if nothing is allocated.
*/ */
int allocated_size() const int allocated_size() const
{ {
return data_size_; return data_size_;
} }
FramePtr convert(PixelFormat format) const; FramePtr convert(PixelFormat format) const;
private: private:
VideoParams params_; VideoParams params_;
char* data_; char *data_;
int data_size_; int data_size_;
rational timestamp_; rational timestamp_;
int linesize_; int linesize_;
int linesize_pixels_;
int linesize_pixels_;
}; };
} }
+150 -133
View File
@@ -31,217 +31,234 @@
#include "core.h" #include "core.h"
#include "render/renderer.h" #include "render/renderer.h"
namespace olive { namespace olive
{
QStringList OIIODecoder::supported_formats_; QStringList OIIODecoder::supported_formats_;
OIIODecoder::OIIODecoder() : OIIODecoder::OIIODecoder()
image_(nullptr) : image_(nullptr)
{ {
} }
QString OIIODecoder::id() const QString OIIODecoder::id() const
{ {
return QStringLiteral("oiio"); return QStringLiteral("oiio");
} }
FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cancelled) const FootageDescription OIIODecoder::Probe(const QString &filename,
CancelAtom *cancelled) const
{ {
Q_UNUSED(cancelled) Q_UNUSED(cancelled)
FootageDescription desc(id()); FootageDescription desc(id());
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying // Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
// to open a file that it can't if it's given one // to open a file that it can't if it's given one
if (!FileTypeIsSupported(filename)) { if (!FileTypeIsSupported(filename)) {
return desc; return desc;
} }
std::string std_filename = filename.toStdString(); std::string std_filename = filename.toStdString();
auto in = OIIO::ImageInput::open(std_filename); auto in = OIIO::ImageInput::open(std_filename);
if (!in) { if (!in) {
return desc; return desc;
} }
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle // Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
// it better // it better
if (!strcmp(in->format_name(), "FFmpeg movie")) { if (!strcmp(in->format_name(), "FFmpeg movie")) {
return desc; return desc;
} }
bool stream_enabled = true; bool stream_enabled = true;
int i; int i;
for (i=0; in->seek_subimage(i, 0); i++) { for (i = 0; in->seek_subimage(i, 0); i++) {
OIIO::ImageSpec spec = in->spec(); OIIO::ImageSpec spec = in->spec();
VideoParams video_params = GetVideoParamsFromImageSpec(spec); VideoParams video_params = GetVideoParamsFromImageSpec(spec);
video_params.set_stream_index(i); video_params.set_stream_index(i);
if (i > 1) { if (i > 1) {
// This is a multilayer image and this image might have an offset // This is a multilayer image and this image might have an offset
OIIO::ImageSpec root_spec = in->spec(0); OIIO::ImageSpec root_spec = in->spec(0);
float norm_x = spec.x + float(spec.width)*0.5f - float(root_spec.width)*0.5f; float norm_x = spec.x + float(spec.width) * 0.5f -
float norm_y = spec.y + float(spec.height)*0.5f - float(root_spec.height)*0.5f; float(root_spec.width) * 0.5f;
float norm_y = spec.y + float(spec.height) * 0.5f -
float(root_spec.height) * 0.5f;
video_params.set_x(norm_x); video_params.set_x(norm_x);
video_params.set_y(norm_y); video_params.set_y(norm_y);
} }
// By default, only enable the first subimage (presumably the combined image). Later we will // By default, only enable the first subimage (presumably the combined image). Later we will
// ask the user if they want to enable the layers instead. // ask the user if they want to enable the layers instead.
video_params.set_enabled(stream_enabled); video_params.set_enabled(stream_enabled);
stream_enabled = false; stream_enabled = false;
// OIIO automatically premultiplies alpha // OIIO automatically premultiplies alpha
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this // FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
// likely reduces the fidelity? // likely reduces the fidelity?
video_params.set_premultiplied_alpha(true); video_params.set_premultiplied_alpha(true);
desc.AddVideoStream(video_params); desc.AddVideoStream(video_params);
} }
desc.SetStreamCount(i); desc.SetStreamCount(i);
// If we're here, we have a successful image open // If we're here, we have a successful image open
in->close(); in->close();
return desc; return desc;
} }
bool OIIODecoder::OpenInternal() bool OIIODecoder::OpenInternal()
{ {
// If we can open the filename provided, assume everything is working // If we can open the filename provided, assume everything is working
return OpenImageHandler(stream().filename(), stream().stream()); return OpenImageHandler(stream().filename(), stream().stream());
} }
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p) TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{ {
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec()); VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(p.divider); vp.set_divider(p.divider);
if (!buffer_.is_allocated() if (!buffer_.is_allocated() || last_params_.divider != p.divider) {
|| last_params_.divider != p.divider) { last_params_ = p;
last_params_ = p;
buffer_.destroy(); buffer_.destroy();
buffer_.set_video_params(vp); buffer_.set_video_params(vp);
buffer_.allocate(); buffer_.allocate();
if (p.divider == 1) { if (p.divider == 1) {
// Just upload straight to the buffer // Just upload straight to the buffer
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data()); image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
} else { } else {
OIIO::ImageBuf buf(image_->spec()); OIIO::ImageBuf buf(image_->spec());
image_->read_image(0,0,0,-1,image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride()); image_->read_image(0, 0, 0, -1, image_->spec().format,
buf.localpixels(), buf.pixel_stride(),
buf.scanline_stride(), buf.z_stride());
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here) // Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
int px_sz = vp.GetBytesPerPixel(); int px_sz = vp.GetBytesPerPixel();
for (int dst_y=0; dst_y<buffer_.height(); dst_y++) { for (int dst_y = 0; dst_y < buffer_.height(); dst_y++) {
int src_y = dst_y * buf.spec().height / buffer_.height(); int src_y = dst_y * buf.spec().height / buffer_.height();
for (int dst_x=0; dst_x<buffer_.width(); dst_x++) { for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width(); int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x, memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y +
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x, px_sz * dst_x,
px_sz); static_cast<uint8_t *>(buf.localpixels()) +
} buf.scanline_stride() * src_y + px_sz * src_x,
} px_sz);
} }
} }
}
}
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels()); return p.renderer->CreateTexture(vp, buffer_.data(),
buffer_.linesize_pixels());
} }
void OIIODecoder::CloseInternal() void OIIODecoder::CloseInternal()
{ {
CloseImageHandle(); CloseImageHandle();
} }
bool OIIODecoder::FileTypeIsSupported(const QString& fn) bool OIIODecoder::FileTypeIsSupported(const QString &fn)
{ {
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG) // We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's // will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
// "extension_list" attribute and match it with the extension of the file. // "extension_list" attribute and match it with the extension of the file.
// Check if we've created the supported formats list, create it if not // Check if we've created the supported formats list, create it if not
if (supported_formats_.isEmpty()) { if (supported_formats_.isEmpty()) {
QStringList extension_list = QString::fromStdString(OIIO::get_string_attribute("extension_list")).split(';'); QStringList extension_list =
QString::fromStdString(OIIO::get_string_attribute("extension_list"))
.split(';');
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions // The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
foreach (const QString& ext, extension_list) { foreach (const QString &ext, extension_list) {
QStringList format_and_ext = ext.split(':'); QStringList format_and_ext = ext.split(':');
supported_formats_.append(format_and_ext.at(1).split(',')); supported_formats_.append(format_and_ext.at(1).split(','));
} }
} }
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) { if (!supported_formats_.contains(QFileInfo(fn).suffix(),
return false; Qt::CaseInsensitive)) {
} return false;
}
return true; return true;
} }
bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage) bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
{ {
image_ = OIIO::ImageInput::open(fn.toStdString()); image_ = OIIO::ImageInput::open(fn.toStdString());
if (!image_) { if (!image_) {
return false; return false;
} }
if (!image_->seek_subimage(subimage, 0)) { if (!image_->seek_subimage(subimage, 0)) {
return false; return false;
} }
// Check if we can work with this pixel format // Check if we can work with this pixel format
const OIIO::ImageSpec& spec = image_->spec(); const OIIO::ImageSpec &spec = image_->spec();
// We use RGBA frames because that tends to be the native format of GPUs // We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)); pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
if (pix_fmt_ == PixelFormat::INVALID) { if (pix_fmt_ == PixelFormat::INVALID) {
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format"; qWarning()
return false; << "Failed to convert OIIO::ImageDesc to native pixel format";
} return false;
}
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_); oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) { if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
qCritical() << "Failed to determine appropriate OIIO basetype from native format"; qCritical()
return false; << "Failed to determine appropriate OIIO basetype from native format";
} return false;
}
return true; return true;
} }
void OIIODecoder::CloseImageHandle() void OIIODecoder::CloseImageHandle()
{ {
if (image_) { if (image_) {
image_->close(); image_->close();
image_ = nullptr; image_ = nullptr;
} }
buffer_.destroy(); buffer_.destroy();
} }
VideoParams OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec) VideoParams
OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
{ {
VideoParams video_params; VideoParams video_params;
video_params.set_width(spec.width); video_params.set_width(spec.width);
video_params.set_height(spec.height); video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype))); video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(
video_params.set_channel_count(spec.nchannels); static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec)); video_params.set_channel_count(spec.nchannels);
video_params.set_video_type(VideoParams::kVideoTypeStill); video_params.set_pixel_aspect_ratio(
OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill);
return video_params; return video_params;
} }
} }
+27 -23
View File
@@ -26,46 +26,50 @@
#include "codec/decoder.h" #include "codec/decoder.h"
namespace olive { namespace olive
class OIIODecoder : public Decoder
{ {
Q_OBJECT
class OIIODecoder : public Decoder {
Q_OBJECT
public: public:
OIIODecoder(); OIIODecoder();
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder) DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
virtual QString id() const override; virtual QString id() const override;
virtual bool SupportsVideo() override{return true;} virtual bool SupportsVideo() override
{
return true;
}
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const override; virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected: protected:
virtual bool OpenInternal() override; virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override; virtual TexturePtr
virtual void CloseInternal() override; RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual void CloseInternal() override;
private: private:
std::unique_ptr<OIIO::ImageInput> image_; std::unique_ptr<OIIO::ImageInput> image_;
static bool FileTypeIsSupported(const QString& fn); static bool FileTypeIsSupported(const QString &fn);
bool OpenImageHandler(const QString& fn, int subimage); bool OpenImageHandler(const QString &fn, int subimage);
void CloseImageHandle(); void CloseImageHandle();
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec); static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
PixelFormat pix_fmt_; PixelFormat pix_fmt_;
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_; OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
Frame buffer_; Frame buffer_;
RetrieveVideoParams last_params_; RetrieveVideoParams last_params_;
static QStringList supported_formats_;
static QStringList supported_formats_;
}; };
} }
+28 -26
View File
@@ -22,60 +22,62 @@
#include "common/oiioutils.h" #include "common/oiioutils.h"
namespace olive { namespace olive
OIIOEncoder::OIIOEncoder(const EncodingParams &params) :
Encoder(params)
{ {
OIIOEncoder::OIIOEncoder(const EncodingParams &params)
: Encoder(params)
{
} }
bool OIIOEncoder::Open() bool OIIOEncoder::Open()
{ {
return true; return true;
} }
bool OIIOEncoder::WriteFrame(FramePtr frame, rational time) bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
{ {
std::string filename = GetFilenameForFrame(time).toStdString(); std::string filename = GetFilenameForFrame(time).toStdString();
auto output = OIIO::ImageOutput::create(filename); auto output = OIIO::ImageOutput::create(filename);
if (!output) { if (!output) {
return false; return false;
} }
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format()); OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
OIIO::ImageSpec spec(frame->width(), frame->height(), frame->channel_count(), type); OIIO::ImageSpec spec(frame->width(), frame->height(),
frame->channel_count(), type);
if (!output->open(filename, spec)) { if (!output->open(filename, spec)) {
return false; return false;
} }
if (!output->write_image(type, frame->data(), OIIO::AutoStride, frame->linesize_bytes())) { if (!output->write_image(type, frame->data(), OIIO::AutoStride,
return false; frame->linesize_bytes())) {
} return false;
}
if (!output->close()) { if (!output->close()) {
return false; return false;
} }
return true; return true;
} }
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio) bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
{ {
// Do nothing // Do nothing
return false; return false;
} }
bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block) bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
{ {
return false; return false;
} }
void OIIOEncoder::Close() void OIIOEncoder::Close()
{ {
// Do nothing // Do nothing
} }
} }
+11 -11
View File
@@ -23,23 +23,23 @@
#include "codec/encoder.h" #include "codec/encoder.h"
namespace olive { namespace olive
class OIIOEncoder : public Encoder
{ {
Q_OBJECT
class OIIOEncoder : public Encoder {
Q_OBJECT
public: public:
OIIOEncoder(const EncodingParams &params); OIIOEncoder(const EncodingParams &params);
public slots: public slots:
virtual bool Open() override; virtual bool Open() override;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override; virtual bool WriteFrame(olive::FramePtr frame,
virtual bool WriteAudio(const SampleBuffer &audio) override; olive::core::rational time) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override; virtual bool WriteAudio(const SampleBuffer &audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
virtual void Close() override;
}; };
} }
+61 -58
View File
@@ -20,100 +20,103 @@
#include "planarfiledevice.h" #include "planarfiledevice.h"
namespace olive { namespace olive
PlanarFileDevice::PlanarFileDevice(QObject *parent) :
QObject(parent)
{ {
PlanarFileDevice::PlanarFileDevice(QObject *parent)
: QObject(parent)
{
} }
PlanarFileDevice::~PlanarFileDevice() PlanarFileDevice::~PlanarFileDevice()
{ {
close(); close();
} }
bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMode mode) bool PlanarFileDevice::open(const QVector<QString> &filenames,
QIODevice::OpenMode mode)
{ {
if (isOpen()) { if (isOpen()) {
// Already open // Already open
return false; return false;
} }
files_.resize(filenames.size()); files_.resize(filenames.size());
files_.fill(nullptr); files_.fill(nullptr);
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
files_[i] = new QFile(filenames.at(i)); files_[i] = new QFile(filenames.at(i));
if (!files_[i]->open(mode)) { if (!files_[i]->open(mode)) {
close(); close();
return false; return false;
} }
} }
return true; return true;
} }
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offset) qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel,
qint64 offset)
{ {
qint64 ret = -1; qint64 ret = -1;
if (isOpen()) { if (isOpen()) {
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine // Kind of clunky but should be largely fine
ret = files_[i]->read(data[i] + offset, bytes_per_channel); ret = files_[i]->read(data[i] + offset, bytes_per_channel);
} }
} }
return ret; return ret;
} }
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel, qint64 offset) qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel,
qint64 offset)
{ {
qint64 ret = -1; qint64 ret = -1;
if (isOpen()) { if (isOpen()) {
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine // Kind of clunky but should be largely fine
ret = files_[i]->write(data[i] + offset, bytes_per_channel); ret = files_[i]->write(data[i] + offset, bytes_per_channel);
} }
} }
return ret; return ret;
} }
qint64 PlanarFileDevice::size() const qint64 PlanarFileDevice::size() const
{ {
if (isOpen()) { if (isOpen()) {
return files_.first()->size(); return files_.first()->size();
} else { } else {
return 0; return 0;
} }
} }
bool PlanarFileDevice::seek(qint64 pos) bool PlanarFileDevice::seek(qint64 pos)
{ {
bool ret = true; bool ret = true;
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
ret = files_[i]->seek(pos) & ret; ret = files_[i]->seek(pos) & ret;
} }
return ret; return ret;
} }
void PlanarFileDevice::close() void PlanarFileDevice::close()
{ {
for (int i=0; i<files_.size(); i++) { for (int i = 0; i < files_.size(); i++) {
QFile *f = files_.at(i); QFile *f = files_.at(i);
if (f) { if (f) {
if (f->isOpen()) { if (f->isOpen()) {
f->close(); f->close();
} }
delete f; delete f;
} }
} }
files_.clear(); files_.clear();
} }
} }
+18 -18
View File
@@ -25,38 +25,38 @@
#include <QFile> #include <QFile>
#include <QObject> #include <QObject>
namespace olive { namespace olive
{
using namespace core; using namespace core;
class PlanarFileDevice : public QObject class PlanarFileDevice : public QObject {
{ Q_OBJECT
Q_OBJECT
public: public:
PlanarFileDevice(QObject *parent = nullptr); PlanarFileDevice(QObject *parent = nullptr);
virtual ~PlanarFileDevice() override; virtual ~PlanarFileDevice() override;
bool isOpen() const bool isOpen() const
{ {
return !files_.isEmpty(); return !files_.isEmpty();
} }
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode); bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0); qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 write(const char **data, qint64 bytes_per_channel, qint64 offset = 0); qint64 write(const char **data, qint64 bytes_per_channel,
qint64 offset = 0);
qint64 size() const; qint64 size() const;
bool seek(qint64 pos); bool seek(qint64 pos);
void close(); void close();
private: private:
QVector<QFile*> files_; QVector<QFile *> files_;
}; };
} }
+3 -6
View File
@@ -23,15 +23,12 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
class AutoScroll { class AutoScroll {
public: public:
enum Method { enum Method { kNone, kPage, kSmooth };
kNone,
kPage,
kSmooth
};
}; };
} }
+22 -20
View File
@@ -24,36 +24,38 @@
#include "common/define.h" #include "common/define.h"
#include "render/cancelatom.h" #include "render/cancelatom.h"
namespace olive { namespace olive
{
class CancelableObject { class CancelableObject {
public: public:
CancelableObject() CancelableObject()
{ {
} }
void Cancel() void Cancel()
{ {
cancel_.Cancel(); cancel_.Cancel();
CancelEvent(); CancelEvent();
} }
CancelAtom *GetCancelAtom() CancelAtom *GetCancelAtom()
{ {
return &cancel_; return &cancel_;
} }
bool IsCancelled() bool IsCancelled()
{ {
return cancel_.IsCancelled(); return cancel_.IsCancelled();
} }
protected: protected:
virtual void CancelEvent(){} virtual void CancelEvent()
{
}
private: private:
CancelAtom cancel_; CancelAtom cancel_;
}; };
} }
+106 -98
View File
@@ -25,146 +25,154 @@
CommandLineParser::~CommandLineParser() CommandLineParser::~CommandLineParser()
{ {
foreach (const KnownOption& o, options_) { foreach (const KnownOption &o, options_) {
delete o.option; delete o.option;
} }
foreach (const KnownPositionalArgument& a, positional_args_) { foreach (const KnownPositionalArgument &a, positional_args_) {
delete a.option; delete a.option;
} }
} }
const CommandLineParser::Option *CommandLineParser::AddOption(const QStringList &strings, const QString &description, bool takes_arg, const QString &arg_placeholder, bool hidden) const CommandLineParser::Option *
CommandLineParser::AddOption(const QStringList &strings,
const QString &description, bool takes_arg,
const QString &arg_placeholder, bool hidden)
{ {
Option* o = new Option(); Option *o = new Option();
options_.append({strings, description, o, takes_arg, arg_placeholder, hidden}); options_.append(
{ strings, description, o, takes_arg, arg_placeholder, hidden });
return o; return o;
} }
const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArgument(const QString &name, const QString &description, bool required) const CommandLineParser::PositionalArgument *
CommandLineParser::AddPositionalArgument(const QString &name,
const QString &description,
bool required)
{ {
PositionalArgument* a = new PositionalArgument(); PositionalArgument *a = new PositionalArgument();
positional_args_.append({name, description, a, required}); positional_args_.append({ name, description, a, required });
return a; return a;
} }
void CommandLineParser::Process(const QVector<QString> &argv) void CommandLineParser::Process(const QVector<QString> &argv)
{ {
int positional_index = 0; int positional_index = 0;
for (int i=1; i<argv.size(); i++) { for (int i = 1; i < argv.size(); i++) {
if (argv[i][0] == '-') { if (argv[i][0] == '-') {
// Must be an option // Must be an option
// Skip past first dashes // Skip past first dashes
QString arg_basename = argv[i].mid(1); QString arg_basename = argv[i].mid(1);
bool matched_known = false; bool matched_known = false;
for (int j=0; j<options_.size(); j++) { for (int j = 0; j < options_.size(); j++) {
KnownOption& o = options_[j]; KnownOption &o = options_[j];
foreach (const QString& s, o.args) { foreach (const QString &s, o.args) {
if (!s.compare(arg_basename, Qt::CaseInsensitive)) { if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
// Flag discovered! // Flag discovered!
o.option->Set(); o.option->Set();
if (o.takes_arg && i+1 < argv.size()) { if (o.takes_arg && i + 1 < argv.size()) {
o.option->SetSetting(argv[i+1]); o.option->SetSetting(argv[i + 1]);
i++; i++;
} }
matched_known = true; matched_known = true;
goto found_flag; goto found_flag;
} }
} }
} }
found_flag: found_flag:
if (!matched_known) { if (!matched_known) {
qWarning() << "Unknown parameter:" << argv[i]; qWarning() << "Unknown parameter:" << argv[i];
} }
} else { } else {
// Must be a positional flag // Must be a positional flag
if (positional_index < positional_args_.size()) { if (positional_index < positional_args_.size()) {
positional_args_[positional_index].option->SetSetting(argv[i]); positional_args_[positional_index].option->SetSetting(argv[i]);
positional_index++; positional_index++;
} else { } else {
qWarning() << "Unknown parameter:" << argv[i]; qWarning() << "Unknown parameter:" << argv[i];
} }
} }
} }
} }
void CommandLineParser::PrintHelp(const char* filename) void CommandLineParser::PrintHelp(const char *filename)
{ {
printf("%s %s\n", printf("%s %s\n", QCoreApplication::applicationName().toUtf8().constData(),
QCoreApplication::applicationName().toUtf8().constData(), QCoreApplication::applicationVersion().toUtf8().constData());
QCoreApplication::applicationVersion().toUtf8().constData());
printf("Copyright (C) 2018-2022 Olive Team\n"); printf("Copyright (C) 2018-2022 Olive Team\n");
QString positional_args; QString positional_args;
for (int i=0; i<positional_args_.size(); i++) { for (int i = 0; i < positional_args_.size(); i++) {
if (i > 0) { if (i > 0) {
positional_args.append(' '); positional_args.append(' ');
} }
positional_args.append('['); positional_args.append('[');
positional_args.append(positional_args_.at(i).name); positional_args.append(positional_args_.at(i).name);
positional_args.append(']'); positional_args.append(']');
} }
const char* basename; const char *basename;
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
basename = strrchr(filename, '\\'); basename = strrchr(filename, '\\');
if (!basename) { if (!basename) {
basename = strrchr(filename, '/'); basename = strrchr(filename, '/');
} }
#else #else
basename = strrchr(filename, '/'); basename = strrchr(filename, '/');
#endif #endif
if (basename) { if (basename) {
// Slash found, increment pointer to avoid showing the slash itself // Slash found, increment pointer to avoid showing the slash itself
basename++; basename++;
} else { } else {
// If no slashes are found, assume string is already a basename // If no slashes are found, assume string is already a basename
basename = filename; basename = filename;
} }
printf("Usage: %s [options] %s\n\n", basename, positional_args.toUtf8().constData()); printf("Usage: %s [options] %s\n\n", basename,
foreach (const KnownOption& o, options_) { positional_args.toUtf8().constData());
if (o.hidden) { foreach (const KnownOption &o, options_) {
continue; if (o.hidden) {
} continue;
}
QString all_args; QString all_args;
for (int i=0; i<o.args.size(); i++) { for (int i = 0; i < o.args.size(); i++) {
if (i > 0) { if (i > 0) {
all_args.append(QStringLiteral(", ")); all_args.append(QStringLiteral(", "));
} }
const QString& this_arg = o.args.at(i); const QString &this_arg = o.args.at(i);
all_args.append('-'); all_args.append('-');
all_args.append(this_arg); all_args.append(this_arg);
} }
if (o.arg_placeholder.isEmpty()) { if (o.arg_placeholder.isEmpty()) {
printf(" %s\n", all_args.toUtf8().constData()); printf(" %s\n", all_args.toUtf8().constData());
} else { } else {
printf(" %s <%s>\n", all_args.toUtf8().constData(), o.arg_placeholder.toUtf8().constData()); printf(" %s <%s>\n", all_args.toUtf8().constData(),
} o.arg_placeholder.toUtf8().constData());
}
printf(" %s\n\n", o.description.toUtf8().constData()); printf(" %s\n\n", o.description.toUtf8().constData());
} }
printf("\n"); printf("\n");
} }
+60 -60
View File
@@ -36,86 +36,86 @@
* the user as a command line argument. Therefore we needed a custom implementation that could * the user as a command line argument. Therefore we needed a custom implementation that could
* parse arguments without the need for a Q(Core)Application to be present already. * parse arguments without the need for a Q(Core)Application to be present already.
*/ */
class CommandLineParser class CommandLineParser {
{
public: public:
~CommandLineParser(); ~CommandLineParser();
DISABLE_COPY_MOVE(CommandLineParser) DISABLE_COPY_MOVE(CommandLineParser)
class PositionalArgument class PositionalArgument {
{ public:
public: PositionalArgument() = default;
PositionalArgument() = default;
const QString& GetSetting() const const QString &GetSetting() const
{ {
return setting_; return setting_;
} }
void SetSetting(const QString& s) void SetSetting(const QString &s)
{ {
setting_ = s; setting_ = s;
} }
private: private:
QString setting_; QString setting_;
};
}; class Option : public PositionalArgument {
public:
Option()
{
is_set_ = false;
}
class Option : public PositionalArgument { bool IsSet() const
public: {
Option() return is_set_;
{ }
is_set_ = false;
}
bool IsSet() const void Set()
{ {
return is_set_; is_set_ = true;
} }
void Set() private:
{ bool is_set_;
is_set_ = true; };
}
private: CommandLineParser() = default;
bool is_set_;
}; const Option *AddOption(const QStringList &strings,
const QString &description, bool takes_arg = false,
const QString &arg_placeholder = QString(),
bool hidden = false);
CommandLineParser() = default; const PositionalArgument *AddPositionalArgument(const QString &name,
const QString &description,
bool required = false);
const Option* AddOption(const QStringList& strings, const QString& description, bool takes_arg = false, const QString& arg_placeholder = QString(), bool hidden = false); void Process(const QVector<QString> &argv);
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false); void PrintHelp(const char *filename);
void Process(const QVector<QString> &argv);
void PrintHelp(const char* filename);
private: private:
struct KnownOption { struct KnownOption {
QStringList args; QStringList args;
QString description; QString description;
Option* option; Option *option;
bool takes_arg; bool takes_arg;
QString arg_placeholder; QString arg_placeholder;
bool hidden; bool hidden;
}; };
struct KnownPositionalArgument { struct KnownPositionalArgument {
QString name; QString name;
QString description; QString description;
PositionalArgument* option; PositionalArgument *option;
bool required; bool required;
}; };
QVector<KnownOption> options_; QVector<KnownOption> options_;
QVector<KnownPositionalArgument> positional_args_;
QVector<KnownPositionalArgument> positional_args_;
}; };
#endif // COMMANDLINEPARSER_H #endif // COMMANDLINEPARSER_H
+44 -33
View File
@@ -40,52 +40,63 @@ crashpad::CrashpadClient *client;
bool InitializeCrashpad() bool InitializeCrashpad()
{ {
QString report_path = QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("reports")); QString report_path = QDir(olive::FileFunctions::GetTempFilePath())
.filePath(QStringLiteral("reports"));
QString handler_fn = olive::FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("crashpad_handler")); QString handler_fn =
olive::FileFunctions::GetFormattedExecutableForPlatform(
QStringLiteral("crashpad_handler"));
// Generate absolute path // Generate absolute path
QString handler_abs_path = QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn); QString handler_abs_path =
QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
bool status = false; bool status = false;
if (QFileInfo::exists(handler_abs_path)) { if (QFileInfo::exists(handler_abs_path)) {
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path)); base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path)); base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics")))); base::FilePath metrics_dir(
QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath())
.filePath(QStringLiteral("metrics"))));
// Metadata that will be posted to the server with the crash report map // Metadata that will be posted to the server with the crash report map
std::map<std::string, std::string> annotations; std::map<std::string, std::string> annotations;
// Disable crashpad rate limiting so that all crashes have dmp files // Disable crashpad rate limiting so that all crashes have dmp files
std::vector<std::string> arguments; std::vector<std::string> arguments;
arguments.push_back("--no-rate-limit"); arguments.push_back("--no-rate-limit");
arguments.push_back("--no-upload-gzip"); arguments.push_back("--no-upload-gzip");
// Initialize Crashpad database // Initialize Crashpad database
std::unique_ptr<crashpad::CrashReportDatabase> database = crashpad::CrashReportDatabase::Initialize(reports_dir); std::unique_ptr<crashpad::CrashReportDatabase> database =
if (database == NULL) return false; crashpad::CrashReportDatabase::Initialize(reports_dir);
if (database == NULL)
return false;
// Disable automated crash uploads // Disable automated crash uploads
crashpad::Settings *settings = database->GetSettings(); crashpad::Settings *settings = database->GetSettings();
if (settings == NULL) return false; if (settings == NULL)
settings->SetUploadsEnabled(false); return false;
settings->SetUploadsEnabled(false);
// Start crash handler // Start crash handler
client = new crashpad::CrashpadClient(); client = new crashpad::CrashpadClient();
status = client->StartHandler(handler, reports_dir, metrics_dir, status = client->StartHandler(
"https://olivevideoeditor.org/crashpad/report.php", handler, reports_dir, metrics_dir,
annotations, arguments, true, true); "https://olivevideoeditor.org/crashpad/report.php", annotations,
} arguments, true, true);
}
// Override Crashpad exception filter with our own // Override Crashpad exception filter with our own
if (!status) { if (!status) {
qWarning() << "Failed to start Crashpad, automatic crash reporting will be disabled"; qWarning()
} << "Failed to start Crashpad, automatic crash reporting will be disabled";
}
return status; return status;
} }
#endif // USE_CRASHPAD #endif // USE_CRASHPAD
+1 -1
View File
@@ -35,6 +35,6 @@
// arrays encoded in UTF-16. // arrays encoded in UTF-16.
#define QSTRING_TO_BASE_STRING(x) x.toStdWString() #define QSTRING_TO_BASE_STRING(x) x.toStdWString()
#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x) #define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x)
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
#endif // CRASHPADUTILS_H #endif // CRASHPADUTILS_H
+29 -27
View File
@@ -20,38 +20,40 @@
#include "debug.h" #include "debug.h"
namespace olive { namespace olive
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{ {
QByteArray localMsg = msg.toLocal8Bit();
const char* msg_type = "UNKNOWN"; void DebugHandler(QtMsgType type, const QMessageLogContext &context,
switch (type) { const QString &msg)
case QtDebugMsg: {
msg_type = "DEBUG"; QByteArray localMsg = msg.toLocal8Bit();
break;
case QtInfoMsg:
msg_type = "INFO";
break;
case QtWarningMsg:
msg_type = "WARNING";
break;
case QtCriticalMsg:
msg_type = "ERROR";
break;
case QtFatalMsg:
msg_type = "FATAL";
break;
}
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line); const char *msg_type = "UNKNOWN";
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData()); switch (type) {
case QtDebugMsg:
msg_type = "DEBUG";
break;
case QtInfoMsg:
msg_type = "INFO";
break;
case QtWarningMsg:
msg_type = "WARNING";
break;
case QtCriticalMsg:
msg_type = "ERROR";
break;
case QtFatalMsg:
msg_type = "FATAL";
break;
}
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line // Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
// is flushed // is flushed
fflush(stderr); fflush(stderr);
#endif #endif
} }
+4 -2
View File
@@ -25,9 +25,11 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); void DebugHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg);
} }
+54 -55
View File
@@ -26,77 +26,76 @@
//#define ALLOW_RETURNING_INFINITY //#define ALLOW_RETURNING_INFINITY
namespace olive { namespace olive
class Decibel
{ {
class Decibel {
public: public:
// In basically all circumstances, this should calculate to 0.0 linear // In basically all circumstances, this should calculate to 0.0 linear
static constexpr double MINIMUM = -200.0; static constexpr double MINIMUM = -200.0;
static double fromLinear(double linear) static double fromLinear(double linear)
{ {
double v = double(20.0) * std::log10(linear); double v = double(20.0) * std::log10(linear);
#ifndef ALLOW_RETURNING_INFINITY #ifndef ALLOW_RETURNING_INFINITY
if (std::isinf(v)) { if (std::isinf(v)) {
return MINIMUM; return MINIMUM;
} }
#endif #endif
return v; return v;
} }
static double toLinear(double decibel) static double toLinear(double decibel)
{ {
double to_linear = std::pow(double(10.0), decibel / double(20.0)); double to_linear = std::pow(double(10.0), decibel / double(20.0));
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0 // Minimum threshold that we figure is close enough to 0 that we may as well just return 0
if (to_linear < 0.000001) { if (to_linear < 0.000001) {
return 0; return 0;
} else { } else {
return to_linear; return to_linear;
} }
} }
static double fromLogarithmic(double logarithmic) static double fromLogarithmic(double logarithmic)
{ {
if (logarithmic < 0.001) if (logarithmic < 0.001)
#ifdef ALLOW_RETURNING_INFINITY #ifdef ALLOW_RETURNING_INFINITY
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
#else #else
return MINIMUM; return MINIMUM;
#endif #endif
else if (logarithmic > 0.99) else if (logarithmic > 0.99)
return 0; return 0;
else else
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100); return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
} }
static double toLogarithmic(double decibel) static double toLogarithmic(double decibel)
{ {
if (qFuzzyIsNull(decibel)) { if (qFuzzyIsNull(decibel)) {
return 1; return 1;
} else { } else {
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100); return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
} }
} }
static double LinearToLogarithmic(double linear) static double LinearToLogarithmic(double linear)
{ {
return 1 - std::exp(-linear * LOG100); return 1 - std::exp(-linear * LOG100);
} }
static double LogarithmicToLinear(double logarithmic) static double LogarithmicToLinear(double logarithmic)
{ {
if (logarithmic > 0.99) { if (logarithmic > 0.99) {
return 1; return 1;
} else { } else {
return -std::log(1 - logarithmic) / LOG100; return -std::log(1 - logarithmic) / LOG100;
} }
} }
private: private:
static constexpr double LOG100 = 4.60517018599; static constexpr double LOG100 = 4.60517018599;
}; };
} }
+16 -12
View File
@@ -21,7 +21,8 @@
#ifndef OLIVECOMMONDEFINE_H #ifndef OLIVECOMMONDEFINE_H
#define OLIVECOMMONDEFINE_H #define OLIVECOMMONDEFINE_H
namespace olive { namespace olive
{
/// The minimum size an icon in ProjectExplorer can be /// The minimum size an icon in ProjectExplorer can be
const int kProjectIconSizeMinimum = 16; const int kProjectIconSizeMinimum = 16;
@@ -39,25 +40,28 @@ const int kBytesInGigabyte = 1073741824;
#define MACRO_NAME_AS_STR(s) #s #define MACRO_NAME_AS_STR(s) #s
#define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s) #define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s)
#define OLIVE_NS_CONST_ARG(x, y) QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y) #define OLIVE_NS_CONST_ARG(x, y) \
#define OLIVE_NS_ARG(x, y) QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y) QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_RETURN_ARG(x, y) QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y) #define OLIVE_NS_ARG(x, y) \
QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_RETURN_ARG(x, y) \
QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
/** /**
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we * Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
* use our own functions for portability. * use our own functions for portability.
*/ */
#define DISABLE_COPY(Class) \ #define DISABLE_COPY(Class) \
Class(const Class &) = delete;\ Class(const Class &) = delete; \
Class &operator=(const Class &) = delete; Class &operator=(const Class &) = delete;
#define DISABLE_MOVE(Class) \ #define DISABLE_MOVE(Class) \
Class(Class &&) = delete; \ Class(Class &&) = delete; \
Class &operator=(Class &&) = delete; Class &operator=(Class &&) = delete;
#define DISABLE_COPY_MOVE(Class) \ #define DISABLE_COPY_MOVE(Class) \
DISABLE_COPY(Class) \ DISABLE_COPY(Class) \
DISABLE_MOVE(Class) DISABLE_MOVE(Class)
#endif // OLIVECOMMONDEFINE_H #endif // OLIVECOMMONDEFINE_H
+10 -9
View File
@@ -23,21 +23,22 @@
#include <stdint.h> #include <stdint.h>
namespace olive { namespace olive
{
inline int64_t GetDigitCount(int64_t input) inline int64_t GetDigitCount(int64_t input)
{ {
input = std::abs(input); input = std::abs(input);
int64_t lim = 10; int64_t lim = 10;
int64_t digit = 1; int64_t digit = 1;
while (input >= lim) { while (input >= lim) {
lim *= 10; lim *= 10;
digit++; digit++;
} }
return digit; return digit;
} }
} }
+148 -141
View File
@@ -20,180 +20,187 @@
#include "common/ffmpegutils.h" #include "common/ffmpegutils.h"
namespace olive { namespace olive
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum)
{ {
AVPixelFormat possible_pix_fmts[3];
possible_pix_fmts[0] = AV_PIX_FMT_RGBA; AVPixelFormat
FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
PixelFormat maximum)
{
AVPixelFormat possible_pix_fmts[3];
if (maximum == PixelFormat::U8) { possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
} else {
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
}
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, if (maximum == PixelFormat::U8) {
pix_fmt, possible_pix_fmts[1] = AV_PIX_FMT_NONE;
1, } else {
nullptr); possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
}
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt, 1,
nullptr);
} }
SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt) SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
{ {
switch (smp_fmt) { switch (smp_fmt) {
case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8:
return SampleFormat::U8; return SampleFormat::U8;
case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16:
return SampleFormat::S16; return SampleFormat::S16;
case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32:
return SampleFormat::S32; return SampleFormat::S32;
case AV_SAMPLE_FMT_S64: case AV_SAMPLE_FMT_S64:
return SampleFormat::S64; return SampleFormat::S64;
case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLT:
return SampleFormat::F32; return SampleFormat::F32;
case AV_SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBL:
return SampleFormat::F64; return SampleFormat::F64;
case AV_SAMPLE_FMT_U8P : case AV_SAMPLE_FMT_U8P:
return SampleFormat::U8P; return SampleFormat::U8P;
case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S16P:
return SampleFormat::S16P; return SampleFormat::S16P;
case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_S32P:
return SampleFormat::S32P; return SampleFormat::S32P;
case AV_SAMPLE_FMT_S64P: case AV_SAMPLE_FMT_S64P:
return SampleFormat::S64P; return SampleFormat::S64P;
case AV_SAMPLE_FMT_FLTP: case AV_SAMPLE_FMT_FLTP:
return SampleFormat::F32P; return SampleFormat::F32P;
case AV_SAMPLE_FMT_DBLP: case AV_SAMPLE_FMT_DBLP:
return SampleFormat::F64P; return SampleFormat::F64P;
case AV_SAMPLE_FMT_NONE: case AV_SAMPLE_FMT_NONE:
case AV_SAMPLE_FMT_NB: case AV_SAMPLE_FMT_NB:
break; break;
} }
return SampleFormat::INVALID; return SampleFormat::INVALID;
} }
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const SampleFormat &smp_fmt) AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const SampleFormat &smp_fmt)
{ {
switch (smp_fmt) { switch (smp_fmt) {
case SampleFormat::U8: case SampleFormat::U8:
return AV_SAMPLE_FMT_U8; return AV_SAMPLE_FMT_U8;
case SampleFormat::S16: case SampleFormat::S16:
return AV_SAMPLE_FMT_S16; return AV_SAMPLE_FMT_S16;
case SampleFormat::S32: case SampleFormat::S32:
return AV_SAMPLE_FMT_S32; return AV_SAMPLE_FMT_S32;
case SampleFormat::S64: case SampleFormat::S64:
return AV_SAMPLE_FMT_S64; return AV_SAMPLE_FMT_S64;
case SampleFormat::F32: case SampleFormat::F32:
return AV_SAMPLE_FMT_FLT; return AV_SAMPLE_FMT_FLT;
case SampleFormat::F64: case SampleFormat::F64:
return AV_SAMPLE_FMT_DBL; return AV_SAMPLE_FMT_DBL;
case SampleFormat::U8P: case SampleFormat::U8P:
return AV_SAMPLE_FMT_U8P; return AV_SAMPLE_FMT_U8P;
case SampleFormat::S16P: case SampleFormat::S16P:
return AV_SAMPLE_FMT_S16P; return AV_SAMPLE_FMT_S16P;
case SampleFormat::S32P: case SampleFormat::S32P:
return AV_SAMPLE_FMT_S32P; return AV_SAMPLE_FMT_S32P;
case SampleFormat::S64P: case SampleFormat::S64P:
return AV_SAMPLE_FMT_S64P; return AV_SAMPLE_FMT_S64P;
case SampleFormat::F32P: case SampleFormat::F32P:
return AV_SAMPLE_FMT_FLTP; return AV_SAMPLE_FMT_FLTP;
case SampleFormat::F64P: case SampleFormat::F64P:
return AV_SAMPLE_FMT_DBLP; return AV_SAMPLE_FMT_DBLP;
case SampleFormat::INVALID: case SampleFormat::INVALID:
case SampleFormat::COUNT: case SampleFormat::COUNT:
break; break;
} }
return AV_SAMPLE_FMT_NONE; return AV_SAMPLE_FMT_NONE;
} }
int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs) int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
{ {
switch (cs) { switch (cs) {
case AVCOL_SPC_BT709: case AVCOL_SPC_BT709:
return SWS_CS_ITU709; return SWS_CS_ITU709;
case AVCOL_SPC_FCC: case AVCOL_SPC_FCC:
return SWS_CS_FCC; return SWS_CS_FCC;
case AVCOL_SPC_BT470BG: case AVCOL_SPC_BT470BG:
return SWS_CS_ITU624; return SWS_CS_ITU624;
case AVCOL_SPC_SMPTE170M: case AVCOL_SPC_SMPTE170M:
return SWS_CS_SMPTE170M; return SWS_CS_SMPTE170M;
case AVCOL_SPC_SMPTE240M: case AVCOL_SPC_SMPTE240M:
return SWS_CS_SMPTE240M; return SWS_CS_SMPTE240M;
case AVCOL_SPC_BT2020_NCL: case AVCOL_SPC_BT2020_NCL:
return SWS_CS_BT2020; return SWS_CS_BT2020;
default: default:
break; break;
} }
return SWS_CS_DEFAULT; return SWS_CS_DEFAULT;
} }
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f) AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
{ {
switch (f) { switch (f) {
case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P; case AV_PIX_FMT_YUVJ420P:
case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P; return AV_PIX_FMT_YUV420P;
case AV_PIX_FMT_YUVJ444P: return AV_PIX_FMT_YUV444P; case AV_PIX_FMT_YUVJ422P:
case AV_PIX_FMT_YUVJ440P: return AV_PIX_FMT_YUV440P; return AV_PIX_FMT_YUV422P;
case AV_PIX_FMT_YUVJ411P: return AV_PIX_FMT_YUV411P; case AV_PIX_FMT_YUVJ444P:
default: return AV_PIX_FMT_YUV444P;
break; case AV_PIX_FMT_YUVJ440P:
} return AV_PIX_FMT_YUV440P;
case AV_PIX_FMT_YUVJ411P:
return AV_PIX_FMT_YUV411P;
default:
break;
}
return f; return f;
} }
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt, int channel_layout) AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
int channel_layout)
{ {
if (channel_layout == VideoParams::kRGBChannelCount) { if (channel_layout == VideoParams::kRGBChannelCount) {
switch (pix_fmt) { switch (pix_fmt) {
case PixelFormat::U8: case PixelFormat::U8:
return AV_PIX_FMT_RGB24; return AV_PIX_FMT_RGB24;
case PixelFormat::U16: case PixelFormat::U16:
return AV_PIX_FMT_RGB48; return AV_PIX_FMT_RGB48;
case PixelFormat::F16: case PixelFormat::F16:
case PixelFormat::F32: case PixelFormat::F32:
case PixelFormat::INVALID: case PixelFormat::INVALID:
case PixelFormat::COUNT: case PixelFormat::COUNT:
break; break;
} }
} else if (channel_layout == VideoParams::kRGBAChannelCount) { } else if (channel_layout == VideoParams::kRGBAChannelCount) {
switch (pix_fmt) { switch (pix_fmt) {
case PixelFormat::U8: case PixelFormat::U8:
return AV_PIX_FMT_RGBA; return AV_PIX_FMT_RGBA;
case PixelFormat::U16: case PixelFormat::U16:
return AV_PIX_FMT_RGBA64; return AV_PIX_FMT_RGBA64;
case PixelFormat::F16: case PixelFormat::F16:
case PixelFormat::F32: case PixelFormat::F32:
case PixelFormat::INVALID: case PixelFormat::INVALID:
case PixelFormat::COUNT: case PixelFormat::COUNT:
break; break;
} }
} }
return AV_PIX_FMT_NONE; return AV_PIX_FMT_NONE;
} }
PixelFormat FFmpegUtils::GetCompatiblePixelFormat(const PixelFormat &pix_fmt) PixelFormat FFmpegUtils::GetCompatiblePixelFormat(const PixelFormat &pix_fmt)
{ {
switch (pix_fmt) { switch (pix_fmt) {
case PixelFormat::U8: case PixelFormat::U8:
return PixelFormat::U8; return PixelFormat::U8;
case PixelFormat::U16: case PixelFormat::U16:
case PixelFormat::F16: case PixelFormat::F16:
case PixelFormat::F32: case PixelFormat::F32:
return PixelFormat::U16; return PixelFormat::U16;
case PixelFormat::INVALID: case PixelFormat::INVALID:
case PixelFormat::COUNT: case PixelFormat::COUNT:
break; break;
} }
return PixelFormat::INVALID; return PixelFormat::INVALID;
} }
} }
+21 -17
View File
@@ -31,63 +31,67 @@ extern "C" {
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
{
using namespace core; using namespace core;
class FFmpegUtils { class FFmpegUtils {
public: public:
/** /**
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss * @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
*/ */
static AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt, PixelFormat maximum = PixelFormat::INVALID); static AVPixelFormat
GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
PixelFormat maximum = PixelFormat::INVALID);
/** /**
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss * @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
*/ */
static PixelFormat GetCompatiblePixelFormat(const PixelFormat& pix_fmt); static PixelFormat GetCompatiblePixelFormat(const PixelFormat &pix_fmt);
/** /**
* @brief Returns an FFmpeg pixel format for a given native pixel format * @brief Returns an FFmpeg pixel format for a given native pixel format
*/ */
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat& pix_fmt, int channel_layout); static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
int channel_layout);
/** /**
* @brief Returns a native sample format type for a given AVSampleFormat * @brief Returns a native sample format type for a given AVSampleFormat
*/ */
static SampleFormat GetNativeSampleFormat(const AVSampleFormat& smp_fmt); static SampleFormat GetNativeSampleFormat(const AVSampleFormat &smp_fmt);
/** /**
* @brief Returns an FFmpeg sample format type for a given native type * @brief Returns an FFmpeg sample format type for a given native type
*/ */
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt); static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
/** /**
* @brief Returns an SWS_CS_* macro from an AVColorSpace enum member * @brief Returns an SWS_CS_* macro from an AVColorSpace enum member
* *
* Why aren't these the same thing anyway? And for that matter, why doesn't FFmpeg provide a * Why aren't these the same thing anyway? And for that matter, why doesn't FFmpeg provide a
* convenience function to do this conversion for us? Who knows, but here we are. * convenience function to do this conversion for us? Who knows, but here we are.
*/ */
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs); static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
/** /**
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart * @brief Convert "JPEG"/full-range colorspace to its regular counterpart
* *
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the * "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range * time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
* aware), we use this function. * aware), we use this function.
*/ */
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f); static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
}; };
using AVFramePtr = std::shared_ptr<AVFrame>; using AVFramePtr = std::shared_ptr<AVFrame>;
inline AVFramePtr CreateAVFramePtr(AVFrame *f) inline AVFramePtr CreateAVFramePtr(AVFrame *f)
{ {
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); }); return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
} }
inline AVFramePtr CreateAVFramePtr() inline AVFramePtr CreateAVFramePtr()
{ {
return CreateAVFramePtr(av_frame_alloc()); return CreateAVFramePtr(av_frame_alloc());
} }
} }
+142 -125
View File
@@ -29,206 +29,223 @@
#include "config/config.h" #include "config/config.h"
namespace olive { namespace olive
{
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename) QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
{ {
QFileInfo info(filename); QFileInfo info(filename);
if (!info.exists()) { if (!info.exists()) {
return QString(); return QString();
} }
QCryptographicHash hash(QCryptographicHash::Sha1); QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(info.absoluteFilePath().toUtf8()); hash.addData(info.absoluteFilePath().toUtf8());
hash.addData(QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8()); hash.addData(
QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
QByteArray result = hash.result(); QByteArray result = hash.result();
return QString(result.toHex()); return QString(result.toHex());
} }
QString FileFunctions::GetConfigurationLocation() QString FileFunctions::GetConfigurationLocation()
{ {
if (IsPortable()) { if (IsPortable()) {
return GetApplicationPath(); return GetApplicationPath();
} else { } else {
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString s =
QDir(s).mkpath("."); QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
return s; QDir(s).mkpath(".");
} return s;
}
} }
bool FileFunctions::IsPortable() bool FileFunctions::IsPortable()
{ {
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable")); return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
} }
QString FileFunctions::GetApplicationPath() QString FileFunctions::GetApplicationPath()
{ {
return QCoreApplication::applicationDirPath(); return QCoreApplication::applicationDirPath();
} }
QString FileFunctions::GetTempFilePath() QString FileFunctions::GetTempFilePath()
{ {
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)) QString temp_path =
.filePath(QCoreApplication::organizationName())) QDir(
.filePath(QCoreApplication::applicationName()); QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
.filePath(QCoreApplication::organizationName()))
.filePath(QCoreApplication::applicationName());
// Ensure it exists // Ensure it exists
QDir(temp_path).mkpath("."); QDir(temp_path).mkpath(".");
return temp_path; return temp_path;
} }
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest) bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString &source,
const QString &dest)
{ {
QFileInfoList info_list = QDir(source).entryInfoList(); QFileInfoList info_list = QDir(source).entryInfoList();
foreach (const QFileInfo& info, info_list) { foreach (const QFileInfo &info, info_list) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually // QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) { if (info.fileName() == QStringLiteral(".") ||
continue; info.fileName() == QStringLiteral("..")) {
} continue;
}
QString dest_equivalent = QDir(dest).filePath(info.fileName()); QString dest_equivalent = QDir(dest).filePath(info.fileName());
if (info.isDir()) { if (info.isDir()) {
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(), dest_equivalent)) { if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(),
return false; dest_equivalent)) {
} return false;
} else if (QFileInfo::exists(dest_equivalent)) { }
return false; } else if (QFileInfo::exists(dest_equivalent)) {
} return false;
} }
}
return true; return true;
} }
void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bool overwrite) void FileFunctions::CopyDirectory(const QString &source, const QString &dest,
bool overwrite)
{ {
QDir d(source); QDir d(source);
if (!d.exists()) { if (!d.exists()) {
qCritical() << "Failed to copy directory, source" << source << "didn't exist"; qCritical()
return; << "Failed to copy directory, source" << source << "didn't exist";
} return;
}
QDir dest_dir(dest); QDir dest_dir(dest);
if (!dest_dir.mkpath(QStringLiteral("."))) { if (!dest_dir.mkpath(QStringLiteral("."))) {
qCritical() << "Failed to create destination directory" << dest; qCritical() << "Failed to create destination directory" << dest;
return; return;
} }
QFileInfoList l = d.entryInfoList(); QFileInfoList l = d.entryInfoList();
foreach (const QFileInfo& info, l) { foreach (const QFileInfo &info, l) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually // QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) { if (info.fileName() == QStringLiteral(".") ||
continue; info.fileName() == QStringLiteral("..")) {
} continue;
}
QString dest_file_path = dest_dir.filePath(info.fileName()); QString dest_file_path = dest_dir.filePath(info.fileName());
if (info.isDir()) { if (info.isDir()) {
// Copy dir // Copy dir
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite); CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
} else { } else {
// Copy file // Copy file
if (overwrite && QFile::exists(dest_file_path)) { if (overwrite && QFile::exists(dest_file_path)) {
QFile file(dest_file_path); QFile file(dest_file_path);
file.setPermissions(file.permissions() | QFileDevice::WriteOwner | QFileDevice::WriteUser | file.setPermissions(
QFileDevice::WriteGroup | QFileDevice::WriteOther); file.permissions() | QFileDevice::WriteOwner |
file.remove(); QFileDevice::WriteUser | QFileDevice::WriteGroup |
} QFileDevice::WriteOther);
file.remove();
}
QFile::copy(info.absoluteFilePath(), dest_file_path); QFile::copy(info.absoluteFilePath(), dest_file_path);
} }
} }
} }
bool FileFunctions::DirectoryIsValid(const QDir &d, bool try_to_create_if_not_exists) bool FileFunctions::DirectoryIsValid(const QDir &d,
bool try_to_create_if_not_exists)
{ {
// Return whether the directory exists, or whether it could be created if it doesn't // Return whether the directory exists, or whether it could be created if it doesn't
return d.exists() || d.mkpath(QStringLiteral(".")); return d.exists() || d.mkpath(QStringLiteral("."));
} }
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension) QString FileFunctions::EnsureFilenameExtension(QString fn,
const QString &extension)
{ {
// No-op if either input is empty // No-op if either input is empty
if (!fn.isEmpty() && !extension.isEmpty()) { if (!fn.isEmpty() && !extension.isEmpty()) {
QString extension_with_dot; QString extension_with_dot;
extension_with_dot.append('.'); extension_with_dot.append('.');
extension_with_dot.append(extension); extension_with_dot.append(extension);
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) { if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
fn.append(extension_with_dot); fn.append(extension_with_dot);
} }
} }
return fn; return fn;
} }
QString FileFunctions::ReadFileAsString(const QString &filename) QString FileFunctions::ReadFileAsString(const QString &filename)
{ {
QFile f(filename); QFile f(filename);
QString file_data; QString file_data;
if (f.open(QFile::ReadOnly | QFile::Text)) { if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream text_stream(&f); QTextStream text_stream(&f);
file_data = text_stream.readAll(); file_data = text_stream.readAll();
f.close(); f.close();
} }
return file_data; return file_data;
} }
QString FileFunctions::GetSafeTemporaryFilename(const QString &original) QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
{ {
int counter = 0; int counter = 0;
QFileInfo original_info(original); QFileInfo original_info(original);
QString basename = original_info.baseName(); QString basename = original_info.baseName();
QString complete_suffix = original_info.completeSuffix(); QString complete_suffix = original_info.completeSuffix();
// If we have a complete suffix, make sure there's a period in it // If we have a complete suffix, make sure there's a period in it
if (!complete_suffix.isEmpty()) { if (!complete_suffix.isEmpty()) {
complete_suffix.prepend('.'); complete_suffix.prepend('.');
} }
QString temp_abs_path; QString temp_abs_path;
do { do {
temp_abs_path = original_info.dir().filePath( temp_abs_path = original_info.dir().filePath(
QStringLiteral("%1.tmp%2%3").arg(basename, QStringLiteral("%1.tmp%2%3")
QString::number(counter), .arg(basename, QString::number(counter), complete_suffix));
complete_suffix)); counter++;
counter++; } while (QFileInfo::exists(temp_abs_path));
} while (QFileInfo::exists(temp_abs_path));
return temp_abs_path; return temp_abs_path;
} }
bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString &to) bool FileFunctions::RenameFileAllowOverwrite(const QString &from,
const QString &to)
{ {
if (QFileInfo::exists(to) && !QFile::remove(to)) { if (QFileInfo::exists(to) && !QFile::remove(to)) {
qCritical() << "Couldn't remove existing file" << to << "for overwrite"; qCritical() << "Couldn't remove existing file" << to << "for overwrite";
return false; return false;
} }
// By this point, we can assume `to` either never existed or has now been deleted // By this point, we can assume `to` either never existed or has now been deleted
if (!QFile::rename(from, to)) { if (!QFile::rename(from, to)) {
qCritical() << "Failed to rename file" << from << "to" << to; qCritical() << "Failed to rename file" << from << "to" << to;
return false; return false;
} }
return true; return true;
} }
QString FileFunctions::GetAutoRecoveryRoot() QString FileFunctions::GetAutoRecoveryRoot()
{ {
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery")); return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppLocalDataLocation))
.filePath(QStringLiteral("autorecovery"));
} }
} }
+29 -26
View File
@@ -26,36 +26,40 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
/** /**
* @brief A collection of static file and directory functions * @brief A collection of static file and directory functions
*/ */
class FileFunctions { class FileFunctions {
public: public:
/** /**
* @brief Returns true if the application is running in portable mode * @brief Returns true if the application is running in portable mode
* *
* In portable mode, any persistent configuration files should be made in a path relative to the application rather * In portable mode, any persistent configuration files should be made in a path relative to the application rather
* than in the user's home folder. * than in the user's home folder.
*/ */
static bool IsPortable(); static bool IsPortable();
static QString GetUniqueFileIdentifier(const QString& filename); static QString GetUniqueFileIdentifier(const QString &filename);
static QString GetConfigurationLocation(); static QString GetConfigurationLocation();
static QString GetApplicationPath(); static QString GetApplicationPath();
static QString GetTempFilePath(); static QString GetTempFilePath();
static bool CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest); static bool CanCopyDirectoryWithoutOverwriting(const QString &source,
const QString &dest);
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false); static void CopyDirectory(const QString &source, const QString &dest,
bool overwrite = false);
static bool DirectoryIsValid(const QDir& dir, bool try_to_create_if_not_exists = true); static bool DirectoryIsValid(const QDir &dir,
bool try_to_create_if_not_exists = true);
/** /**
* @brief Ensures a given filename has a certain extension * @brief Ensures a given filename has a certain extension
* *
* Checks if the filename has the extension provided and appends it if not. The extension is * Checks if the filename has the extension provided and appends it if not. The extension is
@@ -64,11 +68,12 @@ public:
* @return The filename provided either untouched or with the extension appended to it. * @return The filename provided either untouched or with the extension appended to it.
*/ */
static QString EnsureFilenameExtension(QString fn, const QString& extension); static QString EnsureFilenameExtension(QString fn,
const QString &extension);
static QString ReadFileAsString(const QString& filename); static QString ReadFileAsString(const QString &filename);
/** /**
* @brief Returns a temporary filename that can be used while writing rather than the original * @brief Returns a temporary filename that can be used while writing rather than the original
* *
* If overwriting a file, it's safest to write to a new file first and then only replace it at * If overwriting a file, it's safest to write to a new file first and then only replace it at
@@ -78,28 +83,26 @@ public:
* This function returns a slight variant of the filename provided that's guaranteed to not exist * This function returns a slight variant of the filename provided that's guaranteed to not exist
* and therefore won't overwrite anything important. * and therefore won't overwrite anything important.
*/ */
static QString GetSafeTemporaryFilename(const QString& original); static QString GetSafeTemporaryFilename(const QString &original);
/** /**
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first * @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
*/ */
static bool RenameFileAllowOverwrite(const QString& from, const QString& to); static bool RenameFileAllowOverwrite(const QString &from,
const QString &to);
inline static QString GetFormattedExecutableForPlatform(QString unformatted) inline static QString GetFormattedExecutableForPlatform(QString unformatted)
{ {
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
unformatted.append(QStringLiteral(".exe")); unformatted.append(QStringLiteral(".exe"));
#endif #endif
return unformatted; return unformatted;
} }
static QString GetAutoRecoveryRoot();
static QString GetAutoRecoveryRoot();
}; };
} }
#endif // FILEFUNCTIONS_H #endif // FILEFUNCTIONS_H
+349 -323
View File
@@ -5,446 +5,472 @@
#include "xmlutils.h" #include "xmlutils.h"
namespace olive { namespace olive
{
const QVector<QString> Html::kBlockTags = { const QVector<QString> Html::kBlockTags = { QStringLiteral("p"),
QStringLiteral("p"), QStringLiteral("div") };
QStringLiteral("div")
};
inline bool StrEquals(const QStringView &a, const QStringView &b) inline bool StrEquals(const QStringView &a, const QStringView &b)
{ {
return !a.compare(b, Qt::CaseInsensitive); return !a.compare(b, Qt::CaseInsensitive);
} }
QString Html::DocToHtml(const QTextDocument *doc) QString Html::DocToHtml(const QTextDocument *doc)
{ {
QString html; QString html;
QXmlStreamWriter writer(&html); QXmlStreamWriter writer(&html);
//writer.setAutoFormatting(true); //writer.setAutoFormatting(true);
for (auto it=doc->begin(); it!=doc->end(); it=it.next()) { for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
WriteBlock(&writer, it); WriteBlock(&writer, it);
} }
return html; return html;
} }
struct HtmlNode { struct HtmlNode {
QString tag; QString tag;
QTextCharFormat format; QTextCharFormat format;
}; };
QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack) QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
{ {
QTextCharFormat f; QTextCharFormat f;
for (int i=0; i<stack.size(); i++) { for (int i = 0; i < stack.size(); i++) {
f.merge(stack.at(i).format); f.merge(stack.at(i).format);
} }
return f; return f;
} }
void Html::HtmlToDoc(QTextDocument *doc, const QString &html) void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
{ {
// Empty doc // Empty doc
doc->clear(); doc->clear();
bool inside_block = true; bool inside_block = true;
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments // Create cursor, which appears to be Qt's official way of inserting blocks and fragments
QTextCursor c(doc); QTextCursor c(doc);
QString wrapped = QStringLiteral("<html>").append(html).append("</html>"); QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
QXmlStreamReader reader(wrapped); QXmlStreamReader reader(wrapped);
QVector<HtmlNode> fmt_stack; QVector<HtmlNode> fmt_stack;
QTextCharFormat default_fmt; QTextCharFormat default_fmt;
default_fmt.setFontWeight(QFont::Normal); default_fmt.setFontWeight(QFont::Normal);
fmt_stack.append({QStringLiteral("html"), default_fmt}); fmt_stack.append({ QStringLiteral("html"), default_fmt });
QTextCharFormat current_fmt; QTextCharFormat current_fmt;
while (!reader.atEnd()) { while (!reader.atEnd()) {
reader.readNext(); reader.readNext();
if (reader.tokenType() == QXmlStreamReader::StartElement) { if (reader.tokenType() == QXmlStreamReader::StartElement) {
QString tag = reader.name().toString().toLower();
QString tag = reader.name().toString().toLower(); fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) });
current_fmt = MergeHtmlFormats(fmt_stack);
fmt_stack.append({tag, ReadCharFormat(reader.attributes())}); if (kBlockTags.contains(tag)) {
current_fmt = MergeHtmlFormats(fmt_stack); QTextBlockFormat block_fmt =
ReadBlockFormat(reader.attributes());
if (inside_block) {
c.setBlockFormat(block_fmt);
c.setBlockCharFormat(current_fmt);
} else {
c.insertBlock(block_fmt, current_fmt);
inside_block = true;
}
}
if (kBlockTags.contains(tag)) { } else if (reader.tokenType() == QXmlStreamReader::Characters) {
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes()); QString characters = reader.text().toString();
if (inside_block) { c.insertText(characters, current_fmt);
c.setBlockFormat(block_fmt);
c.setBlockCharFormat(current_fmt);
} else {
c.insertBlock(block_fmt, current_fmt);
inside_block = true;
}
}
} else if (reader.tokenType() == QXmlStreamReader::Characters) { } else if (reader.tokenType() == QXmlStreamReader::EndElement) {
QString tag = reader.name().toString().toLower();
QString characters = reader.text().toString(); for (int i = fmt_stack.size() - 1; i >= 0; i--) {
c.insertText(characters, current_fmt); if (fmt_stack.at(i).tag == tag) {
fmt_stack.removeAt(i);
current_fmt = MergeHtmlFormats(fmt_stack);
} else if (reader.tokenType() == QXmlStreamReader::EndElement) { if (kBlockTags.contains(tag)) {
inside_block = false;
}
break;
}
}
}
}
QString tag = reader.name().toString().toLower(); if (reader.error()) {
qCritical() << "Failed to parse HTML:" << reader.errorString();
for (int i=fmt_stack.size()-1; i>=0; i--) { }
if (fmt_stack.at(i).tag == tag) {
fmt_stack.removeAt(i);
current_fmt = MergeHtmlFormats(fmt_stack);
if (kBlockTags.contains(tag)) {
inside_block = false;
}
break;
}
}
}
}
if (reader.error()) {
qCritical() << "Failed to parse HTML:" << reader.errorString();
}
} }
void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block) void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
{ {
writer->writeStartElement(QStringLiteral("p")); writer->writeStartElement(QStringLiteral("p"));
const QTextBlockFormat &fmt = block.blockFormat(); const QTextBlockFormat &fmt = block.blockFormat();
// Write block alignment // Write block alignment
if (!(fmt.alignment() & Qt::AlignLeft)) { if (!(fmt.alignment() & Qt::AlignLeft)) {
if (fmt.alignment() & Qt::AlignRight) { if (fmt.alignment() & Qt::AlignRight) {
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("right")); writer->writeAttribute(QStringLiteral("align"),
} else if (fmt.alignment() & Qt::AlignHCenter) { QStringLiteral("right"));
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center")); } else if (fmt.alignment() & Qt::AlignHCenter) {
} else if (fmt.alignment() & Qt::AlignJustify) { writer->writeAttribute(QStringLiteral("align"),
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify")); QStringLiteral("center"));
} } else if (fmt.alignment() & Qt::AlignJustify) {
} writer->writeAttribute(QStringLiteral("align"),
QStringLiteral("justify"));
}
}
// RTL support // RTL support
if (block.textDirection() == Qt::RightToLeft) { if (block.textDirection() == Qt::RightToLeft) {
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl")); writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
} }
// Write CSS attributes // Write CSS attributes
QString style; QString style;
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) { if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
WriteCSSProperty(&style, QStringLiteral("line-height"), QStringLiteral("%1%").arg(fmt.lineHeight())); WriteCSSProperty(&style, QStringLiteral("line-height"),
} QStringLiteral("%1%").arg(fmt.lineHeight()));
}
WriteCharFormat(&style, block.charFormat()); WriteCharFormat(&style, block.charFormat());
if (!style.isEmpty()) { if (!style.isEmpty()) {
writer->writeAttribute(QStringLiteral("style"), style); writer->writeAttribute(QStringLiteral("style"), style);
} }
auto it = block.begin(); auto it = block.begin();
if (it != block.end()) { if (it != block.end()) {
for (; it!=block.end(); it++) { for (; it != block.end(); it++) {
WriteFragment(writer, it.fragment()); WriteFragment(writer, it.fragment());
} }
} }
writer->writeEndElement(); // p writer->writeEndElement(); // p
} }
void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment) void Html::WriteFragment(QXmlStreamWriter *writer,
const QTextFragment &fragment)
{ {
const QTextCharFormat &fmt = fragment.charFormat(); const QTextCharFormat &fmt = fragment.charFormat();
writer->writeStartElement(QStringLiteral("span")); writer->writeStartElement(QStringLiteral("span"));
// Write CSS attributes // Write CSS attributes
QString style; QString style;
WriteCharFormat(&style, fmt); WriteCharFormat(&style, fmt);
if (!style.isEmpty()) { if (!style.isEmpty()) {
writer->writeAttribute(QStringLiteral("style"), style); writer->writeAttribute(QStringLiteral("style"), style);
} }
QStringList lines = fragment.text().split(QChar::LineSeparator); QStringList lines = fragment.text().split(QChar::LineSeparator);
bool first_line = true; bool first_line = true;
foreach (const QString &l, lines) { foreach (const QString &l, lines) {
if (first_line) { if (first_line) {
first_line = false; first_line = false;
} else { } else {
writer->writeEmptyElement(QStringLiteral("br")); writer->writeEmptyElement(QStringLiteral("br"));
} }
writer->writeCharacters(l); writer->writeCharacters(l);
} }
writer->writeEndElement(); // span writer->writeEndElement(); // span
} }
void Html::WriteCSSProperty(QString *style, const QString &key, const QStringList &values) void Html::WriteCSSProperty(QString *style, const QString &key,
const QStringList &values)
{ {
QString value; QString value;
foreach (QString v, values) { foreach (QString v, values) {
if (v.contains(' ')) { if (v.contains(' ')) {
v = QStringLiteral("'%1'").arg(v); v = QStringLiteral("'%1'").arg(v);
} }
AppendStringAutoSpace(&value, v); AppendStringAutoSpace(&value, v);
} }
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value)); AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
} }
void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt) void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
{ {
QStringList families = fmt.fontFamilies().toStringList(); QStringList families = fmt.fontFamilies().toStringList();
if (!families.isEmpty()) { if (!families.isEmpty()) {
WriteCSSProperty(style, QStringLiteral("font-family"), families.first()); WriteCSSProperty(style, QStringLiteral("font-family"),
} families.first());
}
if (fmt.hasProperty(QTextFormat::FontPointSize)) { if (fmt.hasProperty(QTextFormat::FontPointSize)) {
WriteCSSProperty(style, QStringLiteral("font-size"), QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize()))); WriteCSSProperty(
} style, QStringLiteral("font-size"),
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
}
if (fmt.hasProperty(QTextFormat::FontWeight)) { if (fmt.hasProperty(QTextFormat::FontWeight)) {
WriteCSSProperty(style, QStringLiteral("font-weight"), QString::number(fmt.fontWeight() * 8)); WriteCSSProperty(style, QStringLiteral("font-weight"),
} QString::number(fmt.fontWeight() * 8));
}
if (fmt.hasProperty(QTextFormat::FontItalic)) { if (fmt.hasProperty(QTextFormat::FontItalic)) {
WriteCSSProperty(style, QStringLiteral("font-style"), fmt.fontItalic() ? QStringLiteral("italic") : QStringLiteral("normal")); WriteCSSProperty(style, QStringLiteral("font-style"),
} fmt.fontItalic() ? QStringLiteral("italic") :
QStringLiteral("normal"));
}
if (fmt.hasProperty(QTextFormat::FontStyleName)) { if (fmt.hasProperty(QTextFormat::FontStyleName)) {
WriteCSSProperty(style, QStringLiteral("-ove-font-style"), fmt.fontStyleName().toString()); WriteCSSProperty(style, QStringLiteral("-ove-font-style"),
} fmt.fontStyleName().toString());
}
QStringList deco; QStringList deco;
if (fmt.fontUnderline()) { if (fmt.fontUnderline()) {
deco.append(QStringLiteral("underline")); deco.append(QStringLiteral("underline"));
} }
if (fmt.fontStrikeOut()) { if (fmt.fontStrikeOut()) {
deco.append(QStringLiteral("line-through")); deco.append(QStringLiteral("line-through"));
} }
if (fmt.fontOverline()) { if (fmt.fontOverline()) {
deco.append(QStringLiteral("overline")); deco.append(QStringLiteral("overline"));
} }
if (!deco.isEmpty()) { if (!deco.isEmpty()) {
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco); WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
} }
if (fmt.foreground().style() != Qt::NoBrush) { if (fmt.foreground().style() != Qt::NoBrush) {
const QColor &color = fmt.foreground().color(); const QColor &color = fmt.foreground().color();
QString cs; QString cs;
if (color.alpha() == 255) { if (color.alpha() == 255) {
cs = color.name(); cs = color.name();
} else if (color.alpha()) { } else if (color.alpha()) {
cs = QStringLiteral("rgba(%1, %2, %3, %4)").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()), QString::number(color.alphaF())); cs = QStringLiteral("rgba(%1, %2, %3, %4)")
} .arg(QString::number(color.red()),
QString::number(color.green()),
QString::number(color.blue()),
QString::number(color.alphaF()));
}
WriteCSSProperty(style, QStringLiteral("color"), cs); WriteCSSProperty(style, QStringLiteral("color"), cs);
} }
if (fmt.fontCapitalization() != QFont::MixedCase) { if (fmt.fontCapitalization() != QFont::MixedCase) {
if (fmt.fontCapitalization() == QFont::SmallCaps) { if (fmt.fontCapitalization() == QFont::SmallCaps) {
WriteCSSProperty(style, QStringLiteral("font-variant"), QStringLiteral("small-caps")); WriteCSSProperty(style, QStringLiteral("font-variant"),
// TODO: Add others QStringLiteral("small-caps"));
} // TODO: Add others
} }
}
if (fmt.fontLetterSpacing() != 0.0) { if (fmt.fontLetterSpacing() != 0.0) {
WriteCSSProperty(style, QStringLiteral("letter-spacing"), QStringLiteral("%1%").arg(QString::number(fmt.fontLetterSpacing()))); WriteCSSProperty(style, QStringLiteral("letter-spacing"),
} QStringLiteral("%1%").arg(
QString::number(fmt.fontLetterSpacing())));
}
if (fmt.fontStretch() != 0) { if (fmt.fontStretch() != 0) {
WriteCSSProperty(style, QStringLiteral("font-stretch"), QStringLiteral("%1%").arg(QString::number(fmt.fontStretch()))); WriteCSSProperty(
} style, QStringLiteral("font-stretch"),
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
}
} }
QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes) QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
{ {
QTextCharFormat fmt; QTextCharFormat fmt;
foreach (const QXmlStreamAttribute &attr, attributes) { foreach (const QXmlStreamAttribute &attr, attributes) {
if (StrEquals(attr.name(), QStringLiteral("style"))) { if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString()); auto css = GetCSSFromStyle(attr.value().toString());
for (auto it=css.begin(); it!=css.end(); it++) { for (auto it = css.begin(); it != css.end(); it++) {
const QString &first_val = it.value().first(); const QString &first_val = it.value().first();
if (it.key() == QStringLiteral("font-family")) { if (it.key() == QStringLiteral("font-family")) {
fmt.setFontFamilies({first_val}); fmt.setFontFamilies({ first_val });
} else if (it.key() == QStringLiteral("font-size")) { } else if (it.key() == QStringLiteral("font-size")) {
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) { if (first_val.endsWith(QStringLiteral("pt"),
fmt.setFontPointSize(first_val.chopped(2).toDouble()); Qt::CaseInsensitive)) {
} fmt.setFontPointSize(first_val.chopped(2).toDouble());
} else if (it.key() == QStringLiteral("font-weight")) { }
fmt.setFontWeight(first_val.toInt()/8); } else if (it.key() == QStringLiteral("font-weight")) {
} else if (it.key() == QStringLiteral("font-style")) { fmt.setFontWeight(first_val.toInt() / 8);
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic"))); } else if (it.key() == QStringLiteral("font-style")) {
} else if (it.key() == QStringLiteral("text-decoration")) { fmt.setFontItalic(
foreach (const QString &v, it.value()) { StrEquals(first_val, QStringLiteral("italic")));
if (StrEquals(v, QStringLiteral("underline"))) { } else if (it.key() == QStringLiteral("text-decoration")) {
fmt.setFontUnderline(true); foreach (const QString &v, it.value()) {
} else if (StrEquals(v, QStringLiteral("line-through"))) { if (StrEquals(v, QStringLiteral("underline"))) {
fmt.setFontStrikeOut(true); fmt.setFontUnderline(true);
} else if (StrEquals(v, QStringLiteral("overline"))) { } else if (StrEquals(v,
fmt.setFontOverline(true); QStringLiteral("line-through"))) {
} fmt.setFontStrikeOut(true);
} } else if (StrEquals(v, QStringLiteral("overline"))) {
} else if (it.key() == QStringLiteral("color")) { fmt.setFontOverline(true);
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) { }
QString vals_only = first_val; }
vals_only.remove(QStringLiteral("rgba")); } else if (it.key() == QStringLiteral("color")) {
vals_only.remove(QStringLiteral("(")); if (first_val.startsWith(QStringLiteral("rgba"),
vals_only.remove(QStringLiteral(")")); Qt::CaseInsensitive)) {
QStringList rgba = vals_only.split(','); QString vals_only = first_val;
if (rgba.size() == 4) { vals_only.remove(QStringLiteral("rgba"));
QColor c; vals_only.remove(QStringLiteral("("));
c.setRedF(rgba.at(0).toDouble()); vals_only.remove(QStringLiteral(")"));
c.setGreenF(rgba.at(1).toDouble()); QStringList rgba = vals_only.split(',');
c.setBlueF(rgba.at(2).toDouble()); if (rgba.size() == 4) {
c.setAlphaF(rgba.at(3).toDouble()); QColor c;
fmt.setForeground(c); c.setRedF(rgba.at(0).toDouble());
} c.setGreenF(rgba.at(1).toDouble());
} else { c.setBlueF(rgba.at(2).toDouble());
fmt.setForeground(QColor(first_val)); c.setAlphaF(rgba.at(3).toDouble());
} fmt.setForeground(c);
} else if (it.key() == QStringLiteral("font-variant")) { }
if (StrEquals(first_val, QStringLiteral("small-caps"))) { } else {
fmt.setFontCapitalization(QFont::SmallCaps); fmt.setForeground(QColor(first_val));
} }
} else if (it.key() == QStringLiteral("letter-spacing")) { } else if (it.key() == QStringLiteral("font-variant")) {
if (first_val.contains(QChar('%'))) { if (StrEquals(first_val, QStringLiteral("small-caps"))) {
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble()); fmt.setFontCapitalization(QFont::SmallCaps);
} }
} else if (it.key() == QStringLiteral("font-stretch")) { } else if (it.key() == QStringLiteral("letter-spacing")) {
if (first_val.contains(QChar('%'))) { if (first_val.contains(QChar('%'))) {
fmt.setFontStretch(first_val.chopped(1).toInt()); fmt.setFontLetterSpacing(
} first_val.chopped(1).toDouble());
} else if (it.key() == QStringLiteral("-ove-font-style")) { }
fmt.setFontStyleName(first_val); } else if (it.key() == QStringLiteral("font-stretch")) {
} if (first_val.contains(QChar('%'))) {
} fmt.setFontStretch(first_val.chopped(1).toInt());
} }
} } else if (it.key() == QStringLiteral("-ove-font-style")) {
return fmt; fmt.setFontStyleName(first_val);
}
}
}
}
return fmt;
} }
QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes) QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
{ {
QTextBlockFormat block_fmt; QTextBlockFormat block_fmt;
foreach (const QXmlStreamAttribute &attr, attributes) { foreach (const QXmlStreamAttribute &attr, attributes) {
if (StrEquals(attr.name(), QStringLiteral("align"))) { if (StrEquals(attr.name(), QStringLiteral("align"))) {
if (StrEquals(attr.value(), QStringLiteral("right"))) { if (StrEquals(attr.value(), QStringLiteral("right"))) {
block_fmt.setAlignment(Qt::AlignRight); block_fmt.setAlignment(Qt::AlignRight);
} else if (StrEquals(attr.value(), QStringLiteral("center"))) { } else if (StrEquals(attr.value(), QStringLiteral("center"))) {
block_fmt.setAlignment(Qt::AlignHCenter); block_fmt.setAlignment(Qt::AlignHCenter);
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) { } else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
block_fmt.setAlignment(Qt::AlignJustify); block_fmt.setAlignment(Qt::AlignJustify);
} }
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) { } else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
if (StrEquals(attr.value(), QStringLiteral("rtl"))) { if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
block_fmt.setLayoutDirection(Qt::RightToLeft); block_fmt.setLayoutDirection(Qt::RightToLeft);
} }
} else if (StrEquals(attr.name(), QStringLiteral("style"))) { } else if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString()); auto css = GetCSSFromStyle(attr.value().toString());
for (auto it=css.begin(); it!=css.end(); it++) { for (auto it = css.begin(); it != css.end(); it++) {
if (it.key() == QStringLiteral("line-height")) { if (it.key() == QStringLiteral("line-height")) {
const QString &first_val = it.value().constFirst(); const QString &first_val = it.value().constFirst();
if (first_val.contains(QChar('%'))) { if (first_val.contains(QChar('%'))) {
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight); block_fmt.setLineHeight(
} first_val.chopped(1).toDouble(),
} QTextBlockFormat::ProportionalHeight);
} }
} }
} }
}
}
return block_fmt; return block_fmt;
} }
void Html::AppendStringAutoSpace(QString *s, const QString &append) void Html::AppendStringAutoSpace(QString *s, const QString &append)
{ {
if (!s->isEmpty()) { if (!s->isEmpty()) {
s->append(QChar(' ')); s->append(QChar(' '));
} }
s->append(append); s->append(append);
} }
QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s) QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
{ {
QMap<QString, QStringList> map; QMap<QString, QStringList> map;
QStringList list = s.split(QChar(';')); QStringList list = s.split(QChar(';'));
foreach (const QString &a, list) { foreach (const QString &a, list) {
QStringList kv = a.split(QChar(':')); QStringList kv = a.split(QChar(':'));
if (kv.size() != 2) { if (kv.size() != 2) {
continue; continue;
} }
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split // I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each // by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
// match. Also commas should be filtered out. // match. Also commas should be filtered out.
QStringList values; QStringList values;
const QString &val = kv.at(1); const QString &val = kv.at(1);
QChar in_quote(0); QChar in_quote(0);
QString current_str; QString current_str;
for (int i=0; i<val.size(); i++) { for (int i = 0; i < val.size(); i++) {
const QChar &current_char = val.at(i); const QChar &current_char = val.at(i);
if (!in_quote.isNull()) { if (!in_quote.isNull()) {
// If inside quotes and character isn't quote, indiscriminately append char // If inside quotes and character isn't quote, indiscriminately append char
if (current_char == in_quote) { if (current_char == in_quote) {
in_quote = QChar(0); in_quote = QChar(0);
} else { } else {
current_str.append(current_char); current_str.append(current_char);
} }
} else if (current_char.isSpace() || current_char == QChar(',')) { } else if (current_char.isSpace() || current_char == QChar(',')) {
// Dump current // Dump current
if (!current_str.isEmpty()) { if (!current_str.isEmpty()) {
values.append(current_str); values.append(current_str);
current_str.clear(); current_str.clear();
} }
} else if (in_quote.isNull() && (current_char == QChar('\'') || current_char == QChar('"'))) { } else if (in_quote.isNull() && (current_char == QChar('\'') ||
in_quote = current_char; current_char == QChar('"'))) {
} else { in_quote = current_char;
current_str.append(current_char); } else {
} current_str.append(current_char);
} }
}
if (!current_str.isEmpty()) { if (!current_str.isEmpty()) {
values.append(current_str); values.append(current_str);
} }
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this // Not sure if this will ever happen, but just in case, we will avoid assert failures with this
if (values.isEmpty()) { if (values.isEmpty()) {
values.append(QString()); values.append(QString());
} }
map[kv.at(0).trimmed().toLower()] = values; map[kv.at(0).trimmed().toLower()] = values;
} }
return map; return map;
} }
} }
+22 -17
View File
@@ -6,7 +6,8 @@
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
namespace olive { namespace olive
{
/** /**
* @brief Functions for converting HTML to QTextDocument and vice versa * @brief Functions for converting HTML to QTextDocument and vice versa
@@ -26,33 +27,37 @@ namespace olive {
*/ */
class Html { class Html {
public: public:
static QString DocToHtml(const QTextDocument *doc); static QString DocToHtml(const QTextDocument *doc);
static void HtmlToDoc(QTextDocument *doc, const QString &html); static void HtmlToDoc(QTextDocument *doc, const QString &html);
private: private:
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block); static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
static void WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment); static void WriteFragment(QXmlStreamWriter *writer,
const QTextFragment &fragment);
static void WriteCSSProperty(QString *style, const QString &key, const QStringList &value); static void WriteCSSProperty(QString *style, const QString &key,
static void WriteCSSProperty(QString *style, const QString &key, const QString &value) const QStringList &value);
{ static void WriteCSSProperty(QString *style, const QString &key,
WriteCSSProperty(style, key, QStringList({value})); const QString &value)
} {
WriteCSSProperty(style, key, QStringList({ value }));
}
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt); static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
static QTextCharFormat ReadCharFormat(const QXmlStreamAttributes &attributes); static QTextCharFormat
ReadCharFormat(const QXmlStreamAttributes &attributes);
static QTextBlockFormat ReadBlockFormat(const QXmlStreamAttributes &attributes); static QTextBlockFormat
ReadBlockFormat(const QXmlStreamAttributes &attributes);
static void AppendStringAutoSpace(QString *s, const QString &append); static void AppendStringAutoSpace(QString *s, const QString &append);
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s); static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
static const QVector<QString> kBlockTags;
static const QVector<QString> kBlockTags;
}; };
} }
+9 -8
View File
@@ -2,29 +2,30 @@
#include <QMutex> #include <QMutex>
namespace olive { namespace olive
{
uint64_t job_time_index = 0; uint64_t job_time_index = 0;
QMutex job_time_mutex; QMutex job_time_mutex;
JobTime::JobTime() JobTime::JobTime()
{ {
Acquire(); Acquire();
} }
void JobTime::Acquire() void JobTime::Acquire()
{ {
job_time_mutex.lock(); job_time_mutex.lock();
value_ = job_time_index; value_ = job_time_index;
job_time_index++; job_time_index++;
job_time_mutex.unlock(); job_time_mutex.unlock();
} }
} }
QDebug operator<<(QDebug debug, const olive::JobTime& r) QDebug operator<<(QDebug debug, const olive::JobTime &r)
{ {
return debug.space() << r.value(); return debug.space() << r.value();
} }
+35 -36
View File
@@ -4,58 +4,57 @@
#include <QDebug> #include <QDebug>
#include <stdint.h> #include <stdint.h>
namespace olive { namespace olive
class JobTime
{ {
class JobTime {
public: public:
JobTime(); JobTime();
void Acquire(); void Acquire();
uint64_t value() const uint64_t value() const
{ {
return value_; return value_;
} }
bool operator==(const JobTime &rhs) const bool operator==(const JobTime &rhs) const
{ {
return value_ == rhs.value_; return value_ == rhs.value_;
} }
bool operator!=(const JobTime &rhs) const bool operator!=(const JobTime &rhs) const
{ {
return value_ != rhs.value_; return value_ != rhs.value_;
} }
bool operator<(const JobTime &rhs) const bool operator<(const JobTime &rhs) const
{ {
return value_ < rhs.value_; return value_ < rhs.value_;
} }
bool operator>(const JobTime &rhs) const bool operator>(const JobTime &rhs) const
{ {
return value_ > rhs.value_; return value_ > rhs.value_;
} }
bool operator<=(const JobTime &rhs) const bool operator<=(const JobTime &rhs) const
{ {
return value_ <= rhs.value_; return value_ <= rhs.value_;
} }
bool operator>=(const JobTime &rhs) const bool operator>=(const JobTime &rhs) const
{ {
return value_ >= rhs.value_; return value_ >= rhs.value_;
} }
private: private:
uint64_t value_; uint64_t value_;
}; };
} }
QDebug operator<<(QDebug debug, const olive::JobTime& r); QDebug operator<<(QDebug debug, const olive::JobTime &r);
Q_DECLARE_METATYPE(olive::JobTime) Q_DECLARE_METATYPE(olive::JobTime)
+7 -6
View File
@@ -21,20 +21,21 @@
#ifndef LERP_H #ifndef LERP_H
#define LERP_H #define LERP_H
template<typename T> template <typename T>
/** /**
* @brief Linearly interpolate a value between a and b using t * @brief Linearly interpolate a value between a and b using t
* *
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value * t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
* in between a and b at that point linearly. * in between a and b at that point linearly.
*/ */
T lerp(T a, T b, double t) { T lerp(T a, T b, double t)
return (a * (1.0 - t)) + (b * t); {
return (a * (1.0 - t)) + (b * t);
} }
template<typename T> template <typename T> T lerp(T a, T b, float t)
T lerp(T a, T b, float t) { {
return (a * (1.0f - t)) + (b * t); return (a * (1.0f - t)) + (b * t);
} }
#endif // LERP_H #endif // LERP_H
+249 -245
View File
@@ -31,7 +31,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
/** /**
* @brief MemoryPool base class * @brief MemoryPool base class
@@ -46,386 +47,389 @@ namespace olive {
* `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data * `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool. * will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
*/ */
class MemoryPool : public QObject class MemoryPool : public QObject {
{ Q_OBJECT
Q_OBJECT
public: public:
/** /**
* @brief Constructor * @brief Constructor
* @param element_count * @param element_count
* *
* Number of elements per arena * Number of elements per arena
*/ */
MemoryPool(int element_count) MemoryPool(int element_count)
{ {
element_count_ = element_count; element_count_ = element_count;
clear_timer_ = new QTimer(); clear_timer_ = new QTimer();
clear_timer_->setInterval(kMaxEmptyArenaLife); clear_timer_->setInterval(kMaxEmptyArenaLife);
clear_timer_->moveToThread(qApp->thread()); clear_timer_->moveToThread(qApp->thread());
connect(clear_timer_, &QTimer::timeout, this, &MemoryPool::ClearEmptyArenas, Qt::DirectConnection); connect(clear_timer_, &QTimer::timeout, this,
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection); &MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
} QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
}
/** /**
* @brief Destructor * @brief Destructor
* *
* Deletes all arenas. * Deletes all arenas.
*/ */
virtual ~MemoryPool() virtual ~MemoryPool()
{ {
Clear(); Clear();
clear_timer_->deleteLater(); clear_timer_->deleteLater();
} }
DISABLE_COPY_MOVE(MemoryPool) DISABLE_COPY_MOVE(MemoryPool)
/** /**
* @brief Clears all arenas, freeing all of their memory * @brief Clears all arenas, freeing all of their memory
* *
* Note that this function is not safe, any elements that are still out there will be invalid * Note that this function is not safe, any elements that are still out there will be invalid
* and accessing them will cause a crash. You'll need to make sure all elements are already * and accessing them will cause a crash. You'll need to make sure all elements are already
* relinquished before then. * relinquished before then.
*/ */
void Clear() void Clear()
{ {
qDeleteAll(arenas_); qDeleteAll(arenas_);
arenas_.clear(); arenas_.clear();
} }
/** /**
* @brief Returns whether any arenas are successfully allocated * @brief Returns whether any arenas are successfully allocated
*/ */
inline bool IsAllocated() const inline bool IsAllocated() const
{ {
return !arenas_.empty(); return !arenas_.empty();
} }
/** /**
* @brief Returns current number of allocated arenas * @brief Returns current number of allocated arenas
*/ */
inline int GetArenaCount() const inline int GetArenaCount() const
{ {
return arenas_.size(); return arenas_.size();
} }
class Arena; class Arena;
/** /**
* @brief A handle for a chunk of memory in an arena * @brief A handle for a chunk of memory in an arena
* *
* Calling Get() on the pool or arena will return a shared pointer to an element which will contain a pointer to * Calling Get() on the pool or arena will return a shared pointer to an element which will contain a pointer to
* the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the memory * the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the memory
* is released back into the pool so it can be used by another class. * is released back into the pool so it can be used by another class.
*/ */
class Element { class Element {
public: public:
/** /**
* @brief Element Constructor * @brief Element Constructor
* *
* There is no need to use this outside of the memory pool's internal functions. * There is no need to use this outside of the memory pool's internal functions.
*/ */
Element(Arena* parent, uint8_t* data) Element(Arena *parent, uint8_t *data)
{ {
parent_ = parent; parent_ = parent;
data_ = data; data_ = data;
accessed_ = QDateTime::currentMSecsSinceEpoch(); accessed_ = QDateTime::currentMSecsSinceEpoch();
} }
/** /**
* @brief Element Destructor * @brief Element Destructor
* *
* Automatically releases this element's memory back to the arena it was retrieved from. * Automatically releases this element's memory back to the arena it was retrieved from.
*/ */
~Element() ~Element()
{ {
release(); release();
} }
DISABLE_COPY_MOVE(Element) DISABLE_COPY_MOVE(Element)
/** /**
* @brief Access data represented in the pool * @brief Access data represented in the pool
*/ */
inline uint8_t* data() const inline uint8_t *data() const
{ {
return data_; return data_;
} }
inline const int64_t& timestamp() const inline const int64_t &timestamp() const
{ {
return timestamp_; return timestamp_;
} }
inline void set_timestamp(const int64_t& timestamp) inline void set_timestamp(const int64_t &timestamp)
{ {
timestamp_ = timestamp; timestamp_ = timestamp;
} }
/** /**
* @brief Register that this element has been accessed * @brief Register that this element has been accessed
* *
* \see last_accessed() * \see last_accessed()
*/ */
inline void access() inline void access()
{ {
accessed_ = QDateTime::currentMSecsSinceEpoch(); accessed_ = QDateTime::currentMSecsSinceEpoch();
} }
/** /**
* @brief Returns the last time `access()` was called on this function * @brief Returns the last time `access()` was called on this function
* *
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of * Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of
* time, it can probably be freed back into the pool). This requires all usages to call `access()`. * time, it can probably be freed back into the pool). This requires all usages to call `access()`.
*/ */
inline const int64_t& last_accessed() const inline const int64_t &last_accessed() const
{ {
return accessed_; return accessed_;
} }
void release() void release()
{ {
if (data_) { if (data_) {
parent_->Release(this); parent_->Release(this);
data_ = nullptr; data_ = nullptr;
} }
} }
private: private:
Arena* parent_; Arena *parent_;
uint8_t* data_; uint8_t *data_;
int64_t timestamp_; int64_t timestamp_;
int64_t accessed_; int64_t accessed_;
};
}; using ElementPtr = std::shared_ptr<Element>;
using ElementPtr = std::shared_ptr<Element>; /**
/**
* @brief A memory pool arena - a subsection of memory * @brief A memory pool arena - a subsection of memory
* *
* The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of * The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of
* an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and continue * an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and continue
* providing memory (and freeing arenas when they're no longer in use). * providing memory (and freeing arenas when they're no longer in use).
*/ */
class Arena { class Arena {
public: public:
Arena(MemoryPool* parent) Arena(MemoryPool *parent)
{ {
parent_ = parent; parent_ = parent;
data_ = nullptr; data_ = nullptr;
allocated_sz_ = 0; allocated_sz_ = 0;
empty_time_ = QDateTime::currentMSecsSinceEpoch(); empty_time_ = QDateTime::currentMSecsSinceEpoch();
} }
~Arena() ~Arena()
{ {
std::list<Element*> copy = lent_elements_; std::list<Element *> copy = lent_elements_;
foreach (Element* e, copy) { foreach (Element *e, copy) {
e->release(); e->release();
} }
delete [] data_; delete[] data_;
} }
DISABLE_COPY_MOVE(Arena) DISABLE_COPY_MOVE(Arena)
/** /**
* @brief Returns an element if there is free memory to do so * @brief Returns an element if there is free memory to do so
*/ */
ElementPtr Get() ElementPtr Get()
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
for (int i=0;i<available_.size();i++) { for (int i = 0; i < available_.size(); i++) {
if (available_.at(i)) { if (available_.at(i)) {
// This buffer is available // This buffer is available
available_.replace(i, false); available_.replace(i, false);
ElementPtr e = std::make_shared<Element>(this, ElementPtr e = std::make_shared<Element>(
reinterpret_cast<uint8_t*>(data_ + i * element_sz_)); this,
lent_elements_.push_back(e.get()); reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
lent_elements_.push_back(e.get());
return e; return e;
} }
} }
return nullptr; return nullptr;
} }
/** /**
* @brief Releases an element back into the pool for use elsewhere * @brief Releases an element back into the pool for use elsewhere
*/ */
void Release(Element* e) void Release(Element *e)
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_); quintptr diff = reinterpret_cast<quintptr>(e->data()) -
reinterpret_cast<quintptr>(data_);
int index = diff / element_sz_; int index = diff / element_sz_;
available_.replace(index, true); available_.replace(index, true);
lent_elements_.remove(e); lent_elements_.remove(e);
if (lent_elements_.empty()) { if (lent_elements_.empty()) {
empty_time_ = QDateTime::currentMSecsSinceEpoch(); empty_time_ = QDateTime::currentMSecsSinceEpoch();
} }
} }
int GetUsageCount() int GetUsageCount()
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
return lent_elements_.size(); return lent_elements_.size();
} }
bool Allocate(size_t ele_sz, size_t nb_elements) bool Allocate(size_t ele_sz, size_t nb_elements)
{ {
if (IsAllocated()) { if (IsAllocated()) {
return true; return true;
} }
element_sz_ = ele_sz; element_sz_ = ele_sz;
allocated_sz_ = element_sz_ * nb_elements; allocated_sz_ = element_sz_ * nb_elements;
if ((data_ = new uint8_t[allocated_sz_])) { if ((data_ = new uint8_t[allocated_sz_])) {
available_.resize(nb_elements); available_.resize(nb_elements);
available_.fill(true); available_.fill(true);
return true; return true;
} else { } else {
available_.clear(); available_.clear();
return false; return false;
} }
} }
inline int GetElementCount() const inline int GetElementCount() const
{ {
return available_.size(); return available_.size();
} }
inline bool IsAllocated() const inline bool IsAllocated() const
{ {
return data_; return data_;
} }
inline qint64 GetTimeArenaWasMadeEmpty() inline qint64 GetTimeArenaWasMadeEmpty()
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
return empty_time_; return empty_time_;
} }
private: private:
MemoryPool* parent_; MemoryPool *parent_;
uint8_t* data_; uint8_t *data_;
size_t allocated_sz_; size_t allocated_sz_;
QVector<bool> available_; QVector<bool> available_;
QMutex lock_; QMutex lock_;
size_t element_sz_; size_t element_sz_;
std::list<Element*> lent_elements_; std::list<Element *> lent_elements_;
qint64 empty_time_; qint64 empty_time_;
};
}; /**
/**
* @brief Retrieves an element from an available arena * @brief Retrieves an element from an available arena
*/ */
ElementPtr Get() ElementPtr Get()
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
// Attempt to get an element from an arena // Attempt to get an element from an arena
foreach (Arena* a, arenas_) { foreach (Arena *a, arenas_) {
ElementPtr e = a->Get(); ElementPtr e = a->Get();
if (e) { if (e) {
return e; return e;
} }
} }
// All arenas were empty, we'll need to create a new one // All arenas were empty, we'll need to create a new one
if (arenas_.empty()) { if (arenas_.empty()) {
qDebug() << "No arenas, creating new..."; qDebug() << "No arenas, creating new...";
} else { } else {
qDebug() << "All arenas are full, creating new..."; qDebug() << "All arenas are full, creating new...";
} }
size_t ele_sz = GetElementSize(); size_t ele_sz = GetElementSize();
if (!ele_sz) { if (!ele_sz) {
qCritical() << "Failed to create arena, element size was 0"; qCritical() << "Failed to create arena, element size was 0";
return nullptr; return nullptr;
} }
if (element_count_ <= 0) { if (element_count_ <= 0) {
qCritical() << "Failed to create arena, element count was invalid:" << element_count_; qCritical() << "Failed to create arena, element count was invalid:"
return nullptr; << element_count_;
} return nullptr;
}
Arena* a = new Arena(this); Arena *a = new Arena(this);
if (!a->Allocate(ele_sz, element_count_)) { if (!a->Allocate(ele_sz, element_count_)) {
qCritical() << "Failed to create arena, allocation failed. Out of memory?"; qCritical()
delete a; << "Failed to create arena, allocation failed. Out of memory?";
return nullptr; delete a;
} return nullptr;
}
arenas_.push_back(a); arenas_.push_back(a);
return a->Get(); return a->Get();
} }
protected: protected:
/** /**
* @brief The size of each element * @brief The size of each element
* *
* Override this to use a custom size (e.g. a char array where T = char but the element size is > 1) * Override this to use a custom size (e.g. a char array where T = char but the element size is > 1)
*/ */
virtual size_t GetElementSize() virtual size_t GetElementSize()
{ {
return sizeof(uint8_t); return sizeof(uint8_t);
} }
private: private:
int element_count_; int element_count_;
std::list<Arena*> arenas_; std::list<Arena *> arenas_;
QMutex lock_; QMutex lock_;
QTimer *clear_timer_; QTimer *clear_timer_;
static const qint64 kMaxEmptyArenaLife = 5000; static const qint64 kMaxEmptyArenaLife = 5000;
private slots: private slots:
void ClearEmptyArenas() void ClearEmptyArenas()
{ {
QMutexLocker locker(&lock_); QMutexLocker locker(&lock_);
const qint64 min_time = QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife; const qint64 min_time =
QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
for (auto it=arenas_.begin(); it!=arenas_.end(); ) { for (auto it = arenas_.begin(); it != arenas_.end();) {
Arena* arena = (*it); Arena *arena = (*it);
if (arena->GetUsageCount() == 0 && arena->GetTimeArenaWasMadeEmpty() <= min_time) {
qDebug() << "Removing an empty arena";
delete arena;
it = arenas_.erase(it);
} else {
it++;
}
}
}
if (arena->GetUsageCount() == 0 &&
arena->GetTimeArenaWasMadeEmpty() <= min_time) {
qDebug() << "Removing an empty arena";
delete arena;
it = arenas_.erase(it);
} else {
it++;
}
}
}
}; };
} }
+19 -18
View File
@@ -20,28 +20,29 @@
#include "ocioutils.h" #include "ocioutils.h"
namespace olive { namespace olive
{
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format) OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
{ {
switch (format) { switch (format) {
case PixelFormat::U8: case PixelFormat::U8:
return OCIO::BIT_DEPTH_UINT8; return OCIO::BIT_DEPTH_UINT8;
case PixelFormat::U16: case PixelFormat::U16:
return OCIO::BIT_DEPTH_UINT16; return OCIO::BIT_DEPTH_UINT16;
break; break;
case PixelFormat::F16: case PixelFormat::F16:
return OCIO::BIT_DEPTH_F16; return OCIO::BIT_DEPTH_F16;
break; break;
case PixelFormat::F32: case PixelFormat::F32:
return OCIO::BIT_DEPTH_F32; return OCIO::BIT_DEPTH_F32;
break; break;
case PixelFormat::INVALID: case PixelFormat::INVALID:
case PixelFormat::COUNT: case PixelFormat::COUNT:
break; break;
} }
return OCIO::BIT_DEPTH_UNKNOWN; return OCIO::BIT_DEPTH_UNKNOWN;
} }
} }
+4 -4
View File
@@ -26,12 +26,12 @@ namespace OCIO = OCIO_NAMESPACE;
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
class OCIOUtils
{ {
class OCIOUtils {
public: public:
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format); static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
}; };
} }
+37 -41
View File
@@ -22,62 +22,58 @@
#include <QDebug> #include <QDebug>
namespace olive { namespace olive
void OIIOUtils::FrameToBuffer(const Frame* frame, OIIO::ImageBuf *buf)
{ {
buf->set_pixels(OIIO::ROI(),
buf->spec().format, void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
frame->const_data(), {
OIIO::AutoStride, buf->set_pixels(OIIO::ROI(), buf->spec().format, frame->const_data(),
frame->linesize_bytes()); OIIO::AutoStride, frame->linesize_bytes());
} }
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame* frame) void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame *frame)
{ {
buf->get_pixels(OIIO::ROI(), buf->get_pixels(OIIO::ROI(), buf->spec().format, frame->data(),
buf->spec().format, OIIO::AutoStride, frame->linesize_bytes());
frame->data(),
OIIO::AutoStride,
frame->linesize_bytes());
} }
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec) rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
{ {
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1)); return rational::fromDouble(
spec.get_float_attribute("PixelAspectRatio", 1));
} }
PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type) PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
{ {
switch (type) { switch (type) {
case OIIO::TypeDesc::UNKNOWN: case OIIO::TypeDesc::UNKNOWN:
case OIIO::TypeDesc::NONE: case OIIO::TypeDesc::NONE:
break; break;
case OIIO::TypeDesc::INT8: case OIIO::TypeDesc::INT8:
case OIIO::TypeDesc::INT16: case OIIO::TypeDesc::INT16:
case OIIO::TypeDesc::INT32: case OIIO::TypeDesc::INT32:
case OIIO::TypeDesc::UINT32: case OIIO::TypeDesc::UINT32:
case OIIO::TypeDesc::INT64: case OIIO::TypeDesc::INT64:
case OIIO::TypeDesc::UINT64: case OIIO::TypeDesc::UINT64:
case OIIO::TypeDesc::STRING: case OIIO::TypeDesc::STRING:
case OIIO::TypeDesc::PTR: case OIIO::TypeDesc::PTR:
case OIIO::TypeDesc::LASTBASE: case OIIO::TypeDesc::LASTBASE:
case OIIO::TypeDesc::DOUBLE: case OIIO::TypeDesc::DOUBLE:
qDebug() << "Tried to use unknown OIIO base type"; qDebug() << "Tried to use unknown OIIO base type";
break; break;
case OIIO::TypeDesc::UINT8: case OIIO::TypeDesc::UINT8:
return PixelFormat::U8; return PixelFormat::U8;
case OIIO::TypeDesc::UINT16: case OIIO::TypeDesc::UINT16:
return PixelFormat::U16; return PixelFormat::U16;
case OIIO::TypeDesc::HALF: case OIIO::TypeDesc::HALF:
return PixelFormat::F16; return PixelFormat::F16;
case OIIO::TypeDesc::FLOAT: case OIIO::TypeDesc::FLOAT:
return PixelFormat::F32; return PixelFormat::F32;
} }
return PixelFormat::INVALID; return PixelFormat::INVALID;
} }
} }
+24 -23
View File
@@ -27,37 +27,38 @@
#include "codec/frame.h" #include "codec/frame.h"
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
{
class OIIOUtils { class OIIOUtils {
public: public:
static OIIO::TypeDesc::BASETYPE GetOIIOBaseTypeFromFormat(PixelFormat format) static OIIO::TypeDesc::BASETYPE
{ GetOIIOBaseTypeFromFormat(PixelFormat format)
switch (format) { {
case PixelFormat::U8: switch (format) {
return OIIO::TypeDesc::UINT8; case PixelFormat::U8:
case PixelFormat::U16: return OIIO::TypeDesc::UINT8;
return OIIO::TypeDesc::UINT16; case PixelFormat::U16:
case PixelFormat::F16: return OIIO::TypeDesc::UINT16;
return OIIO::TypeDesc::HALF; case PixelFormat::F16:
case PixelFormat::F32: return OIIO::TypeDesc::HALF;
return OIIO::TypeDesc::FLOAT; case PixelFormat::F32:
case PixelFormat::INVALID: return OIIO::TypeDesc::FLOAT;
case PixelFormat::COUNT: case PixelFormat::INVALID:
break; case PixelFormat::COUNT:
} break;
}
return OIIO::TypeDesc::UNKNOWN; return OIIO::TypeDesc::UNKNOWN;
} }
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf* buf); static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf);
static void BufferToFrame(OIIO::ImageBuf* buf, Frame* frame); static void BufferToFrame(OIIO::ImageBuf *buf, Frame *frame);
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type); static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec);
}; };
} }
+16 -15
View File
@@ -25,30 +25,31 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
uint32_t ceil_to_power_of_2(uint32_t v) uint32_t ceil_to_power_of_2(uint32_t v)
{ {
v--; v--;
v |= v >> 1; v |= v >> 1;
v |= v >> 2; v |= v >> 2;
v |= v >> 4; v |= v >> 4;
v |= v >> 8; v |= v >> 8;
v |= v >> 16; v |= v >> 16;
v++; v++;
return v; return v;
} }
uint32_t floor_to_power_of_2(uint32_t x) uint32_t floor_to_power_of_2(uint32_t x)
{ {
x = x | (x >> 1); x = x | (x >> 1);
x = x | (x >> 2); x = x | (x >> 2);
x = x | (x >> 4); x = x | (x >> 4);
x = x | (x >> 8); x = x | (x >> 8);
x = x | (x >> 16); x = x | (x >> 16);
return x - (x >> 1); return x - (x >> 1);
} }
} }
+125 -114
View File
@@ -22,192 +22,203 @@
#include <QDebug> #include <QDebug>
namespace olive { namespace olive
{
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString& s) { int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString &s)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
return fm.width(s); return fm.width(s);
#else #else
return fm.horizontalAdvance(s); return fm.horizontalAdvance(s);
#endif #endif
} }
QFrame *QtUtils::CreateHorizontalLine() QFrame *QtUtils::CreateHorizontalLine()
{ {
QFrame* horizontal_line = new QFrame(); QFrame *horizontal_line = new QFrame();
horizontal_line->setFrameShape(QFrame::HLine); horizontal_line->setFrameShape(QFrame::HLine);
horizontal_line->setFrameShadow(QFrame::Sunken); horizontal_line->setFrameShadow(QFrame::Sunken);
return horizontal_line; return horizontal_line;
} }
QFrame *QtUtils::CreateVerticalLine() QFrame *QtUtils::CreateVerticalLine()
{ {
QFrame *l = CreateHorizontalLine(); QFrame *l = CreateHorizontalLine();
l->setFrameShape(QFrame::VLine); l->setFrameShape(QFrame::VLine);
return l; return l;
} }
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons) int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon,
const QString &title, const QString &message,
QMessageBox::StandardButtons buttons)
{ {
QMessageBox b(parent); QMessageBox b(parent);
b.setIcon(icon); b.setIcon(icon);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(title); b.setWindowTitle(title);
b.setText(message); b.setText(message);
uint mask = QMessageBox::FirstButton; uint mask = QMessageBox::FirstButton;
while (mask <= QMessageBox::LastButton) { while (mask <= QMessageBox::LastButton) {
uint sb = buttons & mask; uint sb = buttons & mask;
if (sb) { if (sb) {
b.addButton(static_cast<QMessageBox::StandardButton>(sb)); b.addButton(static_cast<QMessageBox::StandardButton>(sb));
} }
mask <<= 1; mask <<= 1;
} }
return b.exec(); return b.exec();
} }
QDateTime QtUtils::GetCreationDate(const QFileInfo &info) QDateTime QtUtils::GetCreationDate(const QFileInfo &info)
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
return info.created(); return info.created();
#else #else
QDateTime t = info.birthTime(); QDateTime t = info.birthTime();
if (!t.isValid()) { if (!t.isValid()) {
t = info.metadataChangeTime(); t = info.metadataChangeTime();
} }
return t; return t;
#endif #endif
} }
QString QtUtils::GetFormattedDateTime(const QDateTime &dt) QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
{ {
return dt.toString(Qt::TextDate); return dt.toString(Qt::TextDate);
} }
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width) QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width)
{ {
QStringList list; QStringList list;
QStringList lines = s.split('\n'); QStringList lines = s.split('\n');
// Iterate every line // Iterate every line
for (int i=0; i<lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
QString this_line = lines.at(i); QString this_line = lines.at(i);
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) { while (this_line.size() > 1 &&
int old_size = this_line.size(); QFontMetricsWidth(fm, this_line) >= bounding_width) {
int hard_break = -1; int old_size = this_line.size();
int hard_break = -1;
for (int j=this_line.size()-1; j>=0; j--) { for (int j = this_line.size() - 1; j >= 0; j--) {
const QChar &char_test = this_line.at(j); const QChar &char_test = this_line.at(j);
if (char_test.isSpace() if (char_test.isSpace() || char_test == '-') {
|| char_test == '-') { if (QFontMetricsWidth(fm, this_line.left(j)) <
if (QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) { bounding_width) {
if (!char_test.isSpace()) { if (!char_test.isSpace()) {
j++; j++;
} }
QString chopped = this_line.left(j); QString chopped = this_line.left(j);
list.append(chopped); list.append(chopped);
while (j < this_line.size() && this_line.at(j).isSpace()) { while (j < this_line.size() &&
j++; this_line.at(j).isSpace()) {
} j++;
this_line.remove(0, j); }
break; this_line.remove(0, j);
} break;
} else if (hard_break == -1 && QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) { }
// In case we can't find a better place to split, split at the earliest time the line } else if (hard_break == -1 &&
// goes under the width limit QFontMetricsWidth(fm, this_line.left(j)) <
hard_break = j; bounding_width) {
} // In case we can't find a better place to split, split at the earliest time the line
} // goes under the width limit
hard_break = j;
}
}
if (old_size == this_line.size()) { if (old_size == this_line.size()) {
if (hard_break != -1) { if (hard_break != -1) {
list.append(this_line.left(hard_break)); list.append(this_line.left(hard_break));
this_line.remove(0, hard_break); this_line.remove(0, hard_break);
} else { } else {
qWarning() << "Failed to find anywhere to wrap. Returning full line."; qWarning()
break; << "Failed to find anywhere to wrap. Returning full line.";
} break;
} }
} }
}
if (!this_line.isEmpty()) { if (!this_line.isEmpty()) {
list.append(this_line); list.append(this_line);
} }
} }
return list; return list;
} }
Qt::KeyboardModifiers QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e) Qt::KeyboardModifiers
QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
{ {
if (e & Qt::ControlModifier & Qt::ShiftModifier) { if (e & Qt::ControlModifier & Qt::ShiftModifier) {
return e; return e;
} }
if (e & Qt::ShiftModifier) { if (e & Qt::ShiftModifier) {
e |= Qt::ControlModifier; e |= Qt::ControlModifier;
e &= ~Qt::ShiftModifier; e &= ~Qt::ShiftModifier;
} else if (e & Qt::ControlModifier) { } else if (e & Qt::ControlModifier) {
e |= Qt::ShiftModifier; e |= Qt::ShiftModifier;
e &= ~Qt::ControlModifier; e &= ~Qt::ControlModifier;
} }
return e; return e;
} }
void QtUtils::SetComboBoxData(QComboBox *cb, int data) void QtUtils::SetComboBoxData(QComboBox *cb, int data)
{ {
for (int i=0; i<cb->count(); i++) { for (int i = 0; i < cb->count(); i++) {
if (cb->itemData(i).toInt() == data) { if (cb->itemData(i).toInt() == data) {
cb->setCurrentIndex(i); cb->setCurrentIndex(i);
break; break;
} }
} }
} }
void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data) void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data)
{ {
for (int i=0; i<cb->count(); i++) { for (int i = 0; i < cb->count(); i++) {
if (cb->itemData(i).toString() == data) { if (cb->itemData(i).toString() == data) {
cb->setCurrentIndex(i); cb->setCurrentIndex(i);
break; break;
} }
} }
} }
QColor QtUtils::toQColor(const core::Color &i) QColor QtUtils::toQColor(const core::Color &i)
{ {
QColor c; QColor c;
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations // QColor only supports values from 0.0 to 1.0 and are only used for UI representations
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f)); c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f)); c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f)); c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f)); c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
return c; return c;
} }
namespace core { namespace core
{
uint qHash(const core::rational &r, uint seed) uint qHash(const core::rational &r, uint seed)
{ {
return ::qHash(r.toDouble(), seed); return ::qHash(r.toDouble(), seed);
} }
uint qHash(const core::TimeRange &r, uint seed) uint qHash(const core::TimeRange &r, uint seed)
{ {
return qHash(r.in(), seed) ^ qHash(r.out(), seed); return qHash(r.in(), seed) ^ qHash(r.out(), seed);
} }
} }
} }
+43 -40
View File
@@ -29,76 +29,79 @@
#include <QFrame> #include <QFrame>
#include <QMessageBox> #include <QMessageBox>
namespace olive { namespace olive
{
class QtUtils { class QtUtils {
public: public:
/** /**
* @brief Retrieves the width of a string according to certain QFontMetrics * @brief Retrieves the width of a string according to certain QFontMetrics
* *
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the * QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for * latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
* earlier. * earlier.
*/ */
static int QFontMetricsWidth(QFontMetrics fm, const QString& s); static int QFontMetricsWidth(QFontMetrics fm, const QString &s);
static QFrame* CreateHorizontalLine(); static QFrame *CreateHorizontalLine();
static QFrame* CreateVerticalLine(); static QFrame *CreateVerticalLine();
static int MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok); static int MsgBox(QWidget *parent, QMessageBox::Icon icon,
const QString &title, const QString &message,
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
static QDateTime GetCreationDate(const QFileInfo &info); static QDateTime GetCreationDate(const QFileInfo &info);
static QString GetFormattedDateTime(const QDateTime &dt); static QString GetFormattedDateTime(const QDateTime &dt);
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width); static QStringList WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width);
static Qt::KeyboardModifiers FlipControlAndShiftModifiers(Qt::KeyboardModifiers e); static Qt::KeyboardModifiers
FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
static void SetComboBoxData(QComboBox *cb, int data); static void SetComboBoxData(QComboBox *cb, int data);
static void SetComboBoxData(QComboBox *cb, const QString &data); static void SetComboBoxData(QComboBox *cb, const QString &data);
template <typename T> template <typename T> static T *GetParentOfType(const QObject *child)
static T *GetParentOfType(const QObject *child) {
{ QObject *t = child->parent();
QObject *t = child->parent();
while (t) { while (t) {
if (T *p = dynamic_cast<T*>(t)) { if (T *p = dynamic_cast<T *>(t)) {
return p; return p;
} }
t = t->parent(); t = t->parent();
} }
return nullptr; return nullptr;
} }
static QColor toQColor(const core::Color &c); static QColor toQColor(const core::Color &c);
/** /**
* @brief Convert a pointer to a value that can be sent between NodeParams * @brief Convert a pointer to a value that can be sent between NodeParams
*/ */
static QVariant PtrToValue(void* ptr) static QVariant PtrToValue(void *ptr)
{ {
return reinterpret_cast<quintptr>(ptr); return reinterpret_cast<quintptr>(ptr);
} }
/** /**
* @brief Convert a NodeParam value to a pointer of any kind * @brief Convert a NodeParam value to a pointer of any kind
*/ */
template<class T> template <class T> static T *ValueToPtr(const QVariant &ptr)
static T* ValueToPtr(const QVariant &ptr) {
{ return reinterpret_cast<T *>(ptr.value<quintptr>());
return reinterpret_cast<T*>(ptr.value<quintptr>()); }
}
}; };
namespace core { namespace core
{
uint qHash(const core::rational& r, uint seed = 0); uint qHash(const core::rational &r, uint seed = 0);
uint qHash(const core::TimeRange& r, uint seed = 0); uint qHash(const core::TimeRange &r, uint seed = 0);
} }
+2 -3
View File
@@ -21,10 +21,9 @@
#ifndef RANGE_H #ifndef RANGE_H
#define RANGE_H #define RANGE_H
template <typename T> template <typename T> bool InRange(T a, T b, T range)
bool InRange(T a, T b, T range)
{ {
return (a >= b - range && a <= b + range); return (a >= b - range && a <= b + range);
} }
#endif // RANGE_H #endif // RANGE_H
+54 -52
View File
@@ -24,69 +24,71 @@
#include <QMessageBox> #include <QMessageBox>
#include <QRegularExpression> #include <QRegularExpression>
namespace olive { namespace olive
double GetFloatRatioFromUser(QWidget* parent,
const QString& title,
bool* ok_in)
{ {
QString s;
forever { double GetFloatRatioFromUser(QWidget *parent, const QString &title, bool *ok_in)
bool ok; {
QString s;
s = QInputDialog::getText(parent, forever
title, {
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"), bool ok;
QLineEdit::Normal,
s,
&ok);
if (!ok) { s = QInputDialog::getText(
// User cancelled dialog, do nothing parent, title,
if (ok_in) { QCoreApplication::translate(
*ok_in = false; "RatioDialog",
} "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
return qSNaN(); QLineEdit::Normal, s, &ok);
}
QStringList ratio_components = s.split(QRegularExpression(QStringLiteral(":|;|\\/"))); if (!ok) {
// User cancelled dialog, do nothing
if (ok_in) {
*ok_in = false;
}
return qSNaN();
}
if (ratio_components.size() == 1) { QStringList ratio_components =
bool float_ok; s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
double flt = ratio_components.at(0).toDouble(&float_ok); if (ratio_components.size() == 1) {
bool float_ok;
if (float_ok && flt > 0) { double flt = ratio_components.at(0).toDouble(&float_ok);
if (ok_in) {
*ok_in = true;
}
return flt;
}
} else if (ratio_components.size() == 2) {
bool numer_ok, denom_ok;
double num = ratio_components.at(0).toDouble(&numer_ok); if (float_ok && flt > 0) {
double den = ratio_components.at(1).toDouble(&denom_ok); if (ok_in) {
*ok_in = true;
}
return flt;
}
} else if (ratio_components.size() == 2) {
bool numer_ok, denom_ok;
if (numer_ok double num = ratio_components.at(0).toDouble(&numer_ok);
&& denom_ok double den = ratio_components.at(1).toDouble(&denom_ok);
&& num > 0
&& den > 0) {
// Exit loop and set this ratio
if (ok_in) {
*ok_in = true;
}
return num / den;
}
}
QMessageBox::warning(parent, if (numer_ok && denom_ok && num > 0 && den > 0) {
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"), // Exit loop and set this ratio
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a " if (ok_in) {
"rational fraction with a ':' or a '/' separator.").arg(s), *ok_in = true;
QMessageBox::Ok); }
} return num / den;
}
}
QMessageBox::warning(
parent,
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
QCoreApplication::translate(
"RatioDialog",
"Failed to parse \"%1\" into an aspect ratio. Please format a "
"rational fraction with a ':' or a '/' separator.")
.arg(s),
QMessageBox::Ok);
}
} }
} }
+4 -4
View File
@@ -23,11 +23,11 @@
#include <QInputDialog> #include <QInputDialog>
namespace olive { namespace olive
{
double GetFloatRatioFromUser(QWidget* parent, double GetFloatRatioFromUser(QWidget *parent, const QString &title,
const QString& title, bool *ok_in);
bool* ok_in);
} }
+10 -13
View File
@@ -4,24 +4,21 @@
#include <QMap> #include <QMap>
#include <QMutex> #include <QMutex>
template <typename K, typename V> template <typename K, typename V> class ThreadSafeMap {
class ThreadSafeMap
{
public: public:
ThreadSafeMap() = default; ThreadSafeMap() = default;
void insert(K key, V value) void insert(K key, V value)
{ {
mutex_.lock(); mutex_.lock();
map_.insert(key, value); map_.insert(key, value);
mutex_.unlock(); mutex_.unlock();
} }
private: private:
QMutex mutex_; QMutex mutex_;
QMap<K, V> map_;
QMap<K, V> map_;
}; };
#endif // THREADSAFEMAP_H #endif // THREADSAFEMAP_H
+5 -3
View File
@@ -6,10 +6,12 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
inline QString ToHex(quint64 t) { inline QString ToHex(quint64 t)
return QStringLiteral("%1").arg(t, 0, 16); {
return QStringLiteral("%1").arg(t, 0, 16);
} }
} }
+2 -3
View File
@@ -21,10 +21,9 @@
#ifndef UTIL_H #ifndef UTIL_H
#define UTIL_H #define UTIL_H
template <typename T> template <typename T> inline T mid(T a, T b)
inline T mid(T a, T b)
{ {
return (a + b) * 0.5; return (a + b) * 0.5;
} }
#endif // UTIL_H #endif // UTIL_H
+13 -12
View File
@@ -23,23 +23,24 @@
#include "node/block/block.h" #include "node/block/block.h"
#include "node/factory.h" #include "node/factory.h"
namespace olive { namespace olive
{
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom) bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
{ {
QXmlStreamReader::TokenType token; QXmlStreamReader::TokenType token;
while ((token = reader->readNext()) != QXmlStreamReader::Invalid while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
&& token != QXmlStreamReader::EndDocument token != QXmlStreamReader::EndDocument &&
&& (!cancel_atom || !cancel_atom->IsCancelled())) { (!cancel_atom || !cancel_atom->IsCancelled())) {
if (reader->isEndElement()) { if (reader->isEndElement()) {
return false; return false;
} else if (reader->isStartElement()) { } else if (reader->isStartElement()) {
return true; return true;
} }
} }
return false; return false;
} }
} }
+5 -3
View File
@@ -27,7 +27,8 @@
#include "render/cancelatom.h" #include "render/cancelatom.h"
#include "undo/undocommand.h" #include "undo/undocommand.h"
namespace olive { namespace olive
{
class Block; class Block;
class Node; class Node;
@@ -35,7 +36,7 @@ class NodeInput;
class NodeGroup; class NodeGroup;
#define XMLAttributeLoop(reader, item) \ #define XMLAttributeLoop(reader, item) \
foreach (const QXmlStreamAttribute& item, reader->attributes()) foreach (const QXmlStreamAttribute &item, reader->attributes())
/** /**
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document * @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
@@ -46,7 +47,8 @@ class NodeGroup;
* *
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error * See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
*/ */
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr); bool XMLReadNextStartElement(QXmlStreamReader *reader,
CancelAtom *cancel_atom = nullptr);
} }
+297 -189
View File
@@ -37,278 +37,386 @@
#include "ui/style/style.h" #include "ui/style/style.h"
#include "window/mainwindow/mainwindow.h" #include "window/mainwindow/mainwindow.h"
namespace olive { namespace olive
{
Config Config::current_config_; Config Config::current_config_;
Config::Config() Config::Config()
{ {
SetDefaults(); SetDefaults();
} }
void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data) void Config::SetEntryInternal(const QString &key, NodeValue::Type type,
const QVariant &data)
{ {
config_map_[key] = {type, data}; config_map_[key] = { type, data };
} }
QString Config::GetConfigFilePath() QString Config::GetConfigFilePath()
{ {
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("config.xml")); return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("config.xml"));
} }
Config &Config::Current() Config &Config::Current()
{ {
return current_config_; return current_config_;
} }
void Config::SetDefaults() void Config::SetDefaults()
{ {
config_map_.clear(); config_map_.clear();
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle); SetEntryInternal(QStringLiteral("Style"), NodeValue::kText,
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame); StyleManager::kDefaultStyle);
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2))); SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false); Timecode::kTimecodeDropFrame);
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational,
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true); QVariant::fromValue(rational(2)));
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1); SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt, 20); SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000); true);
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString()); SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false); true);
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false); 1);
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true); 20);
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false); 10000);
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true); false);
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true); false);
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("EditToolSelectsLinks"),
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage); NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"),
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"),
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk); NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true); false);
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true); false);
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true); false);
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean,
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false); true);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"),
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false); NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt,
AutoScroll::kPage);
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"),
NodeValue::kInt, ImportTool::kDWSAsk);
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailInOut); SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled); Timeline::kThumbnailInOut);
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt,
Timeline::kWaveformsEnabled);
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve")); SetEntryInternal(
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve")); QStringLiteral("DefaultVideoTransition"), NodeValue::kText,
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1))); QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(
QStringLiteral("DefaultAudioTransition"), NodeValue::kText,
QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultTransitionLength"),
NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt, 48); SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText, QString()); 48);
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt, QFont::Bold); SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText,
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean, true); QString());
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt,
QFont::Bold);
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000); SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt, ColorCoding::kRed); SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt, ColorCoding::kMaroon); ColorCoding::kRed);
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt, ColorCoding::kOrange); SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt, ColorCoding::kBrown); ColorCoding::kMaroon);
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt, ColorCoding::kYellow); SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt, ColorCoding::kOlive); ColorCoding::kOrange);
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt, ColorCoding::kLime); SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt, ColorCoding::kGreen); ColorCoding::kBrown);
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt, ColorCoding::kCyan); SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt, ColorCoding::kTeal); ColorCoding::kYellow);
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt, ColorCoding::kBlue); SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt, ColorCoding::kNavy); ColorCoding::kOlive);
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt,
ColorCoding::kLime);
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt,
ColorCoding::kGreen);
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt,
ColorCoding::kCyan);
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt,
ColorCoding::kTeal);
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt,
ColorCoding::kBlue);
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt,
ColorCoding::kNavy);
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString()); SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText,
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString()); QString());
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); 48000);
SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"),
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(
QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText,
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV); SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM); ExportFormat::kFormatWAV);
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); ExportCodec::kCodecPCM);
SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"),
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320); NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"),
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(
QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText,
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt,
320);
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0))); SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational,
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60))); QVariant::fromValue(rational(0)));
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational,
QVariant::fromValue(rational(60)));
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920); SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080); 1920);
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1))); SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeValue::kRational, QVariant::fromValue(rational(1001, 30000))); 1080);
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeValue::kInt, VideoParams::kInterlaceNone); SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"),
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"), NodeValue::kBoolean, false); NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"),
SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt, QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO))); NodeValue::kRational,
QVariant::fromValue(rational(1001, 30000)));
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"),
NodeValue::kInt, VideoParams::kInterlaceNone);
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"),
NodeValue::kInt, 48000);
SetEntryInternal(
QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt,
QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
// Online/offline settings // Online/offline settings
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, PixelFormat::F32); SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt,
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, PixelFormat::F16); PixelFormat::F32);
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt,
PixelFormat::F16);
SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt, ColorCoding::kLime); SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt,
ColorCoding::kLime);
} }
void Config::Load() void Config::Load()
{ {
QFile config_file(GetConfigFilePath()); QFile config_file(GetConfigFilePath());
if (!config_file.exists()) { if (!config_file.exists()) {
return; return;
} }
if (!config_file.open(QFile::ReadOnly)) { if (!config_file.open(QFile::ReadOnly)) {
qWarning() << "Failed to load application settings. This session will use defaults."; qWarning()
return; << "Failed to load application settings. This session will use defaults.";
} return;
}
// Reset to defaults // Reset to defaults
current_config_.SetDefaults(); current_config_.SetDefaults();
QXmlStreamReader reader(&config_file); QXmlStreamReader reader(&config_file);
QString config_version; QString config_version;
while (XMLReadNextStartElement(&reader)) { while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("Configuration")) { if (reader.name() == QStringLiteral("Configuration")) {
while (XMLReadNextStartElement(&reader)) { while (XMLReadNextStartElement(&reader)) {
QString key = reader.name().toString(); QString key = reader.name().toString();
QString value = reader.readElementText(); QString value = reader.readElementText();
if (key == QStringLiteral("Version")) { if (key == QStringLiteral("Version")) {
config_version = value; config_version = value;
if (!value.contains(".")) { if (!value.contains(".")) {
qDebug() << "CONFIG: This is a 0.1.x config file, upconvert"; qDebug()
} << "CONFIG: This is a 0.1.x config file, upconvert";
} else if (key == QStringLiteral("DefaultSequenceFrameRate") && !config_version.contains('.')) { }
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest } else if (key == QStringLiteral("DefaultSequenceFrameRate") &&
// supported rational !config_version.contains('.')) {
qDebug() << " CONFIG: Finding closest match to" << value; // 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
// supported rational
qDebug() << " CONFIG: Finding closest match to" << value;
double config_fr = value.toDouble(); double config_fr = value.toDouble();
const QVector<rational>& supported_frame_rates = VideoParams::kSupportedFrameRates; const QVector<rational> &supported_frame_rates =
VideoParams::kSupportedFrameRates;
rational match = supported_frame_rates.first(); rational match = supported_frame_rates.first();
double match_diff = qAbs(match.toDouble() - config_fr); double match_diff = qAbs(match.toDouble() - config_fr);
for (int i=1;i<supported_frame_rates.size();i++) { for (int i = 1; i < supported_frame_rates.size(); i++) {
double diff = qAbs(supported_frame_rates.at(i).toDouble() - config_fr); double diff = qAbs(
supported_frame_rates.at(i).toDouble() - config_fr);
if (diff < match_diff) { if (diff < match_diff) {
match = supported_frame_rates.at(i); match = supported_frame_rates.at(i);
match_diff = diff; match_diff = diff;
} }
} }
qDebug() << " CONFIG: Closest match was" << match.toDouble(); qDebug()
<< " CONFIG: Closest match was" << match.toDouble();
current_config_[key] = QVariant::fromValue(match.flipped()); current_config_[key] = QVariant::fromValue(match.flipped());
} else { } else {
current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false); current_config_[key] = NodeValue::StringToValue(
} current_config_.GetConfigEntryType(key), value, false);
} }
}
//reader.skipCurrentElement(); //reader.skipCurrentElement();
} else { } else {
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
} }
if (reader.hasError()) { if (reader.hasError()) {
QMessageBox::critical(Core::instance()->main_window(), QMessageBox::critical(
QCoreApplication::translate("Config", "Error loading settings"), Core::instance()->main_window(),
QCoreApplication::translate("Config", "Failed to load application settings. This session will " QCoreApplication::translate("Config", "Error loading settings"),
"use defaults.\n\n%1").arg(reader.errorString()), QCoreApplication::translate(
QMessageBox::Ok); "Config",
current_config_.SetDefaults(); "Failed to load application settings. This session will "
} "use defaults.\n\n%1")
.arg(reader.errorString()),
QMessageBox::Ok);
current_config_.SetDefaults();
}
config_file.close(); config_file.close();
} }
void Config::Save() void Config::Save()
{ {
QString real_filename = GetConfigFilePath(); QString real_filename = GetConfigFilePath();
QString temp_filename = FileFunctions::GetSafeTemporaryFilename(real_filename); QString temp_filename =
FileFunctions::GetSafeTemporaryFilename(real_filename);
QFile config_file(temp_filename); QFile config_file(temp_filename);
if (!config_file.open(QFile::WriteOnly)) { if (!config_file.open(QFile::WriteOnly)) {
QMessageBox::critical(Core::instance()->main_window(), QMessageBox::critical(
QCoreApplication::translate("Config", "Error saving settings"), Core::instance()->main_window(),
QCoreApplication::translate("Config", "Failed to save application settings. The application " QCoreApplication::translate("Config", "Error saving settings"),
"may lack write permissions for this location."), QCoreApplication::translate(
QMessageBox::Ok); "Config",
return; "Failed to save application settings. The application "
} "may lack write permissions for this location."),
QMessageBox::Ok);
return;
}
QXmlStreamWriter writer(&config_file); QXmlStreamWriter writer(&config_file);
writer.setAutoFormatting(true); writer.setAutoFormatting(true);
writer.writeStartDocument(); writer.writeStartDocument();
writer.writeStartElement("Configuration"); writer.writeStartElement("Configuration");
// Anything after the hyphen is considered "unimportant" information // Anything after the hyphen is considered "unimportant" information
writer.writeTextElement("Version", QCoreApplication::applicationVersion().split('-').first()); writer.writeTextElement(
"Version", QCoreApplication::applicationVersion().split('-').first());
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_); QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
while (iterator.hasNext()) { while (iterator.hasNext()) {
iterator.next(); iterator.next();
QString value = NodeValue::ValueToString(iterator.value().type, iterator.value().data, false); QString value = NodeValue::ValueToString(iterator.value().type,
iterator.value().data, false);
if (iterator.value().type == NodeValue::kNone) { if (iterator.value().type == NodeValue::kNone) {
qWarning() << "Config key" << iterator.key() << "had null type and was discarded"; qWarning() << "Config key" << iterator.key()
} else { << "had null type and was discarded";
writer.writeTextElement(iterator.key(), value); } else {
} writer.writeTextElement(iterator.key(), value);
} }
}
writer.writeEndElement(); // Configuration writer.writeEndElement(); // Configuration
writer.writeEndDocument(); writer.writeEndDocument();
config_file.close(); config_file.close();
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename, real_filename)) { if (!FileFunctions::RenameFileAllowOverwrite(temp_filename,
qWarning() << QStringLiteral("Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.") real_filename)) {
.arg(real_filename, temp_filename); qWarning()
} << QStringLiteral(
"Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
.arg(real_filename, temp_filename);
}
} }
QVariant Config::operator[](const QString &key) const QVariant Config::operator[](const QString &key) const
{ {
return config_map_[key].data; return config_map_[key].data;
} }
QVariant &Config::operator[](const QString &key) QVariant &Config::operator[](const QString &key)
{ {
return config_map_[key].data; return config_map_[key].data;
} }
NodeValue::Type Config::GetConfigEntryType(const QString &key) const NodeValue::Type Config::GetConfigEntryType(const QString &key) const
{ {
return config_map_[key].type; return config_map_[key].type;
} }
} }
+19 -17
View File
@@ -27,42 +27,44 @@
#include "node/value.h" #include "node/value.h"
namespace olive { namespace olive
{
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)] #define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
#define OLIVE_CONFIG_STR(x) Config::Current()[x] #define OLIVE_CONFIG_STR(x) Config::Current()[x]
class Config { class Config {
public: public:
static Config& Current(); static Config &Current();
void SetDefaults(); void SetDefaults();
static void Load(); static void Load();
static void Save(); static void Save();
QVariant operator[](const QString&) const; QVariant operator[](const QString &) const;
QVariant& operator[](const QString&); QVariant &operator[](const QString &);
NodeValue::Type GetConfigEntryType(const QString& key) const; NodeValue::Type GetConfigEntryType(const QString &key) const;
private: private:
Config(); Config();
struct ConfigEntry { struct ConfigEntry {
NodeValue::Type type; NodeValue::Type type;
QVariant data; QVariant data;
}; };
void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data); void SetEntryInternal(const QString &key, NodeValue::Type type,
const QVariant &data);
QMap<QString, ConfigEntry> config_map_; QMap<QString, ConfigEntry> config_map_;
static Config current_config_; static Config current_config_;
static QString GetConfigFilePath(); static QString GetConfigFilePath();
}; };
} }
+1042 -956
View File
File diff suppressed because it is too large Load Diff
+256 -255
View File
@@ -35,7 +35,8 @@
#include "undo/undostack.h" #include "undo/undostack.h"
#include "widget/projectexplorer/projectviewmodel.h" #include "widget/projectexplorer/projectviewmodel.h"
namespace olive { namespace olive
{
class MainWindow; class MainWindow;
@@ -48,132 +49,125 @@ class MainWindow;
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder, * The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
* opening the import dialog, etc.) * opening the import dialog, etc.)
*/ */
class Core : public QObject class Core : public QObject {
{ Q_OBJECT
Q_OBJECT
public: public:
class CoreParams class CoreParams {
{ public:
public: CoreParams();
CoreParams();
enum RunMode { enum RunMode { kRunNormal, kHeadlessExport, kHeadlessPreCache };
kRunNormal,
kHeadlessExport,
kHeadlessPreCache
};
bool fullscreen() const bool fullscreen() const
{ {
return run_fullscreen_; return run_fullscreen_;
} }
void set_fullscreen(bool e) void set_fullscreen(bool e)
{ {
run_fullscreen_ = e; run_fullscreen_ = e;
} }
RunMode run_mode() const RunMode run_mode() const
{ {
return mode_; return mode_;
} }
void set_run_mode(RunMode m) void set_run_mode(RunMode m)
{ {
mode_ = m; mode_ = m;
} }
const QString startup_project() const const QString startup_project() const
{ {
return startup_project_; return startup_project_;
} }
void set_startup_project(const QString& p) void set_startup_project(const QString &p)
{ {
startup_project_ = p; startup_project_ = p;
} }
const QString& startup_language() const const QString &startup_language() const
{ {
return startup_language_; return startup_language_;
} }
void set_startup_language(const QString& s) void set_startup_language(const QString &s)
{ {
startup_language_ = s; startup_language_ = s;
} }
bool crash_on_startup() const bool crash_on_startup() const
{ {
return crash_; return crash_;
} }
void set_crash_on_startup(bool e) void set_crash_on_startup(bool e)
{ {
crash_ = true; crash_ = true;
} }
private: private:
RunMode mode_; RunMode mode_;
QString startup_project_; QString startup_project_;
QString startup_language_; QString startup_language_;
bool run_fullscreen_; bool run_fullscreen_;
bool crash_; bool crash_;
};
}; /**
/**
* @brief Core Constructor * @brief Core Constructor
* *
* Currently empty * Currently empty
*/ */
Core(const CoreParams& params); Core(const CoreParams &params);
/** /**
* @brief Core object accessible from anywhere in the code * @brief Core object accessible from anywhere in the code
* *
* Use this to access Core functions. * Use this to access Core functions.
*/ */
static Core* instance(); static Core *instance();
const CoreParams& core_params() const const CoreParams &core_params() const
{ {
return core_params_; return core_params_;
} }
/** /**
* @brief Start Olive Core * @brief Start Olive Core
* *
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode). * Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
*/ */
void Start(); void Start();
/** /**
* @brief Stop Olive Core * @brief Stop Olive Core
* *
* Ends all threads and frees all memory ready for the application to exit. * Ends all threads and frees all memory ready for the application to exit.
*/ */
void Stop(); void Stop();
/** /**
* @brief Retrieve main window instance * @brief Retrieve main window instance
* *
* @return * @return
* *
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode. * Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
*/ */
MainWindow* main_window(); MainWindow *main_window();
/** /**
* @brief Retrieve UndoStack object * @brief Retrieve UndoStack object
*/ */
UndoStack* undo_stack(); UndoStack *undo_stack();
/** /**
* @brief Import a list of files * @brief Import a list of files
* *
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to * FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
@@ -181,34 +175,34 @@ public:
* *
* @param urls * @param urls
*/ */
void ImportFiles(const QStringList& urls, Folder *parent); void ImportFiles(const QStringList &urls, Folder *parent);
/** /**
* @brief Get the currently active tool * @brief Get the currently active tool
*/ */
const Tool::Item& tool() const; const Tool::Item &tool() const;
/** /**
* @brief Get the currently selected object that the add tool should make (if the add tool is active) * @brief Get the currently selected object that the add tool should make (if the add tool is active)
*/ */
const Tool::AddableObject& GetSelectedAddableObject() const; const Tool::AddableObject &GetSelectedAddableObject() const;
/** /**
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active) * @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
*/ */
const QString& GetSelectedTransition() const; const QString &GetSelectedTransition() const;
/** /**
* @brief Get current snapping value * @brief Get current snapping value
*/ */
const bool& snapping() const; const bool &snapping() const;
/** /**
* @brief Returns a list of the most recently opened/saved projects * @brief Returns a list of the most recently opened/saved projects
*/ */
const QStringList& GetRecentProjects() const; const QStringList &GetRecentProjects() const;
/** /**
* @brief Get the currently active project * @brief Get the currently active project
* *
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project * Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
@@ -218,402 +212,409 @@ public:
* *
* The active Project file, or nullptr if the heuristic couldn't find one. * The active Project file, or nullptr if the heuristic couldn't find one.
*/ */
Project* GetActiveProject() const; Project *GetActiveProject() const;
Folder* GetSelectedFolderInActiveProject() const; Folder *GetSelectedFolderInActiveProject() const;
/** /**
* @brief Gets current timecode display mode * @brief Gets current timecode display mode
*/ */
Timecode::Display GetTimecodeDisplay() const; Timecode::Display GetTimecodeDisplay() const;
/** /**
* @brief Sets current timecode display mode * @brief Sets current timecode display mode
*/ */
void SetTimecodeDisplay(Timecode::Display d); void SetTimecodeDisplay(Timecode::Display d);
/** /**
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified()) * @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
*/ */
void SetAutorecoveryInterval(int minutes); void SetAutorecoveryInterval(int minutes);
static void CopyStringToClipboard(const QString& s); static void CopyStringToClipboard(const QString &s);
static QString PasteStringFromClipboard(); static QString PasteStringFromClipboard();
/** /**
* @brief Recursively count files in a file/directory list * @brief Recursively count files in a file/directory list
*/ */
static int CountFilesInFileList(const QFileInfoList &filenames); static int CountFilesInFileList(const QFileInfoList &filenames);
/** /**
* @brief Show a dialog to the user to rename a set of nodes * @brief Show a dialog to the user to rename a set of nodes
*/ */
bool LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent = nullptr); bool LabelNodes(const QVector<Node *> &nodes,
MultiUndoCommand *parent = nullptr);
/** /**
* @brief Create a new sequence named appropriately for the active project * @brief Create a new sequence named appropriately for the active project
*/ */
static Sequence* CreateNewSequenceForProject(const QString &format, Project *project); static Sequence *CreateNewSequenceForProject(const QString &format,
static Sequence* CreateNewSequenceForProject(Project *project) Project *project);
{ static Sequence *CreateNewSequenceForProject(Project *project)
return CreateNewSequenceForProject(tr("Sequence %1"), project); {
} return CreateNewSequenceForProject(tr("Sequence %1"), project);
}
/** /**
* @brief Opens a project from the recently opened list * @brief Opens a project from the recently opened list
*/ */
void OpenProjectFromRecentList(int index); void OpenProjectFromRecentList(int index);
/** /**
* @brief Closes a project * @brief Closes a project
*/ */
bool CloseProject(bool auto_open_new, bool ignore_modified = false); bool CloseProject(bool auto_open_new, bool ignore_modified = false);
/** /**
* @brief Runs a modal cache task on the currently active sequence * @brief Runs a modal cache task on the currently active sequence
*/ */
void CacheActiveSequence(bool in_out_only); void CacheActiveSequence(bool in_out_only);
/** /**
* @brief Check each footage object for whether it still exists or has changed * @brief Check each footage object for whether it still exists or has changed
*/ */
bool ValidateFootageInLoadedProject(Project* project, const QString &project_saved_url); bool ValidateFootageInLoadedProject(Project *project,
const QString &project_saved_url);
/** /**
* @brief Changes the current language * @brief Changes the current language
*/ */
bool SetLanguage(const QString& locale); bool SetLanguage(const QString &locale);
/** /**
* @brief Show message in main window's status bar * @brief Show message in main window's status bar
* *
* Shorthand for Core::instance()->main_window()->statusBar()->showMessage(); * Shorthand for Core::instance()->main_window()->statusBar()->showMessage();
*/ */
void ShowStatusBarMessage(const QString& s, int timeout = 0); void ShowStatusBarMessage(const QString &s, int timeout = 0);
void ClearStatusBarMessage(); void ClearStatusBarMessage();
void OpenRecoveryProject(const QString& filename); void OpenRecoveryProject(const QString &filename);
void OpenNodeInViewer(ViewerOutput* viewer); void OpenNodeInViewer(ViewerOutput *viewer);
void OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image); void OpenExportDialogForViewer(ViewerOutput *viewer,
bool start_still_image);
bool IsMagicEnabled() const { return magic_; } bool IsMagicEnabled() const
{
return magic_;
}
public slots: public slots:
/** /**
* @brief Starts an open file dialog to load a project from file * @brief Starts an open file dialog to load a project from file
*/ */
void OpenProject(); void OpenProject();
/** /**
* @brief Saves the current project * @brief Saves the current project
*/ */
bool SaveProject(); bool SaveProject();
/** /**
* @brief Performs a "save as" on the current project * @brief Performs a "save as" on the current project
*/ */
bool SaveProjectAs(); bool SaveProjectAs();
void RevertProject(); void RevertProject();
/** /**
* @brief Set the current application-wide tool * @brief Set the current application-wide tool
* *
* @param tool * @param tool
*/ */
void SetTool(const Tool::Item& tool); void SetTool(const Tool::Item &tool);
/** /**
* @brief Set the current snapping setting * @brief Set the current snapping setting
*/ */
void SetSnapping(const bool& b); void SetSnapping(const bool &b);
/** /**
* @brief Show an About dialog * @brief Show an About dialog
*/ */
void DialogAboutShow(); void DialogAboutShow();
/** /**
* @brief Open the import footage dialog and import the files selected (runs ImportFiles()) * @brief Open the import footage dialog and import the files selected (runs ImportFiles())
*/ */
void DialogImportShow(); void DialogImportShow();
/** /**
* @brief Show Preferences dialog * @brief Show Preferences dialog
*/ */
void DialogPreferencesShow(); void DialogPreferencesShow();
/** /**
* @brief Show Project Properties dialog * @brief Show Project Properties dialog
*/ */
void DialogProjectPropertiesShow(); void DialogProjectPropertiesShow();
/** /**
* @brief Show Export dialog * @brief Show Export dialog
*/ */
void DialogExportShow(); void DialogExportShow();
/** /**
* @brief Show OTIO import dialog * @brief Show OTIO import dialog
*/ */
#ifdef USE_OTIO #ifdef USE_OTIO
bool DialogImportOTIOShow(const QList<Sequence*>& sequences); bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
#endif #endif
/** /**
* @brief Create a new folder in the currently active project * @brief Create a new folder in the currently active project
*/ */
void CreateNewFolder(); void CreateNewFolder();
/** /**
* @brief Create a new sequence in the currently active project * @brief Create a new sequence in the currently active project
*/ */
void CreateNewSequence(); void CreateNewSequence();
/** /**
* @brief Set the currently selected object that the add tool should make * @brief Set the currently selected object that the add tool should make
*/ */
void SetSelectedAddableObject(const Tool::AddableObject& obj); void SetSelectedAddableObject(const Tool::AddableObject &obj);
/** /**
* @brief Set the currently selected object that the add tool should make * @brief Set the currently selected object that the add tool should make
*/ */
void SetSelectedTransitionObject(const QString& obj); void SetSelectedTransitionObject(const QString &obj);
/** /**
* @brief Clears the list of recently opened/saved projects * @brief Clears the list of recently opened/saved projects
*/ */
void ClearOpenRecentList(); void ClearOpenRecentList();
/** /**
* @brief Creates a new empty project and opens it * @brief Creates a new empty project and opens it
*/ */
void CreateNewProject(); void CreateNewProject();
void CheckForAutoRecoveries(); void CheckForAutoRecoveries();
void BrowseAutoRecoveries(); void BrowseAutoRecoveries();
void RequestPixelSamplingInViewers(bool e); void RequestPixelSamplingInViewers(bool e);
void WarnCacheFull(); void WarnCacheFull();
void SetMagic(bool e) void SetMagic(bool e)
{ {
magic_ = e; magic_ = e;
} }
signals: signals:
/** /**
* @brief Signal emitted when the tool is changed from somewhere * @brief Signal emitted when the tool is changed from somewhere
*/ */
void ToolChanged(const Tool::Item& tool); void ToolChanged(const Tool::Item &tool);
/** /**
* @brief Signal emitted when addable object changes through SetSelectedAddableObject * @brief Signal emitted when addable object changes through SetSelectedAddableObject
*/ */
void AddableObjectChanged(Tool::AddableObject o); void AddableObjectChanged(Tool::AddableObject o);
/** /**
* @brief Signal emitted when the snapping setting is changed * @brief Signal emitted when the snapping setting is changed
*/ */
void SnappingChanged(const bool& b); void SnappingChanged(const bool &b);
/** /**
* @brief Signal emitted when the default timecode display mode changed * @brief Signal emitted when the default timecode display mode changed
*/ */
void TimecodeDisplayChanged(Timecode::Display d); void TimecodeDisplayChanged(Timecode::Display d);
/** /**
* @brief Signal emitted when a change is made to the open recent list * @brief Signal emitted when a change is made to the open recent list
*/ */
void OpenRecentListChanged(); void OpenRecentListChanged();
/** /**
* @brief Enable mouse color sampling functionality on all viewers * @brief Enable mouse color sampling functionality on all viewers
* *
* This can be slow, so we only turn it on when we need it. * This can be slow, so we only turn it on when we need it.
*/ */
void ColorPickerEnabled(bool e); void ColorPickerEnabled(bool e);
/** /**
* @brief A viewer with color picked enabled has emitted a color * @brief A viewer with color picked enabled has emitted a color
*/ */
void ColorPickerColorEmitted(const Color &reference, const Color &display); void ColorPickerColorEmitted(const Color &reference, const Color &display);
private: private:
/** /**
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects * @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
*/ */
static QString GetProjectFilter(bool include_any_filter); static QString GetProjectFilter(bool include_any_filter);
/** /**
* @brief Returns the filename where the recently opened/saved projects should be stored * @brief Returns the filename where the recently opened/saved projects should be stored
*/ */
static QString GetRecentProjectsFilePath(); static QString GetRecentProjectsFilePath();
/** /**
* @brief Called only on startup to set the locale * @brief Called only on startup to set the locale
*/ */
void SetStartupLocale(); void SetStartupLocale();
/** /**
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists) * @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
*/ */
void PushRecentlyOpenedProject(const QString &s); void PushRecentlyOpenedProject(const QString &s);
/** /**
* @brief Declare custom types/classes for Qt's signal/slot system * @brief Declare custom types/classes for Qt's signal/slot system
* *
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain * Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
* them all in a function here. * them all in a function here.
*/ */
void DeclareTypesForQt(); void DeclareTypesForQt();
/** /**
* @brief Start GUI portion of Olive * @brief Start GUI portion of Olive
* *
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will * Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
* create an application instance that is completely valid minus the UI (e.g. for CLI modes). * create an application instance that is completely valid minus the UI (e.g. for CLI modes).
*/ */
void StartGUI(bool full_screen); void StartGUI(bool full_screen);
/** /**
* @brief Internal function for saving a project to a file * @brief Internal function for saving a project to a file
*/ */
void SaveProjectInternal(const QString &override_filename = QString()); void SaveProjectInternal(const QString &override_filename = QString());
/** /**
* @brief Retrieves the currently most active sequence for exporting * @brief Retrieves the currently most active sequence for exporting
*/ */
ViewerOutput *GetSequenceToExport(); ViewerOutput *GetSequenceToExport();
static QString GetAutoRecoveryIndexFilename(); static QString GetAutoRecoveryIndexFilename();
void SaveUnrecoveredList(); void SaveUnrecoveredList();
bool RevertProjectInternal(bool by_opening_existing); bool RevertProjectInternal(bool by_opening_existing);
void SaveRecentProjectsList(); void SaveRecentProjectsList();
/** /**
* @brief Adds a project to the "open projects" list * @brief Adds a project to the "open projects" list
*/ */
void AddOpenProject(olive::Project* p, bool add_to_recents = false); void AddOpenProject(olive::Project *p, bool add_to_recents = false);
bool AddOpenProjectFromTask(Task* task, bool add_to_recents); bool AddOpenProjectFromTask(Task *task, bool add_to_recents);
void SetActiveProject(Project *p); void SetActiveProject(Project *p);
/** /**
* @brief Internal main window object * @brief Internal main window object
*/ */
MainWindow* main_window_; MainWindow *main_window_;
/** /**
* @brief List of currently open projects * @brief List of currently open projects
*/ */
Project *open_project_; Project *open_project_;
/** /**
* @brief Currently active tool * @brief Currently active tool
*/ */
Tool::Item tool_; Tool::Item tool_;
/** /**
* @brief Currently active addable object * @brief Currently active addable object
*/ */
Tool::AddableObject addable_object_; Tool::AddableObject addable_object_;
/** /**
* @brief Currently selected transition * @brief Currently selected transition
*/ */
QString selected_transition_; QString selected_transition_;
/** /**
* @brief Current snapping setting * @brief Current snapping setting
*/ */
bool snapping_; bool snapping_;
/** /**
* @brief Internal timer for saving autorecovery files * @brief Internal timer for saving autorecovery files
*/ */
QTimer autorecovery_timer_; QTimer autorecovery_timer_;
/** /**
* @brief Application-wide undo stack instance * @brief Application-wide undo stack instance
*/ */
UndoStack undo_stack_; UndoStack undo_stack_;
/** /**
* @brief List of most recently opened/saved projects * @brief List of most recently opened/saved projects
*/ */
QStringList recent_projects_; QStringList recent_projects_;
/** /**
* @brief Parameters set up in main() determining how the program should run * @brief Parameters set up in main() determining how the program should run
*/ */
CoreParams core_params_; CoreParams core_params_;
/** /**
* @brief Static singleton core instance * @brief Static singleton core instance
*/ */
static Core* instance_; static Core *instance_;
/** /**
* @brief Internal translator * @brief Internal translator
*/ */
QTranslator* translator_; QTranslator *translator_;
/** /**
* @brief List of projects that are unsaved but have autorecovery projects * @brief List of projects that are unsaved but have autorecovery projects
*/ */
QVector<QUuid> autorecovered_projects_; QVector<QUuid> autorecovered_projects_;
/** /**
* @brief Do something debug related * @brief Do something debug related
*/ */
bool magic_; bool magic_;
/** /**
* @brief How many widgets currently need pixel sampling access * @brief How many widgets currently need pixel sampling access
*/ */
int pixel_sampling_users_; int pixel_sampling_users_;
bool shown_cache_full_warning_; bool shown_cache_full_warning_;
private slots: private slots:
void SaveAutorecovery(); void SaveAutorecovery();
void ProjectSaveSucceeded(Task *task); void ProjectSaveSucceeded(Task *task);
bool AddOpenProjectFromTaskAndAddToRecents(Task* task) bool AddOpenProjectFromTaskAndAddToRecents(Task *task)
{ {
return AddOpenProjectFromTask(task, true); return AddOpenProjectFromTask(task, true);
} }
void ImportTaskComplete(Task *task); void ImportTaskComplete(Task *task);
bool ConfirmImageSequence(const QString &filename); bool ConfirmImageSequence(const QString &filename);
void ProjectWasModified(bool e); void ProjectWasModified(bool e);
bool StartHeadlessExport(); bool StartHeadlessExport();
void OpenStartupProject(); void OpenStartupProject();
void AddRecoveryProjectFromTask(Task* task); void AddRecoveryProjectFromTask(Task *task);
/** /**
* @brief Internal project open * @brief Internal project open
*/ */
void OpenProjectInternal(const QString& filename, bool recovery_project = false); void OpenProjectInternal(const QString &filename,
bool recovery_project = false);
void ImportSingleFile(const QString &f);
void ImportSingleFile(const QString &f);
}; };
} }
+259 -224
View File
@@ -40,335 +40,370 @@
#include "common/filefunctions.h" #include "common/filefunctions.h"
#include "version.h" #include "version.h"
namespace olive { namespace olive
CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
{ {
setWindowTitle(tr("Olive"));
setWindowFlags(Qt::WindowStaysOnTopHint);
report_filename_ = report_path; CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
waiting_for_upload_ = false; {
setWindowTitle(tr("Olive"));
setWindowFlags(Qt::WindowStaysOnTopHint);
QVBoxLayout* layout = new QVBoxLayout(this); report_filename_ = report_path;
waiting_for_upload_ = false;
layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by " QVBoxLayout *layout = new QVBoxLayout(this);
"sending an error report.")));
QSplitter* splitter = new QSplitter(Qt::Vertical); layout->addWidget(new QLabel(
splitter->setChildrenCollapsible(false); tr("We're sorry, Olive has crashed. Please help us fix it by "
layout->addWidget(splitter); "sending an error report.")));
summary_edit_ = new QTextEdit(); QSplitter *splitter = new QSplitter(Qt::Vertical);
summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as " splitter->setChildrenCollapsible(false);
"possible. If you can, provide steps to reproduce this crash.")); layout->addWidget(splitter);
splitter->addWidget(summary_edit_); summary_edit_ = new QTextEdit();
summary_edit_->setPlaceholderText(
tr("Describe what you were doing in as much detail as "
"possible. If you can, provide steps to reproduce this crash."));
QWidget* crash_widget = new QWidget(); splitter->addWidget(summary_edit_);
QVBoxLayout* crash_widget_layout = new QVBoxLayout(crash_widget);
crash_widget_layout->setMargin(0);
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:"))); QWidget *crash_widget = new QWidget();
QVBoxLayout *crash_widget_layout = new QVBoxLayout(crash_widget);
crash_widget_layout->setMargin(0);
crash_report_ = new QTextEdit(); crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
crash_report_->setReadOnly(true);
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
crash_widget_layout->addWidget(crash_report_);
splitter->addWidget(crash_widget); crash_report_ = new QTextEdit();
crash_report_->setReadOnly(true);
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
crash_widget_layout->addWidget(crash_report_);
QHBoxLayout* btn_layout = new QHBoxLayout(); splitter->addWidget(crash_widget);
btn_layout->setMargin(0);
btn_layout->addStretch();
send_report_btn_ = new QPushButton(tr("Send Error Report")); QHBoxLayout *btn_layout = new QHBoxLayout();
connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport); btn_layout->setMargin(0);
btn_layout->addWidget(send_report_btn_); btn_layout->addStretch();
dont_send_btn_ = new QPushButton(tr("Don't Send")); send_report_btn_ = new QPushButton(tr("Send Error Report"));
connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject); connect(send_report_btn_, &QPushButton::clicked, this,
btn_layout->addWidget(dont_send_btn_); &CrashHandlerDialog::SendErrorReport);
btn_layout->addWidget(send_report_btn_);
layout->addLayout(btn_layout); dont_send_btn_ = new QPushButton(tr("Don't Send"));
connect(dont_send_btn_, &QPushButton::clicked, this,
&CrashHandlerDialog::reject);
btn_layout->addWidget(dont_send_btn_);
crash_report_->setEnabled(false); layout->addLayout(btn_layout);
send_report_btn_->setEnabled(false);
crash_report_->setText(tr("Waiting for crash report to be generated...")); crash_report_->setEnabled(false);
send_report_btn_->setEnabled(false);
GenerateReport(); crash_report_->setText(tr("Waiting for crash report to be generated..."));
GenerateReport();
} }
void CrashHandlerDialog::SetGUIObjectsEnabled(bool e) void CrashHandlerDialog::SetGUIObjectsEnabled(bool e)
{ {
summary_edit_->setEnabled(e); summary_edit_->setEnabled(e);
crash_report_->setEnabled(e); crash_report_->setEnabled(e);
send_report_btn_->setEnabled(e); send_report_btn_->setEnabled(e);
dont_send_btn_->setEnabled(e); dont_send_btn_->setEnabled(e);
} }
QString CrashHandlerDialog::GetSymbolPath() QString CrashHandlerDialog::GetSymbolPath()
{ {
QDir app_path(qApp->applicationDirPath()); QDir app_path(qApp->applicationDirPath());
QString symbols_path; QString symbols_path;
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
symbols_path = app_path.filePath(QStringLiteral("symbols")); symbols_path = app_path.filePath(QStringLiteral("symbols"));
#elif BUILDFLAG(IS_LINUX) #elif BUILDFLAG(IS_LINUX)
app_path.cdUp(); app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("share/olive-editor/symbols")); symbols_path =
app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
#elif BUILDFLAG(IS_APPLE) #elif BUILDFLAG(IS_APPLE)
app_path.cdUp(); app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols")); symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
#endif #endif
return symbols_path; return symbols_path;
} }
void CrashHandlerDialog::GenerateReport() void CrashHandlerDialog::GenerateReport()
{ {
QProcess* p = new QProcess(); QProcess *p = new QProcess();
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &CrashHandlerDialog::ReadProcessFinished); this, &CrashHandlerDialog::ReadProcessFinished);
connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData); connect(p, &QProcess::readyReadStandardOutput, this,
&CrashHandlerDialog::ReadProcessHasData);
QString stackwalk_filename = FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("minidump_stackwalk")); QString stackwalk_filename =
FileFunctions::GetFormattedExecutableForPlatform(
QStringLiteral("minidump_stackwalk"));
QString stackwalk_bin = QDir(qApp->applicationDirPath()).filePath(stackwalk_filename); QString stackwalk_bin =
p->start(stackwalk_bin, {report_filename_, GetSymbolPath()}); QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin)); p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
crash_report_->setText(
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
} }
void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply) void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
{ {
waiting_for_upload_ = false; waiting_for_upload_ = false;
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
// Close dialog // Close dialog
QDialog::accept(); QDialog::accept();
} else { } else {
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Critical); b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Upload Failed")); b.setWindowTitle(tr("Upload Failed"));
b.setText(tr("Failed to send error report (%1). Please try again later.").arg(QString::number(reply->error()))); b.setText(
b.addButton(QMessageBox::Ok); tr("Failed to send error report (%1). Please try again later.")
b.exec(); .arg(QString::number(reply->error())));
b.addButton(QMessageBox::Ok);
b.exec();
SetGUIObjectsEnabled(true); SetGUIObjectsEnabled(true);
} }
} }
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &se) void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply,
const QList<QSslError> &se)
{ {
QStringList errors; QStringList errors;
for (const QSslError &err : se) { for (const QSslError &err : se) {
errors.append(err.errorString()); errors.append(err.errorString());
} }
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Critical); b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("SSL Error")); b.setWindowTitle(tr("SSL Error"));
b.setText(tr("Encountered the following SSL errors:\n\n%1").arg(errors.join('\n'))); b.setText(tr("Encountered the following SSL errors:\n\n%1")
b.addButton(QMessageBox::Ok); .arg(errors.join('\n')));
b.exec(); b.addButton(QMessageBox::Ok);
b.exec();
} }
void CrashHandlerDialog::AttemptToFindReport() void CrashHandlerDialog::AttemptToFindReport()
{ {
// If we found it, use it, otherwise wait a second and try again // If we found it, use it, otherwise wait a second and try again
if (report_filename_.isEmpty()) { if (report_filename_.isEmpty()) {
// Couldn't find report, try again in one second // Couldn't find report, try again in one second
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport); QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
} else { } else {
GenerateReport(); GenerateReport();
} }
} }
void CrashHandlerDialog::ReadProcessHasData() void CrashHandlerDialog::ReadProcessHasData()
{ {
report_data_.append(static_cast<QProcess*>(sender())->readAllStandardOutput()); report_data_.append(
static_cast<QProcess *>(sender())->readAllStandardOutput());
} }
void CrashHandlerDialog::ReadProcessFinished() void CrashHandlerDialog::ReadProcessFinished()
{ {
SetGUIObjectsEnabled(true); SetGUIObjectsEnabled(true);
crash_report_->setText(report_data_); crash_report_->setText(report_data_);
delete sender(); delete sender();
} }
void CrashHandlerDialog::SendErrorReport() void CrashHandlerDialog::SendErrorReport()
{ {
if (summary_edit_->document()->isEmpty()) { if (summary_edit_->document()->isEmpty()) {
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Question); b.setIcon(QMessageBox::Question);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setText(tr("You must write a description to submit this crash report.")); b.setText(
b.addButton(QMessageBox::Ok); tr("You must write a description to submit this crash report."));
b.exec(); b.addButton(QMessageBox::Ok);
return; b.exec();
} return;
}
QNetworkAccessManager* manager = new QNetworkAccessManager(); QNetworkAccessManager *manager = new QNetworkAccessManager();
connect(manager, &QNetworkAccessManager::finished, this, &CrashHandlerDialog::ReplyFinished); connect(manager, &QNetworkAccessManager::finished, this,
connect(manager, &QNetworkAccessManager::sslErrors, this, &CrashHandlerDialog::HandleSslErrors); &CrashHandlerDialog::ReplyFinished);
connect(manager, &QNetworkAccessManager::sslErrors, this,
&CrashHandlerDialog::HandleSslErrors);
QNetworkRequest request; QNetworkRequest request;
request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
request.setUrl(QStringLiteral("https://olivevideoeditor.org/crashpad/report.php")); request.setUrl(
QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
// Create HTTP form // Create HTTP form
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpMultiPart *multipart =
new QHttpMultiPart(QHttpMultiPart::FormDataType);
// Create description section // Create description section
QHttpPart desc_part; QHttpPart desc_part;
desc_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8")); desc_part.setHeader(QNetworkRequest::ContentTypeHeader,
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"description\"")); QStringLiteral("text/plain; charset=UTF-8"));
desc_part.setBody(summary_edit_->toPlainText().toUtf8()); desc_part.setHeader(QNetworkRequest::ContentDispositionHeader,
multipart->append(desc_part); QStringLiteral("form-data; name=\"description\""));
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
multipart->append(desc_part);
// Create report section // Create report section
QHttpPart report_part; QHttpPart report_part;
report_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8")); report_part.setHeader(QNetworkRequest::ContentTypeHeader,
report_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"report\"")); QStringLiteral("text/plain; charset=UTF-8"));
report_part.setBody(report_data_); report_part.setHeader(QNetworkRequest::ContentDispositionHeader,
multipart->append(report_part); QStringLiteral("form-data; name=\"report\""));
report_part.setBody(report_data_);
multipart->append(report_part);
// Create commit section // Create commit section
QHttpPart commit_part; QHttpPart commit_part;
commit_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8")); commit_part.setHeader(QNetworkRequest::ContentTypeHeader,
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"commit\"")); QStringLiteral("text/plain; charset=UTF-8"));
commit_part.setBody(kAppVersionLong.toUtf8()); commit_part.setHeader(QNetworkRequest::ContentDispositionHeader,
multipart->append(commit_part); QStringLiteral("form-data; name=\"commit\""));
commit_part.setBody(kAppVersionLong.toUtf8());
multipart->append(commit_part);
// Create dump section // Create dump section
QHttpPart dump_part; QHttpPart dump_part;
dump_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream")); dump_part.setHeader(QNetworkRequest::ContentTypeHeader,
dump_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"") QStringLiteral("application/octet-stream"));
.arg(QFileInfo(report_filename_).fileName())); dump_part.setHeader(
QFile* dump_file = new QFile(report_filename_); QNetworkRequest::ContentDispositionHeader,
dump_file->open(QFile::ReadOnly); QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
dump_part.setBodyDevice(dump_file); .arg(QFileInfo(report_filename_).fileName()));
dump_file->setParent(multipart); // Delete file with multipart QFile *dump_file = new QFile(report_filename_);
multipart->append(dump_part); dump_file->open(QFile::ReadOnly);
dump_part.setBodyDevice(dump_file);
dump_file->setParent(multipart); // Delete file with multipart
multipart->append(dump_part);
// Find symbol file // Find symbol file
QDir symbol_dir(GetSymbolPath()); QDir symbol_dir(GetSymbolPath());
QString symbol_bin_name; QString symbol_bin_name;
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
symbol_bin_name = QStringLiteral("olive-editor.pdb"); symbol_bin_name = QStringLiteral("olive-editor.pdb");
#elif BUILDFLAG(IS_APPLE) #elif BUILDFLAG(IS_APPLE)
symbol_bin_name = QStringLiteral("Olive"); symbol_bin_name = QStringLiteral("Olive");
#else #else
symbol_bin_name = QStringLiteral("olive-editor"); symbol_bin_name = QStringLiteral("olive-editor");
#endif #endif
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name)); symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
QStringList folders_in_symbol_path = symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); QStringList folders_in_symbol_path =
symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
if (folders_in_symbol_path.size() > 0) { if (folders_in_symbol_path.size() > 0) {
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first())); symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
} else { } else {
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Critical); b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Failed to send report")); b.setWindowTitle(tr("Failed to send report"));
b.setText(tr("Failed to find symbols necessary to send report. " b.setText(tr("Failed to find symbols necessary to send report. "
"This is a packaging issue. Please notify " "This is a packaging issue. Please notify "
"the maintainers of this package.")); "the maintainers of this package."));
b.addButton(QMessageBox::Ok); b.addButton(QMessageBox::Ok);
b.exec(); b.exec();
return; return;
} }
// Create sym section // Create sym section
QString symbol_filename; QString symbol_filename;
#if BUILDFLAG(IS_APPLE) #if BUILDFLAG(IS_APPLE)
symbol_filename = QStringLiteral("Olive.sym"); symbol_filename = QStringLiteral("Olive.sym");
#else #else
symbol_filename = QStringLiteral("olive-editor.sym"); symbol_filename = QStringLiteral("olive-editor.sym");
#endif #endif
QString symbol_full_path = symbol_dir.filePath(symbol_filename); QString symbol_full_path = symbol_dir.filePath(symbol_filename);
QHttpPart sym_part; QHttpPart sym_part;
sym_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream")); sym_part.setHeader(QNetworkRequest::ContentTypeHeader,
sym_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"") QStringLiteral("application/octet-stream"));
.arg(symbol_filename)); sym_part.setHeader(
QFile sym_file(symbol_full_path); QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
.arg(symbol_filename));
QFile sym_file(symbol_full_path);
if (!sym_file.open(QFile::ReadOnly)) { if (!sym_file.open(QFile::ReadOnly)) {
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Critical); b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Failed to send report")); b.setWindowTitle(tr("Failed to send report"));
b.setText(tr("Failed to open symbol file. You may not have " b.setText(tr("Failed to open symbol file. You may not have "
"permission to access it.")); "permission to access it."));
b.addButton(QMessageBox::Ok); b.addButton(QMessageBox::Ok);
b.exec(); b.exec();
return; return;
} }
QByteArray symbol_data = qCompress(sym_file.readAll(), 9); QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
sym_file.close(); sym_file.close();
sym_part.setBody(symbol_data); sym_part.setBody(symbol_data);
multipart->append(sym_part); multipart->append(sym_part);
manager->post(request, multipart); manager->post(request, multipart);
SetGUIObjectsEnabled(false); SetGUIObjectsEnabled(false);
waiting_for_upload_ = true; waiting_for_upload_ = true;
} }
void CrashHandlerDialog::closeEvent(QCloseEvent* e) void CrashHandlerDialog::closeEvent(QCloseEvent *e)
{ {
QMessageBox b(this); QMessageBox b(this);
b.setIcon(QMessageBox::Warning); b.setIcon(QMessageBox::Warning);
b.setWindowModality(Qt::WindowModal); b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Confirm Close")); b.setWindowTitle(tr("Confirm Close"));
b.setText(tr("Crash report is still uploading. Closing now may result in no " b.setText(
"report being sent. Are you sure you wish to close?")); tr("Crash report is still uploading. Closing now may result in no "
b.addButton(QMessageBox::Ok); "report being sent. Are you sure you wish to close?"));
b.addButton(QMessageBox::Cancel); b.addButton(QMessageBox::Ok);
b.addButton(QMessageBox::Cancel);
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) { if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
e->ignore(); e->ignore();
} else { } else {
e->accept(); e->accept();
} }
} }
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QString report; QString report;
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
int num_args; int num_args;
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args); LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
if (num_args < 2) { if (num_args < 2) {
LocalFree(args); LocalFree(args);
return 1; return 1;
} }
report = QString::fromWCharArray(args[1]); report = QString::fromWCharArray(args[1]);
LocalFree(args); LocalFree(args);
#else #else
if (argc < 2) { if (argc < 2) {
return 1; return 1;
} }
report = argv[1]; report = argv[1];
#endif #endif
QApplication a(argc, argv); QApplication a(argc, argv);
olive::CrashHandlerDialog chd(report); olive::CrashHandlerDialog chd(report);
chd.open(); chd.open();
return a.exec(); return a.exec();
} }
+22 -23
View File
@@ -30,51 +30,50 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
class CrashHandlerDialog : public QDialog
{ {
Q_OBJECT
class CrashHandlerDialog : public QDialog {
Q_OBJECT
public: public:
CrashHandlerDialog(const QString& report_path); CrashHandlerDialog(const QString &report_path);
private: private:
void SetGUIObjectsEnabled(bool e); void SetGUIObjectsEnabled(bool e);
void GenerateReport(); void GenerateReport();
static QString GetSymbolPath(); static QString GetSymbolPath();
QTextEdit* summary_edit_; QTextEdit *summary_edit_;
QTextEdit* crash_report_; QTextEdit *crash_report_;
QPushButton* send_report_btn_; QPushButton *send_report_btn_;
QPushButton* dont_send_btn_; QPushButton *dont_send_btn_;
QString report_filename_; QString report_filename_;
QByteArray report_data_; QByteArray report_data_;
bool waiting_for_upload_; bool waiting_for_upload_;
protected: protected:
virtual void closeEvent(QCloseEvent* e) override; virtual void closeEvent(QCloseEvent *e) override;
private slots: private slots:
void ReplyFinished(QNetworkReply *reply); void ReplyFinished(QNetworkReply *reply);
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors); void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
void AttemptToFindReport(); void AttemptToFindReport();
void ReadProcessHasData(); void ReadProcessHasData();
void ReadProcessFinished(); void ReadProcessFinished();
void SendErrorReport();
void SendErrorReport();
}; };
} }
+96 -83
View File
@@ -30,114 +30,127 @@
#include "patreon.h" #include "patreon.h"
#include "scrollinglabel.h" #include "scrollinglabel.h"
namespace olive { namespace olive
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
QDialog(parent)
{ {
if (welcome_dialog) {
setWindowTitle(tr("Welcome to %1").arg(QApplication::applicationName()));
} else {
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
}
QFontMetrics fm = fontMetrics(); AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
: QDialog(parent)
{
if (welcome_dialog) {
setWindowTitle(
tr("Welcome to %1").arg(QApplication::applicationName()));
} else {
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
}
QVBoxLayout* layout = new QVBoxLayout(this); QFontMetrics fm = fontMetrics();
layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
QHBoxLayout *horiz_layout = new QHBoxLayout(); QVBoxLayout *layout = new QVBoxLayout(this);
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height()); layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
horiz_layout->setSpacing(fm.height()*2); fm.height());
QLabel* icon = new QLabel(QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>")); QHBoxLayout *horiz_layout = new QHBoxLayout();
icon->setAlignment(Qt::AlignCenter); horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
horiz_layout->addWidget(icon); fm.height());
horiz_layout->setSpacing(fm.height() * 2);
// Construct About text QLabel *icon = new QLabel(
QLabel* label = QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
new QLabel(QStringLiteral("<html><head/><body>" icon->setAlignment(Qt::AlignCenter);
"<p><b>%1</b> %2</p>" // AppName (version identifier) horiz_layout->addWidget(icon);
"<p><a href=\"https://www.olivevideoeditor.org/\">"
"https://www.olivevideoeditor.org/"
"</a></p>"
"<p>%3</p>" // First statement
"</body></html>").arg(QApplication::applicationName(),
QApplication::applicationVersion(),
tr("Olive is a free open source non-linear video editor. "
"This software is licensed under the GNU GPL Version 3.")));
// Set text formatting // Construct About text
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); QLabel *label = new QLabel(
label->setWordWrap(true); QStringLiteral("<html><head/><body>"
label->setOpenExternalLinks(true); "<p><b>%1</b> %2</p>" // AppName (version identifier)
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); "<p><a href=\"https://www.olivevideoeditor.org/\">"
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); "https://www.olivevideoeditor.org/"
label->setCursor(Qt::IBeamCursor); "</a></p>"
horiz_layout->addWidget(label); "<p>%3</p>" // First statement
"</body></html>")
.arg(QApplication::applicationName(),
QApplication::applicationVersion(),
tr("Olive is a free open source non-linear video editor. "
"This software is licensed under the GNU GPL Version 3.")));
layout->addLayout(horiz_layout); // Set text formatting
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
label->setWordWrap(true);
label->setOpenExternalLinks(true);
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
label->setTextInteractionFlags(Qt::TextSelectableByMouse |
Qt::LinksAccessibleByMouse);
label->setCursor(Qt::IBeamCursor);
horiz_layout->addWidget(label);
// Patrons where possible layout->addLayout(horiz_layout);
layout->addWidget(new QLabel());
QString opening_statement; // Patrons where possible
layout->addWidget(new QLabel());
if (welcome_dialog || patrons.isEmpty()) { QString opening_statement;
opening_statement = tr("<b>Olive relies on support from the community to continue its development.</b>");
} else {
opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people.");
}
QLabel* support_lbl = new QLabel(tr("<html>%1 " if (welcome_dialog || patrons.isEmpty()) {
"If you like this project, please consider making a " opening_statement = tr(
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or " "<b>Olive relies on support from the community to continue its development.</b>");
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to " } else {
"support its development.</html>").arg(opening_statement)); opening_statement = tr(
support_lbl->setWordWrap(true); "Olive wouldn't be possible without the support of gracious donations from the following people.");
support_lbl->setAlignment(Qt::AlignCenter); }
support_lbl->setOpenExternalLinks(true);
layout->addWidget(support_lbl);
if (!patrons.isEmpty()) { QLabel *support_lbl = new QLabel(
ScrollingLabel* scroll = new ScrollingLabel(patrons); tr("<html>%1 "
scroll->StartAnimating(); "If you like this project, please consider making a "
layout->addWidget(scroll); "<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
} "<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
"support its development.</html>")
.arg(opening_statement));
support_lbl->setWordWrap(true);
support_lbl->setAlignment(Qt::AlignCenter);
support_lbl->setOpenExternalLinks(true);
layout->addWidget(support_lbl);
layout->addWidget(new QLabel()); if (!patrons.isEmpty()) {
ScrollingLabel *scroll = new ScrollingLabel(patrons);
scroll->StartAnimating();
layout->addWidget(scroll);
}
QHBoxLayout *btn_layout = new QHBoxLayout(); layout->addWidget(new QLabel());
btn_layout->setContentsMargins(0, 0, 0, 0);
btn_layout->setSpacing(0);
if (welcome_dialog) { QHBoxLayout *btn_layout = new QHBoxLayout();
dont_show_again_checkbox_ = new QCheckBox(tr("Don't show this message again")); btn_layout->setContentsMargins(0, 0, 0, 0);
btn_layout->addWidget(dont_show_again_checkbox_); btn_layout->setSpacing(0);
} else {
dont_show_again_checkbox_ = nullptr;
}
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); if (welcome_dialog) {
if (!welcome_dialog) { dont_show_again_checkbox_ =
buttons->setCenterButtons(true); new QCheckBox(tr("Don't show this message again"));
} btn_layout->addWidget(dont_show_again_checkbox_);
btn_layout->addWidget(buttons); } else {
dont_show_again_checkbox_ = nullptr;
}
layout->addLayout(btn_layout); QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok, this);
if (!welcome_dialog) {
buttons->setCenterButtons(true);
}
btn_layout->addWidget(buttons);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); layout->addLayout(btn_layout);
setFixedSize(sizeHint()); connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
setFixedSize(sizeHint());
} }
void AboutDialog::accept() void AboutDialog::accept()
{ {
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) { if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
OLIVE_CONFIG("ShowWelcomeDialog") = false; OLIVE_CONFIG("ShowWelcomeDialog") = false;
} }
QDialog::accept(); QDialog::accept();
} }
} }
+8 -9
View File
@@ -26,7 +26,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
/** /**
* @brief The AboutDialog class * @brief The AboutDialog class
@@ -34,11 +35,10 @@ namespace olive {
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from * The About dialog (accessible through Help > About). Contains license and version information. This can be run from
* anywhere * anywhere
*/ */
class AboutDialog : public QDialog class AboutDialog : public QDialog {
{ Q_OBJECT
Q_OBJECT
public: public:
/** /**
* @brief AboutDialog Constructor * @brief AboutDialog Constructor
* *
* Creates About dialog. * Creates About dialog.
@@ -47,14 +47,13 @@ public:
* *
* QWidget parent object. Usually this will be MainWindow. * QWidget parent object. Usually this will be MainWindow.
*/ */
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr); explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
public slots: public slots:
virtual void accept() override; virtual void accept() override;
private: private:
QCheckBox *dont_show_again_checkbox_; QCheckBox *dont_show_again_checkbox_;
}; };
} }
+58 -55
View File
@@ -24,98 +24,101 @@
#include "common/qtutils.h" #include "common/qtutils.h"
namespace olive { namespace olive
{
const int ScrollingLabel::kMinLineHeight = 10; const int ScrollingLabel::kMinLineHeight = 10;
ScrollingLabel::ScrollingLabel(QWidget *parent) : ScrollingLabel::ScrollingLabel(QWidget *parent)
QWidget(parent), : QWidget(parent)
animate_(0) , animate_(0)
{ {
timer_.setInterval(50); timer_.setInterval(50);
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate); connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
} }
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent) : ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent)
ScrollingLabel(parent) : ScrollingLabel(parent)
{ {
SetText(text); SetText(text);
} }
void ScrollingLabel::SetText(const QStringList &text) void ScrollingLabel::SetText(const QStringList &text)
{ {
text_ = text; text_ = text;
QFontMetrics fm = fontMetrics(); QFontMetrics fm = fontMetrics();
text_height_ = fm.height(); text_height_ = fm.height();
int width = 0; int width = 0;
foreach (const QString& s, text_) { foreach (const QString &s, text_) {
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s)); width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
} }
setMinimumSize(width, text_height_ * kMinLineHeight); setMinimumSize(width, text_height_ * kMinLineHeight);
} }
void ScrollingLabel::paintEvent(QPaintEvent *e) void ScrollingLabel::paintEvent(QPaintEvent *e)
{ {
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied); QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
map.fill(Qt::transparent); map.fill(Qt::transparent);
{ {
QPainter p(&map); QPainter p(&map);
p.setPen(palette().text().color()); p.setPen(palette().text().color());
QFontMetrics fm = p.fontMetrics(); QFontMetrics fm = p.fontMetrics();
int half_width = width(); int half_width = width();
for (int i=0; i<text_.size(); i++) { for (int i = 0; i < text_.size(); i++) {
int text_y = fm.ascent() + height() - animate_ + (fm.height()*i); int text_y = fm.ascent() + height() - animate_ + (fm.height() * i);
int text_bottom = text_y + fm.descent(); int text_bottom = text_y + fm.descent();
int text_top = text_y - fm.ascent(); int text_top = text_y - fm.ascent();
if (text_bottom < 0 || text_top >= height()) { if (text_bottom < 0 || text_top >= height()) {
continue; continue;
} }
const QString& s = text_.at(i); const QString &s = text_.at(i);
int width = QtUtils::QFontMetricsWidth(fm, s); int width = QtUtils::QFontMetricsWidth(fm, s);
p.drawText(half_width/2 - width/2, text_y, s); p.drawText(half_width / 2 - width / 2, text_y, s);
} }
for (int y=0; y<text_height_; y++) { for (int y = 0; y < text_height_; y++) {
double mul = double(y)/double(text_height_); double mul = double(y) / double(text_height_);
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul); SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
SetOpacityOfScanLine(map.scanLine(map.height()-1-y), map.width(), 4, mul); SetOpacityOfScanLine(map.scanLine(map.height() - 1 - y),
} map.width(), 4, mul);
} }
}
QPainter wp(this); QPainter wp(this);
wp.drawImage(0, 0, map); wp.drawImage(0, 0, map);
} }
void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width, int channels, double mul) void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width,
int channels, double mul)
{ {
for (int x=0; x<width; x++) { for (int x = 0; x < width; x++) {
uchar* pixel = &scan_line[x*4]; uchar *pixel = &scan_line[x * 4];
for (int c=0; c<channels; c++) { for (int c = 0; c < channels; c++) {
pixel[c] *= mul; pixel[c] *= mul;
} }
} }
} }
void ScrollingLabel::AnimationUpdate() void ScrollingLabel::AnimationUpdate()
{ {
animate_++; animate_++;
if (animate_ >= height() + text_.size() * text_height_) { if (animate_ >= height() + text_.size() * text_height_) {
animate_ = 0; animate_ = 0;
} }
update(); update();
} }
} }
+24 -24
View File
@@ -24,46 +24,46 @@
#include <QTimer> #include <QTimer>
#include <QWidget> #include <QWidget>
namespace olive { namespace olive
class ScrollingLabel : public QWidget
{ {
Q_OBJECT
class ScrollingLabel : public QWidget {
Q_OBJECT
public: public:
ScrollingLabel(QWidget* parent = nullptr); ScrollingLabel(QWidget *parent = nullptr);
ScrollingLabel(const QStringList& text, QWidget* parent = nullptr); ScrollingLabel(const QStringList &text, QWidget *parent = nullptr);
void SetText(const QStringList& text); void SetText(const QStringList &text);
void StartAnimating() void StartAnimating()
{ {
timer_.start(); timer_.start();
} }
void StopAnimating() void StopAnimating()
{ {
timer_.stop(); timer_.stop();
} }
protected: protected:
virtual void paintEvent(QPaintEvent* e) override; virtual void paintEvent(QPaintEvent *e) override;
private: private:
static void SetOpacityOfScanLine(uchar* scan_line, int width, int channels, double mul); static void SetOpacityOfScanLine(uchar *scan_line, int width, int channels,
double mul);
static const int kMinLineHeight; static const int kMinLineHeight;
QStringList text_; QStringList text_;
int text_height_; int text_height_;
QTimer timer_; QTimer timer_;
int animate_; int animate_;
private slots: private slots:
void AnimationUpdate(); void AnimationUpdate();
}; };
} }
+187 -183
View File
@@ -25,247 +25,251 @@
#include <QMenuBar> #include <QMenuBar>
#include <QVBoxLayout> #include <QVBoxLayout>
namespace olive { namespace olive
ActionSearch::ActionSearch(QWidget *parent) :
QDialog(parent),
menu_bar_(nullptr)
{ {
// ActionSearch requires a parent widget
Q_ASSERT(parent != nullptr);
// Set styling (object name is required for CSS specific to this object) ActionSearch::ActionSearch(QWidget *parent)
setObjectName("ASDiag"); : QDialog(parent)
setStyleSheet("#ASDiag{border: 2px solid #808080;}"); , menu_bar_(nullptr)
{
// ActionSearch requires a parent widget
Q_ASSERT(parent != nullptr);
// Size proportionally to the parent (usually MainWindow). // Set styling (object name is required for CSS specific to this object)
resize(parent->width()/3, parent->height()/3); setObjectName("ASDiag");
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it. // Size proportionally to the parent (usually MainWindow).
setWindowFlags(Qt::Popup); resize(parent->width() / 3, parent->height() / 3);
QVBoxLayout* layout = new QVBoxLayout(this); // Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
setWindowFlags(Qt::Popup);
// Construct the main entry text field. QVBoxLayout *layout = new QVBoxLayout(this);
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
// Set the main entry field font size to 1.2x its standard font size. // Construct the main entry text field.
QFont entry_field_font = entry_field->font(); ActionSearchEntry *entry_field = new ActionSearchEntry(this);
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
entry_field->setFont(entry_field_font);
// Set placeholder text for the main entry field // Set the main entry field font size to 1.2x its standard font size.
entry_field->setPlaceholderText(tr("Search for action...")); QFont entry_field_font = entry_field->font();
entry_field_font.setPointSize(qRound(entry_field_font.pointSize() * 1.2));
entry_field->setFont(entry_field_font);
// Connect signals/slots // Set placeholder text for the main entry field
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &))); entry_field->setPlaceholderText(tr("Search for action..."));
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field. // Connect signals/slots
// We override it here to select the upper or lower item in the list. connect(entry_field, SIGNAL(textChanged(const QString &)), this,
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up())); SLOT(search_update(const QString &)));
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down())); connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
layout->addWidget(entry_field);
// Construct list of actions // moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
list_widget = new ActionSearchList(this); // We override it here to select the upper or lower item in the list.
connect(entry_field, SIGNAL(moveSelectionUp()), this,
SLOT(move_selection_up()));
connect(entry_field, SIGNAL(moveSelectionDown()), this,
SLOT(move_selection_down()));
layout->addWidget(entry_field);
// Set list's font to 1.2x its standard font size // Construct list of actions
QFont list_widget_font = list_widget->font(); list_widget = new ActionSearchList(this);
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
list_widget->setFont(list_widget_font);
layout->addWidget(list_widget); // Set list's font to 1.2x its standard font size
QFont list_widget_font = list_widget->font();
list_widget_font.setPointSize(qRound(list_widget_font.pointSize() * 1.2));
list_widget->setFont(list_widget_font);
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action())); layout->addWidget(list_widget);
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
// shortcut for example).
entry_field->setFocus(); // Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
// shortcut for example).
entry_field->setFocus();
} }
void ActionSearch::SetMenuBar(QMenuBar *menu_bar) void ActionSearch::SetMenuBar(QMenuBar *menu_bar)
{ {
menu_bar_ = menu_bar; menu_bar_ = menu_bar;
} }
void ActionSearch::search_update(const QString &s, const QString &p, QMenu *parent) void ActionSearch::search_update(const QString &s, const QString &p,
QMenu *parent)
{ {
// Do nothing if there's no menu bar to work with // Do nothing if there's no menu bar to work with
if (menu_bar_ == nullptr) { if (menu_bar_ == nullptr) {
return; return;
} }
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two // This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent // modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any // referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
// submenus). // submenus).
if (parent == nullptr) { if (parent == nullptr) {
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
// (and their submenus).
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus // We'll clear all the current items in the list since if we're here, we're just starting.
// (and their submenus). list_widget->clear();
// We'll clear all the current items in the list since if we're here, we're just starting. QList<QAction *> menus = menu_bar_->actions();
list_widget->clear();
QList<QAction*> menus = menu_bar_->actions(); // Loop through all menus from the menubar and run this function on each one.
for (int i = 0; i < menus.size(); i++) {
QMenu *menu = menus.at(i)->menu();
// Loop through all menus from the menubar and run this function on each one. search_update(s, p, menu);
for (int i=0;i<menus.size();i++) { }
QMenu* menu = menus.at(i)->menu();
search_update(s, p, menu); // Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
} // keyboard-exclusive functionality.
if (list_widget->count() > 0) {
list_widget->item(0)->setSelected(true);
}
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better } else {
// keyboard-exclusive functionality. // Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
if (list_widget->count() > 0) {
list_widget->item(0)->setSelected(true);
}
} else { // The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
// adding the current menu's text to the existing hierarchy (passed in `p`).
QString menu_text;
if (!p.isEmpty())
menu_text += p + " > ";
menu_text += parent->title().replace(
"&", ""); // Strip out any &s used in menu action names
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`. // Loop over the menu's actions
QList<QAction *> actions = parent->actions();
for (int i = 0; i < actions.size(); i++) {
QAction *a = actions.at(i);
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by // Ignore separator actions
// adding the current menu's text to the existing hierarchy (passed in `p`). if (!a->isSeparator()) {
QString menu_text; if (a->menu() != nullptr) {
if (!p.isEmpty()) menu_text += p + " > "; // If the action is a menu, run this function recursively on it
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names search_update(s, menu_text, a->menu());
// Loop over the menu's actions } else {
QList<QAction*> actions = parent->actions(); // This is a valid non-separator non-menu action, so check it against the currently entered string.
for (int i=0;i<actions.size();i++) {
QAction* a = actions.at(i); // Strip out all &s from the action's name
QString comp = a->text().replace("&", "");
// Ignore separator actions // See if the action's name contains any of the currently entered string
if (!a->isSeparator()) { if (comp.contains(s, Qt::CaseInsensitive)) {
// If so, we add it to the list widget.
QListWidgetItem *item = new QListWidgetItem(
QStringLiteral("%1\n(%2)").arg(comp, menu_text),
list_widget);
if (a->menu() != nullptr) { // Add a pointer to the original QAction in the item's data
item->setData(Qt::UserRole + 1,
reinterpret_cast<quintptr>(a));
// If the action is a menu, run this function recursively on it list_widget->addItem(item);
search_update(s, menu_text, a->menu()); }
}
} else { }
}
// This is a valid non-separator non-menu action, so check it against the currently entered string. }
// Strip out all &s from the action's name
QString comp = a->text().replace("&", "");
// See if the action's name contains any of the currently entered string
if (comp.contains(s, Qt::CaseInsensitive)) {
// If so, we add it to the list widget.
QListWidgetItem* item = new QListWidgetItem(QStringLiteral("%1\n(%2)").arg(comp, menu_text), list_widget);
// Add a pointer to the original QAction in the item's data
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
list_widget->addItem(item);
}
}
}
}
}
} }
void ActionSearch::perform_action() { void ActionSearch::perform_action()
{
// Loop over all the items in the list and if we find one that's selected, we trigger it.
QList<QListWidgetItem *> selected_items = list_widget->selectedItems();
if (list_widget->count() > 0 && selected_items.size() > 0) {
QListWidgetItem *item = selected_items.at(0);
// Loop over all the items in the list and if we find one that's selected, we trigger it. // Get QAction pointer from item's data
QList<QListWidgetItem*> selected_items = list_widget->selectedItems(); QAction *a = reinterpret_cast<QAction *>(
if (list_widget->count() > 0 && selected_items.size() > 0) { item->data(Qt::UserRole + 1).value<quintptr>());
QListWidgetItem* item = selected_items.at(0); a->trigger();
}
// Get QAction pointer from item's data
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
a->trigger();
}
// Close this popup
accept();
// Close this popup
accept();
} }
void ActionSearch::move_selection_up() { void ActionSearch::move_selection_up()
{
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
// bottom item).
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start int lim = list_widget->count();
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very for (int i = 1; i < lim; i++) {
// bottom item). if (list_widget->item(i)->isSelected()) {
list_widget->item(i - 1)->setSelected(true);
int lim = list_widget->count(); list_widget->scrollToItem(list_widget->item(i - 1));
for (int i=1;i<lim;i++) { break;
if (list_widget->item(i)->isSelected()) { }
list_widget->item(i-1)->setSelected(true); }
list_widget->scrollToItem(list_widget->item(i-1));
break;
}
}
} }
void ActionSearch::move_selection_down() { void ActionSearch::move_selection_down()
{
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
// bottom item).
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it int lim = list_widget->count() - 1;
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very for (int i = 0; i < lim; i++) {
// bottom item). if (list_widget->item(i)->isSelected()) {
list_widget->item(i + 1)->setSelected(true);
int lim = list_widget->count()-1; list_widget->scrollToItem(list_widget->item(i + 1));
for (int i=0;i<lim;i++) { break;
if (list_widget->item(i)->isSelected()) { }
list_widget->item(i+1)->setSelected(true); }
list_widget->scrollToItem(list_widget->item(i+1));
break;
}
}
} }
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {} ActionSearchEntry::ActionSearchEntry(QWidget *parent)
: QLineEdit(parent)
{
}
bool ActionSearchEntry::event(QEvent *e) bool ActionSearchEntry::event(QEvent *e)
{ {
switch (e->type()) { switch (e->type()) {
case QEvent::ShortcutOverride: case QEvent::ShortcutOverride:
switch (static_cast<QKeyEvent*>(e)->key()) { switch (static_cast<QKeyEvent *>(e)->key()) {
case Qt::Key_Up: case Qt::Key_Up:
case Qt::Key_Down: case Qt::Key_Down:
e->accept(); e->accept();
return true; return true;
} }
break; break;
case QEvent::KeyPress: case QEvent::KeyPress:
// Listen for up/down, otherwise pass the key event to the base class. // Listen for up/down, otherwise pass the key event to the base class.
switch (static_cast<QKeyEvent*>(e)->key()) { switch (static_cast<QKeyEvent *>(e)->key()) {
case Qt::Key_Up: case Qt::Key_Up:
e->accept(); e->accept();
emit moveSelectionUp(); emit moveSelectionUp();
return true; return true;
case Qt::Key_Down: case Qt::Key_Down:
e->accept(); e->accept();
emit moveSelectionDown(); emit moveSelectionDown();
return true; return true;
} }
break; break;
default: default:
break; break;
} }
return QLineEdit::event(e); return QLineEdit::event(e);
} }
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {} ActionSearchList::ActionSearchList(QWidget *parent)
: QListWidget(parent)
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) { {
}
// Indiscriminately emit a signal on any double click
emit dbl_click();
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *)
{
// Indiscriminately emit a signal on any double click
emit dbl_click();
} }
} }
+40 -36
View File
@@ -29,7 +29,8 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
{
class ActionSearchList; class ActionSearchList;
@@ -39,11 +40,10 @@ class ActionSearchList;
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing * A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid. * rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
*/ */
class ActionSearch : public QDialog class ActionSearch : public QDialog {
{ Q_OBJECT
Q_OBJECT
public: public:
/** /**
* @brief ActionSearch Constructor * @brief ActionSearch Constructor
* *
* Create ActionSearch popup. * Create ActionSearch popup.
@@ -52,14 +52,14 @@ public:
* *
* QWidget parent. Usually MainWindow. * QWidget parent. Usually MainWindow.
*/ */
ActionSearch(QWidget* parent); ActionSearch(QWidget *parent);
/** /**
* @brief Set the menu bar to use in this action search * @brief Set the menu bar to use in this action search
*/ */
void SetMenuBar(QMenuBar* menu_bar); void SetMenuBar(QMenuBar *menu_bar);
private slots: private slots:
/** /**
* @brief Update the list of actions according to a search query * @brief Update the list of actions according to a search query
* *
* This function adds/removes actions in the action list according to a given search query entered by the user. * This function adds/removes actions in the action list according to a given search query entered by the user.
@@ -82,42 +82,44 @@ private slots:
* The current menu to loop over. In most cases, this should be left as nullptr when called externally. * The current menu to loop over. In most cases, this should be left as nullptr when called externally.
* search_update() will fill this automatically as it needs while calling itself recursively. * search_update() will fill this automatically as it needs while calling itself recursively.
*/ */
void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr); void search_update(const QString &s, const QString &p = nullptr,
QMenu *parent = nullptr);
/** /**
* @brief Perform the currently selected action * @brief Perform the currently selected action
* *
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently * Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is * highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
* triggered and the popup closes anyway. * triggered and the popup closes anyway.
*/ */
void perform_action(); void perform_action();
/** /**
* @brief Move selection up * @brief Move selection up
* *
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the * A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
* selection is already at the top of the list, this is a no-op. * selection is already at the top of the list, this is a no-op.
*/ */
void move_selection_up(); void move_selection_up();
/** /**
* @brief Move selection down * @brief Move selection down
* *
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the * A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
* selection is already at the bottom of the list, this is a no-op. * selection is already at the bottom of the list, this is a no-op.
*/ */
void move_selection_down(); void move_selection_down();
private: private:
/** /**
* @brief Main widget that shows the list of commands * @brief Main widget that shows the list of commands
*/ */
ActionSearchList* list_widget; ActionSearchList *list_widget;
/** /**
* @brief Attached menu bar object * @brief Attached menu bar object
*/ */
QMenuBar* menu_bar_; QMenuBar *menu_bar_;
}; };
/** /**
@@ -127,25 +129,26 @@ private:
* to a slot that triggers the currently selected action. * to a slot that triggers the currently selected action.
*/ */
class ActionSearchList : public QListWidget { class ActionSearchList : public QListWidget {
Q_OBJECT Q_OBJECT
public: public:
/** /**
* @brief ActionSearchList Constructor * @brief ActionSearchList Constructor
* @param parent * @param parent
* *
* Usually ActionSearch. * Usually ActionSearch.
*/ */
ActionSearchList(QWidget* parent); ActionSearchList(QWidget *parent);
protected: protected:
/** /**
* @brief Override of QListWidget's double click event that emits a signal. * @brief Override of QListWidget's double click event that emits a signal.
*/ */
void mouseDoubleClickEvent(QMouseEvent *); void mouseDoubleClickEvent(QMouseEvent *);
signals: signals:
/** /**
* @brief Signal emitted when a QListWidget item is double clicked. * @brief Signal emitted when a QListWidget item is double clicked.
*/ */
void dbl_click(); void dbl_click();
}; };
/** /**
@@ -155,32 +158,33 @@ signals:
* can connect them to moving the current selection up or down. * can connect them to moving the current selection up or down.
*/ */
class ActionSearchEntry : public QLineEdit { class ActionSearchEntry : public QLineEdit {
Q_OBJECT Q_OBJECT
public: public:
/** /**
* @brief ActionSearchEntry * @brief ActionSearchEntry
* @param parent * @param parent
* *
* Usually ActionSearch. * Usually ActionSearch.
*/ */
ActionSearchEntry(QWidget* parent); ActionSearchEntry(QWidget *parent);
protected: protected:
/** /**
* @brief Override of QLineEdit's key press event that listens for up/down key presses. * @brief Override of QLineEdit's key press event that listens for up/down key presses.
* @param event * @param event
*/ */
virtual bool event(QEvent *e) override; virtual bool event(QEvent *e) override;
signals: signals:
/** /**
* @brief Emitted when the user presses the up arrow key. * @brief Emitted when the user presses the up arrow key.
*/ */
void moveSelectionUp(); void moveSelectionUp();
/** /**
* @brief Emitted when the user presses the down arrow key. * @brief Emitted when the user presses the down arrow key.
*/ */
void moveSelectionDown(); void moveSelectionDown();
}; };
} }
+90 -71
View File
@@ -29,108 +29,127 @@
#include "core.h" #include "core.h"
namespace olive { namespace olive
{
#define super QDialog #define super QDialog
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message, const QStringList &recoveries, bool autocheck_latest, QWidget* parent) : AutoRecoveryDialog::AutoRecoveryDialog(const QString &message,
QDialog(parent) const QStringList &recoveries,
bool autocheck_latest, QWidget *parent)
: QDialog(parent)
{ {
Init(message); Init(message);
PopulateTree(recoveries, autocheck_latest); PopulateTree(recoveries, autocheck_latest);
} }
void AutoRecoveryDialog::accept() void AutoRecoveryDialog::accept()
{ {
foreach (QTreeWidgetItem* checkable, checkable_items_) { foreach (QTreeWidgetItem *checkable, checkable_items_) {
if (checkable->checkState(0) == Qt::Checked) { if (checkable->checkState(0) == Qt::Checked) {
QString filename = checkable->data(0, kFilenameRole).toString(); QString filename = checkable->data(0, kFilenameRole).toString();
Core::instance()->OpenRecoveryProject(filename); Core::instance()->OpenRecoveryProject(filename);
} }
} }
super::accept(); super::accept();
} }
void AutoRecoveryDialog::Init(const QString& header_text) void AutoRecoveryDialog::Init(const QString &header_text)
{ {
QVBoxLayout* layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
setWindowTitle(tr("Auto-Recovery")); setWindowTitle(tr("Auto-Recovery"));
layout->addWidget(new QLabel(header_text)); layout->addWidget(new QLabel(header_text));
tree_widget_ = new QTreeWidget(); tree_widget_ = new QTreeWidget();
tree_widget_->setHeaderHidden(true); tree_widget_->setHeaderHidden(true);
layout->addWidget(tree_widget_); layout->addWidget(tree_widget_);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttons =
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load")); new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &AutoRecoveryDialog::accept); buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
connect(buttons, &QDialogButtonBox::rejected, this, &AutoRecoveryDialog::reject); connect(buttons, &QDialogButtonBox::accepted, this,
layout->addWidget(buttons); &AutoRecoveryDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this,
&AutoRecoveryDialog::reject);
layout->addWidget(buttons);
} }
void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autocheck_latest) void AutoRecoveryDialog::PopulateTree(const QStringList &recoveries,
bool autocheck_latest)
{ {
// Each entry in `recoveries` is a directory with 1+ recovery projects in it // Each entry in `recoveries` is a directory with 1+ recovery projects in it
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot()); QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
foreach (const QString& recovery_folder, recoveries) { foreach (const QString &recovery_folder, recoveries) {
QDir recovery_dir(autorecovery_root.filePath(recovery_folder)); QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
QString pretty_name; QString pretty_name;
{ {
// Retrieve pretty name // Retrieve pretty name
QFile pretty_name_file(recovery_dir.filePath(QStringLiteral("realname.txt"))); QFile pretty_name_file(
if (pretty_name_file.open(QFile::ReadOnly)) { recovery_dir.filePath(QStringLiteral("realname.txt")));
// Read pretty name that we should have written in the autorecovery process if (pretty_name_file.open(QFile::ReadOnly)) {
pretty_name = QString::fromUtf8(pretty_name_file.readAll()); // Read pretty name that we should have written in the autorecovery process
pretty_name_file.close(); pretty_name = QString::fromUtf8(pretty_name_file.readAll());
} pretty_name_file.close();
}
if (pretty_name.isEmpty()) { if (pretty_name.isEmpty()) {
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing. // Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
pretty_name = recovery_dir.dirName(); pretty_name = recovery_dir.dirName();
} }
} }
QTreeWidgetItem* top_level = new QTreeWidgetItem(tree_widget_); QTreeWidgetItem *top_level = new QTreeWidgetItem(tree_widget_);
top_level->setText(0, pretty_name); top_level->setText(0, pretty_name);
{ {
// Populate with recoveries // Populate with recoveries
QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); QStringList entries =
for (int i=0; i<entries.size(); i++) { recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
const QString& entry = entries.at(i); QDir::Name | QDir::Reversed);
for (int i = 0; i < entries.size(); i++) {
const QString &entry = entries.at(i);
if (entry.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) { if (entry.endsWith(QStringLiteral(".ove"),
QTreeWidgetItem* entry_item = new QTreeWidgetItem(top_level); Qt::CaseInsensitive)) {
QTreeWidgetItem *entry_item =
new QTreeWidgetItem(top_level);
bool ok; bool ok;
qint64 recovery_time = entry.left(entry.indexOf('.')).toLongLong(&ok); qint64 recovery_time =
QString entry_name; entry.left(entry.indexOf('.')).toLongLong(&ok);
QString entry_name;
if (ok) { if (ok) {
// Set as time/date of recovery // Set as time/date of recovery
entry_name = QDateTime::fromSecsSinceEpoch(recovery_time).toString(); entry_name =
} else { QDateTime::fromSecsSinceEpoch(recovery_time)
// Fallback if we couldn't discern a date from this .toString();
entry_name = entry; } else {
} // Fallback if we couldn't discern a date from this
entry_name = entry;
}
entry_item->setText(0, entry_name); entry_item->setText(0, entry_name);
entry_item->setData(0, kFilenameRole, recovery_dir.filePath(entry)); entry_item->setData(0, kFilenameRole,
recovery_dir.filePath(entry));
// Allow to be checked, auto-checking the first entry // Allow to be checked, auto-checking the first entry
entry_item->setCheckState(0, (autocheck_latest && top_level->childCount() == 1) ? Qt::Checked : Qt::Unchecked); entry_item->setCheckState(
0, (autocheck_latest && top_level->childCount() == 1) ?
Qt::Checked :
Qt::Unchecked);
checkable_items_.append(entry_item); checkable_items_.append(entry_item);
} }
} }
} }
} }
} }
} }
+12 -14
View File
@@ -26,30 +26,28 @@
#include "common/define.h" #include "common/define.h"
namespace olive { namespace olive
class AutoRecoveryDialog : public QDialog
{ {
Q_OBJECT
class AutoRecoveryDialog : public QDialog {
Q_OBJECT
public: public:
AutoRecoveryDialog(const QString& message, const QStringList& recoveries, bool autocheck_latest, QWidget* parent); AutoRecoveryDialog(const QString &message, const QStringList &recoveries,
bool autocheck_latest, QWidget *parent);
public slots: public slots:
virtual void accept() override; virtual void accept() override;
private: private:
void Init(const QString &header_text); void Init(const QString &header_text);
void PopulateTree(const QStringList &recoveries, bool autocheck); void PopulateTree(const QStringList &recoveries, bool autocheck);
QTreeWidget* tree_widget_; QTreeWidget *tree_widget_;
QVector<QTreeWidgetItem*> checkable_items_; QVector<QTreeWidgetItem *> checkable_items_;
enum DataRole {
kFilenameRole = Qt::UserRole
};
enum DataRole { kFilenameRole = Qt::UserRole };
}; };
} }
+120 -101
View File
@@ -26,167 +26,186 @@
#include "common/qtutils.h" #include "common/qtutils.h"
namespace olive { namespace olive
ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start, QWidget *parent) :
QDialog(parent),
color_manager_(color_manager)
{ {
setWindowTitle(tr("Select Color"));
QVBoxLayout* layout = new QVBoxLayout(this); ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
QWidget *parent)
: QDialog(parent)
, color_manager_(color_manager)
{
setWindowTitle(tr("Select Color"));
QSplitter* splitter = new QSplitter(Qt::Horizontal); QVBoxLayout *layout = new QVBoxLayout(this);
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
QWidget* graphics_area = new QWidget(); QSplitter *splitter = new QSplitter(Qt::Horizontal);
splitter->addWidget(graphics_area); splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area); QWidget *graphics_area = new QWidget();
splitter->addWidget(graphics_area);
QHBoxLayout* wheel_layout = new QHBoxLayout(); QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
graphics_layout->addLayout(wheel_layout);
color_wheel_ = new ColorWheelWidget(); QHBoxLayout *wheel_layout = new QHBoxLayout();
wheel_layout->addWidget(color_wheel_); graphics_layout->addLayout(wheel_layout);
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical); color_wheel_ = new ColorWheelWidget();
hsv_value_gradient_->setFixedWidth(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH"))); wheel_layout->addWidget(color_wheel_);
wheel_layout->addWidget(hsv_value_gradient_);
QHBoxLayout *swatch_layout = new QHBoxLayout(); hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
graphics_layout->addLayout(swatch_layout); hsv_value_gradient_->setFixedWidth(
QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
wheel_layout->addWidget(hsv_value_gradient_);
swatch_layout->addStretch(); QHBoxLayout *swatch_layout = new QHBoxLayout();
graphics_layout->addLayout(swatch_layout);
swatch_ = new ColorSwatchChooser(color_manager_); swatch_layout->addStretch();
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
swatch_layout->addWidget(swatch_);
swatch_layout->addStretch(); swatch_ = new ColorSwatchChooser(color_manager_);
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
swatch_layout->addWidget(swatch_);
QWidget* value_area = new QWidget(); swatch_layout->addStretch();
QVBoxLayout* value_layout = new QVBoxLayout(value_area);
value_layout->setSpacing(0);
splitter->addWidget(value_area);
color_values_widget_ = new ColorValuesWidget(color_manager_); QWidget *value_area = new QWidget();
color_values_widget_->IgnorePickFrom(this); QVBoxLayout *value_layout = new QVBoxLayout(value_area);
value_layout->addWidget(color_values_widget_); value_layout->setSpacing(0);
splitter->addWidget(value_area);
chooser_ = new ColorSpaceChooser(color_manager_); color_values_widget_ = new ColorValuesWidget(color_manager_);
color_values_widget_->IgnorePickFrom(this);
value_layout->addWidget(color_values_widget_);
value_layout->addWidget(chooser_); chooser_ = new ColorSpaceChooser(color_manager_);
// Split window 50/50 value_layout->addWidget(chooser_);
splitter->setSizes({INT_MAX, INT_MAX});
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor); // Split window 50/50
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor); splitter->setSizes({ INT_MAX, INT_MAX });
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_, &ColorValuesWidget::SetColor);
connect(color_wheel_, &ColorWheelWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientWidget::setFixedHeight); connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
color_values_widget_, &ColorValuesWidget::SetColor);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_,
&ColorSwatchChooser::SetCurrentColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
color_values_widget_, &ColorValuesWidget::SetColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_,
&ColorSwatchChooser::SetCurrentColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_,
&ColorGradientWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_,
&ColorWheelWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_,
&ColorValuesWidget::SetColor);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(color_wheel_, &ColorWheelWidget::DiameterChanged,
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
layout->addWidget(buttons);
SetColor(start); QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
layout->addWidget(buttons);
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged); SetColor(start);
ColorSpaceChanged(chooser_->input(), chooser_->output());
// Set default size ratio to 2:1 connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this,
resize(sizeHint().height() * 2, sizeHint().height()); &ColorDialog::ColorSpaceChanged);
ColorSpaceChanged(chooser_->input(), chooser_->output());
// Set default size ratio to 2:1
resize(sizeHint().height() * 2, sizeHint().height());
} }
void ColorDialog::SetColor(const ManagedColor &start) void ColorDialog::SetColor(const ManagedColor &start)
{ {
chooser_->set_input(start.color_input()); chooser_->set_input(start.color_input());
chooser_->set_output(start.color_output()); chooser_->set_output(start.color_output());
Color managed_start; Color managed_start;
if (start.color_input().isEmpty()) { if (start.color_input().isEmpty()) {
managed_start = start;
managed_start = start; } else {
// Convert reference color to the input space
ColorProcessorPtr linear_to_input = ColorProcessor::Create(
color_manager_, color_manager_->GetReferenceColorSpace(),
start.color_input());
} else { managed_start = linear_to_input->ConvertColor(start);
}
// Convert reference color to the input space color_wheel_->SetSelectedColor(managed_start);
ColorProcessorPtr linear_to_input = ColorProcessor::Create(color_manager_, hsv_value_gradient_->SetSelectedColor(managed_start);
color_manager_->GetReferenceColorSpace(), color_values_widget_->SetColor(managed_start);
start.color_input()); swatch_->SetCurrentColor(managed_start);
managed_start = linear_to_input->ConvertColor(start);
}
color_wheel_->SetSelectedColor(managed_start);
hsv_value_gradient_->SetSelectedColor(managed_start);
color_values_widget_->SetColor(managed_start);
swatch_->SetCurrentColor(managed_start);
} }
ManagedColor ColorDialog::GetSelectedColor() const ManagedColor ColorDialog::GetSelectedColor() const
{ {
ManagedColor selected = color_wheel_->GetSelectedColor(); ManagedColor selected = color_wheel_->GetSelectedColor();
// Convert to linear and return a linear color // Convert to linear and return a linear color
if (input_to_ref_processor_) { if (input_to_ref_processor_) {
selected = input_to_ref_processor_->ConvertColor(selected); selected = input_to_ref_processor_->ConvertColor(selected);
} }
selected.set_color_input(GetColorSpaceInput()); selected.set_color_input(GetColorSpaceInput());
selected.set_color_output(GetColorSpaceOutput()); selected.set_color_output(GetColorSpaceOutput());
return selected; return selected;
} }
QString ColorDialog::GetColorSpaceInput() const QString ColorDialog::GetColorSpaceInput() const
{ {
return chooser_->input(); return chooser_->input();
} }
ColorTransform ColorDialog::GetColorSpaceOutput() const ColorTransform ColorDialog::GetColorSpaceOutput() const
{ {
return chooser_->output(); return chooser_->output();
} }
void ColorDialog::ColorSpaceChanged(const QString &input, const ColorTransform &output) void ColorDialog::ColorSpaceChanged(const QString &input,
const ColorTransform &output)
{ {
input_to_ref_processor_ = ColorProcessor::Create(color_manager_, input, color_manager_->GetReferenceColorSpace()); input_to_ref_processor_ = ColorProcessor::Create(
color_manager_, input, color_manager_->GetReferenceColorSpace());
ColorProcessorPtr ref_to_display = ColorProcessor::Create(color_manager_, ColorProcessorPtr ref_to_display = ColorProcessor::Create(
color_manager_->GetReferenceColorSpace(), color_manager_, color_manager_->GetReferenceColorSpace(), output);
output);
ColorProcessorPtr ref_to_input = ColorProcessor::Create(color_manager_, color_manager_->GetReferenceColorSpace(), input); ColorProcessorPtr ref_to_input = ColorProcessor::Create(
color_manager_, color_manager_->GetReferenceColorSpace(), input);
// FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to // FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to
// crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget) // crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget)
/*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(), /*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(),
color_manager_->GetReferenceColorSpace(), color_manager_->GetReferenceColorSpace(),
display, display,
view, view,
look, look,
ColorProcessor::kInverse);*/ ColorProcessor::kInverse);*/
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display); color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_, ref_to_display); hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_,
color_values_widget_->SetColorProcessor(input_to_ref_processor_, ref_to_display, nullptr, ref_to_input); ref_to_display);
color_values_widget_->SetColorProcessor(
input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
} }
} }
+21 -20
View File
@@ -31,13 +31,13 @@
#include "widget/colorwheel/colorvalueswidget.h" #include "widget/colorwheel/colorvalueswidget.h"
#include "widget/colorwheel/colorwheelwidget.h" #include "widget/colorwheel/colorwheelwidget.h"
namespace olive { namespace olive
class ColorDialog : public QDialog
{ {
Q_OBJECT
class ColorDialog : public QDialog {
Q_OBJECT
public: public:
/** /**
* @brief ColorDialog Constructor * @brief ColorDialog Constructor
* *
* @param color_manager * @param color_manager
@@ -56,40 +56,41 @@ public:
* *
* QWidget parent. * QWidget parent.
*/ */
ColorDialog(ColorManager* color_manager, const ManagedColor &start = Color(1.0f, 1.0f, 1.0f), QWidget* parent = nullptr); ColorDialog(ColorManager *color_manager,
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
QWidget *parent = nullptr);
/** /**
* @brief Retrieves the color selected by the user * @brief Retrieves the color selected by the user
* *
* The color is always returned in the ColorManager's reference space (usually scene linear). * The color is always returned in the ColorManager's reference space (usually scene linear).
*/ */
ManagedColor GetSelectedColor() const; ManagedColor GetSelectedColor() const;
QString GetColorSpaceInput() const; QString GetColorSpaceInput() const;
ColorTransform GetColorSpaceOutput() const; ColorTransform GetColorSpaceOutput() const;
public slots: public slots:
void SetColor(const ManagedColor &c); void SetColor(const ManagedColor &c);
private: private:
ColorManager* color_manager_; ColorManager *color_manager_;
ColorWheelWidget* color_wheel_; ColorWheelWidget *color_wheel_;
ColorValuesWidget* color_values_widget_; ColorValuesWidget *color_values_widget_;
ColorGradientWidget* hsv_value_gradient_; ColorGradientWidget *hsv_value_gradient_;
ColorProcessorPtr input_to_ref_processor_; ColorProcessorPtr input_to_ref_processor_;
ColorSpaceChooser* chooser_; ColorSpaceChooser *chooser_;
ColorSwatchChooser *swatch_; ColorSwatchChooser *swatch_;
private slots: private slots:
void ColorSpaceChanged(const QString& input, const ColorTransform &output); void ColorSpaceChanged(const QString &input, const ColorTransform &output);
}; };
} }
+39 -37
View File
@@ -26,66 +26,68 @@
#include "core.h" #include "core.h"
namespace olive { namespace olive
ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
QDialog(parent)
{ {
QVBoxLayout* layout = new QVBoxLayout(this);
QSplitter* splitter = new QSplitter(); ConfigDialogBase::ConfigDialogBase(QWidget *parent)
splitter->setChildrenCollapsible(false); : QDialog(parent)
layout->addWidget(splitter); {
QVBoxLayout *layout = new QVBoxLayout(this);
list_widget_ = new QListWidget(); QSplitter *splitter = new QSplitter();
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
preference_pane_stack_ = new QStackedWidget(this); list_widget_ = new QListWidget();
splitter->addWidget(list_widget_); preference_pane_stack_ = new QStackedWidget(this);
splitter->addWidget(preference_pane_stack_);
QDialogButtonBox* button_box = new QDialogButtonBox(this); splitter->addWidget(list_widget_);
button_box->setOrientation(Qt::Horizontal); splitter->addWidget(preference_pane_stack_);
button_box->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
layout->addWidget(button_box); QDialogButtonBox *button_box = new QDialogButtonBox(this);
button_box->setOrientation(Qt::Horizontal);
button_box->setStandardButtons(QDialogButtonBox::Cancel |
QDialogButtonBox::Ok);
connect(button_box, &QDialogButtonBox::accepted, this, &ConfigDialogBase::accept); layout->addWidget(button_box);
connect(button_box, &QDialogButtonBox::rejected, this, &ConfigDialogBase::reject);
connect(list_widget_, connect(button_box, &QDialogButtonBox::accepted, this,
&QListWidget::currentRowChanged, &ConfigDialogBase::accept);
preference_pane_stack_, connect(button_box, &QDialogButtonBox::rejected, this,
&QStackedWidget::setCurrentIndex); &ConfigDialogBase::reject);
connect(list_widget_, &QListWidget::currentRowChanged,
preference_pane_stack_, &QStackedWidget::setCurrentIndex);
} }
void ConfigDialogBase::accept() void ConfigDialogBase::accept()
{ {
foreach (ConfigDialogBaseTab* tab, tabs_) { foreach (ConfigDialogBaseTab *tab, tabs_) {
if (!tab->Validate()) { if (!tab->Validate()) {
return; return;
} }
} }
MultiUndoCommand* command = new MultiUndoCommand(); MultiUndoCommand *command = new MultiUndoCommand();
foreach (ConfigDialogBaseTab* tab, tabs_) { foreach (ConfigDialogBaseTab *tab, tabs_) {
tab->Accept(command); tab->Accept(command);
} }
Core::instance()->undo_stack()->push(command, tr("Set Configuration")); Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
AcceptEvent(); AcceptEvent();
QDialog::accept(); QDialog::accept();
} }
void ConfigDialogBase::AddTab(ConfigDialogBaseTab *tab, const QString &title) void ConfigDialogBase::AddTab(ConfigDialogBaseTab *tab, const QString &title)
{ {
list_widget_->addItem(title); list_widget_->addItem(title);
preference_pane_stack_->addWidget(tab); preference_pane_stack_->addWidget(tab);
tabs_.append(tab); tabs_.append(tab);
} }
} }
+14 -13
View File
@@ -27,32 +27,33 @@
#include "configdialogbasetab.h" #include "configdialogbasetab.h"
namespace olive { namespace olive
class ConfigDialogBase : public QDialog
{ {
Q_OBJECT
class ConfigDialogBase : public QDialog {
Q_OBJECT
public: public:
ConfigDialogBase(QWidget* parent = nullptr); ConfigDialogBase(QWidget *parent = nullptr);
private slots: private slots:
/** /**
* @brief Override of accept to save preferences to Config. * @brief Override of accept to save preferences to Config.
*/ */
virtual void accept() override; virtual void accept() override;
protected: protected:
void AddTab(ConfigDialogBaseTab* tab, const QString& title); void AddTab(ConfigDialogBaseTab *tab, const QString &title);
virtual void AcceptEvent(){} virtual void AcceptEvent()
{
}
private: private:
QListWidget* list_widget_; QListWidget *list_widget_;
QStackedWidget* preference_pane_stack_; QStackedWidget *preference_pane_stack_;
QList<ConfigDialogBaseTab*> tabs_;
QList<ConfigDialogBaseTab *> tabs_;
}; };
} }
@@ -20,11 +20,12 @@
#include "configdialogbasetab.h" #include "configdialogbasetab.h"
namespace olive { namespace olive
{
bool ConfigDialogBaseTab::Validate() bool ConfigDialogBaseTab::Validate()
{ {
return true; return true;
} }
} }
+6 -6
View File
@@ -26,16 +26,16 @@
#include "config/config.h" #include "config/config.h"
#include "undo/undocommand.h" #include "undo/undocommand.h"
namespace olive { namespace olive
class ConfigDialogBaseTab : public QWidget
{ {
class ConfigDialogBaseTab : public QWidget {
public: public:
ConfigDialogBaseTab() = default; ConfigDialogBaseTab() = default;
virtual bool Validate(); virtual bool Validate();
virtual void Accept(MultiUndoCommand *parent) = 0; virtual void Accept(MultiUndoCommand *parent) = 0;
}; };
} }
+69 -54
View File
@@ -28,86 +28,101 @@
#include "config/config.h" #include "config/config.h"
#include "core.h" #include "core.h"
namespace olive { namespace olive
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) :
QDialog(parent),
folder_(folder)
{ {
QGridLayout* layout = new QGridLayout(this);
int row = 0; DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
: QDialog(parent)
, folder_(folder)
{
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())), row, 0, 1, 2); int row = 0;
setWindowTitle(tr("Disk Cache Settings"));
row++; layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())),
row, 0, 1, 2);
setWindowTitle(tr("Disk Cache Settings"));
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0); row++;
maximum_cache_slider_ = new FloatSlider(); layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
maximum_cache_slider_->SetFormat(tr("%1 GB"));
maximum_cache_slider_->SetMinimum(1.0);
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) / static_cast<double>(kBytesInGigabyte));
layout->addWidget(maximum_cache_slider_, row, 1);
row++; maximum_cache_slider_ = new FloatSlider();
maximum_cache_slider_->SetFormat(tr("%1 GB"));
maximum_cache_slider_->SetMinimum(1.0);
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) /
static_cast<double>(kBytesInGigabyte));
layout->addWidget(maximum_cache_slider_, row, 1);
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache")); row++;
connect(clear_cache_btn_, &QPushButton::clicked, this, static_cast<void(DiskCacheDialog::*)()>(&DiskCacheDialog::ClearDiskCache));
layout->addWidget(clear_cache_btn_, row, 1);
row++; clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
connect(clear_cache_btn_, &QPushButton::clicked, this,
static_cast<void (DiskCacheDialog::*)()>(
&DiskCacheDialog::ClearDiskCache));
layout->addWidget(clear_cache_btn_, row, 1);
clear_disk_cache_ = new QCheckBox(tr("Automatically clear disk cache on close")); row++;
clear_disk_cache_->setChecked(folder->GetClearOnClose());
layout->addWidget(clear_disk_cache_, row, 1);
row++; clear_disk_cache_ =
new QCheckBox(tr("Automatically clear disk cache on close"));
clear_disk_cache_->setChecked(folder->GetClearOnClose());
layout->addWidget(clear_disk_cache_, row, 1);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); row++;
connect(buttons, &QDialogButtonBox::accepted, this, &DiskCacheDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &DiskCacheDialog::reject); QDialogButtonBox *buttons =
layout->addWidget(buttons, row, 0, 1, 2); new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this,
&DiskCacheDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this,
&DiskCacheDialog::reject);
layout->addWidget(buttons, row, 0, 1, 2);
} }
void DiskCacheDialog::accept() void DiskCacheDialog::accept()
{ {
qint64 new_disk_cache_limit = qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte); qint64 new_disk_cache_limit =
if (new_disk_cache_limit != folder_->GetLimit()) { qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
folder_->SetLimit(new_disk_cache_limit); if (new_disk_cache_limit != folder_->GetLimit()) {
} folder_->SetLimit(new_disk_cache_limit);
}
if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) { if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) {
folder_->SetClearOnClose(clear_disk_cache_->isChecked()); folder_->SetClearOnClose(clear_disk_cache_->isChecked());
} }
QDialog::accept(); QDialog::accept();
} }
void DiskCacheDialog::ClearDiskCache() void DiskCacheDialog::ClearDiskCache()
{ {
ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_); ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_);
} }
void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn) void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent,
QPushButton *clear_btn)
{ {
if (QMessageBox::question(parent, if (QMessageBox::question(
tr("Clear Disk Cache"), parent, tr("Clear Disk Cache"),
tr("Are you sure you want to clear the disk cache in '%1'?").arg(path), tr("Are you sure you want to clear the disk cache in '%1'?")
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { .arg(path),
if (clear_btn) clear_btn->setEnabled(false); QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (clear_btn)
clear_btn->setEnabled(false);
if (DiskManager::instance()->ClearDiskCache(path)) { if (DiskManager::instance()->ClearDiskCache(path)) {
if (clear_btn) clear_btn->setText(tr("Disk Cache Cleared")); if (clear_btn)
} else { clear_btn->setText(tr("Disk Cache Cleared"));
QMessageBox::information(parent, } else {
tr("Clear Disk Cache"), QMessageBox::information(
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."), parent, tr("Clear Disk Cache"),
QMessageBox::Ok); tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
if (clear_btn) clear_btn->setText(tr("Disk Cache Partially Cleared")); QMessageBox::Ok);
} if (clear_btn)
} clear_btn->setText(tr("Disk Cache Partially Cleared"));
}
}
} }
} }
+14 -14
View File
@@ -28,31 +28,31 @@
#include "render/diskmanager.h" #include "render/diskmanager.h"
#include "widget/slider/floatslider.h" #include "widget/slider/floatslider.h"
namespace olive { namespace olive
class DiskCacheDialog : public QDialog
{ {
Q_OBJECT
public:
DiskCacheDialog(DiskCacheFolder* folder, QWidget* parent = nullptr);
static void ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn = nullptr); class DiskCacheDialog : public QDialog {
Q_OBJECT
public:
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
static void ClearDiskCache(const QString &path, QWidget *parent,
QPushButton *clear_btn = nullptr);
public slots: public slots:
virtual void accept() override; virtual void accept() override;
private: private:
DiskCacheFolder* folder_; DiskCacheFolder *folder_;
FloatSlider* maximum_cache_slider_; FloatSlider *maximum_cache_slider_;
QCheckBox* clear_disk_cache_; QCheckBox *clear_disk_cache_;
QPushButton* clear_cache_btn_; QPushButton *clear_cache_btn_;
private slots: private slots:
void ClearDiskCache(); void ClearDiskCache();
}; };
} }
+70 -64
View File
@@ -28,105 +28,111 @@
#include "common/qtutils.h" #include "common/qtutils.h"
#include "widget/slider/integerslider.h" #include "widget/slider/integerslider.h"
namespace olive { namespace olive
{
AV1Section::AV1Section(QWidget *parent) : AV1Section::AV1Section(QWidget *parent)
AV1Section(AV1CRFSection::kDefaultAV1CRF, parent) : AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
{ {
} }
AV1Section::AV1Section(int default_crf, QWidget *parent) : AV1Section::AV1Section(int default_crf, QWidget *parent)
CodecSection(parent) : CodecSection(parent)
{ {
QGridLayout* layout = new QGridLayout(this); QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
int row = 0; int row = 0;
layout->addWidget(new QLabel(tr("Preset:")), row, 0); layout->addWidget(new QLabel(tr("Preset:")), row, 0);
preset_combobox_ = new QComboBox(); preset_combobox_ = new QComboBox();
preset_combobox_->setToolTip(tr("This parameter governs the efficiency/encode-time trade-off.\n" preset_combobox_->setToolTip(tr(
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n" "This parameter governs the efficiency/encode-time trade-off.\n"
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value.")); "Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
for (int i = 0; i <= 13; i++) for (int i = 0; i <= 13; i++)
preset_combobox_->addItem(QString::number(i)); preset_combobox_->addItem(QString::number(i));
preset_combobox_->setCurrentIndex(8); preset_combobox_->setCurrentIndex(8);
layout->addWidget(preset_combobox_, row, 1); layout->addWidget(preset_combobox_, row, 1);
row++; row++;
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0); layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
QComboBox* compression_box = new QComboBox(); QComboBox *compression_box = new QComboBox();
compression_box->setToolTip(tr("This parameter governs the quality/size trade-off.\n" compression_box->setToolTip(tr(
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n" "This parameter governs the quality/size trade-off.\n"
"Lower CRF values retain more detail at the cost of larger file sizes.\n" "Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
"The possible range of CRF in SVT-AV1 is 1-63.")); "Lower CRF values retain more detail at the cost of larger file sizes.\n"
"The possible range of CRF in SVT-AV1 is 1-63."));
// These items must correspond to the CompressionMethod enum // These items must correspond to the CompressionMethod enum
compression_box->addItem(tr("Constant Rate Factor")); compression_box->addItem(tr("Constant Rate Factor"));
layout->addWidget(compression_box, row, 1); layout->addWidget(compression_box, row, 1);
row++; row++;
compression_method_stack_ = new QStackedWidget(); compression_method_stack_ = new QStackedWidget();
layout->addWidget(compression_method_stack_, row, 0, 1, 2); layout->addWidget(compression_method_stack_, row, 0, 1, 2);
crf_section_ = new AV1CRFSection(default_crf); crf_section_ = new AV1CRFSection(default_crf);
compression_method_stack_->addWidget(crf_section_); compression_method_stack_->addWidget(crf_section_);
connect(compression_box, connect(
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), compression_box,
compression_method_stack_, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
&QStackedWidget::setCurrentIndex); compression_method_stack_, &QStackedWidget::setCurrentIndex);
} }
void AV1Section::AddOpts(EncodingParams *params) void AV1Section::AddOpts(EncodingParams *params)
{ {
CompressionMethod method = static_cast<CompressionMethod>(
compression_method_stack_->currentIndex());
CompressionMethod method = static_cast<CompressionMethod>(compression_method_stack_->currentIndex()); if (method == kConstantRateFactor) {
// Set Quantizer value
params->set_video_option(QStringLiteral("qp"),
QString::number(crf_section_->GetValue()));
}
if (method == kConstantRateFactor) { params->set_video_option(QStringLiteral("preset"),
QString::number(preset_combobox_->currentIndex()));
// Set Quantizer value
params->set_video_option(QStringLiteral("qp"), QString::number(crf_section_->GetValue()));
}
params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex()));
} }
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) : AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
QWidget(parent) : QWidget(parent)
{ {
QHBoxLayout* layout = new QHBoxLayout(this); QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
crf_slider_ = new QSlider(Qt::Horizontal); crf_slider_ = new QSlider(Qt::Horizontal);
crf_slider_->setMinimum(kMinimumCRF); crf_slider_->setMinimum(kMinimumCRF);
crf_slider_->setMaximum(kMaximumCRF); crf_slider_->setMaximum(kMaximumCRF);
crf_slider_->setValue(default_crf); crf_slider_->setValue(default_crf);
layout->addWidget(crf_slider_); layout->addWidget(crf_slider_);
IntegerSlider* crf_input = new IntegerSlider(); IntegerSlider *crf_input = new IntegerSlider();
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH"))); crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(
crf_input->SetMinimum(kMinimumCRF); crf_input->fontMetrics(), QStringLiteral("HHHH")));
crf_input->SetMaximum(kMaximumCRF); crf_input->SetMinimum(kMinimumCRF);
crf_input->SetValue(default_crf); crf_input->SetMaximum(kMaximumCRF);
crf_input->SetDefaultValue(default_crf); crf_input->SetValue(default_crf);
layout->addWidget(crf_input); crf_input->SetDefaultValue(default_crf);
layout->addWidget(crf_input);
connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue); connect(crf_slider_, &QSlider::valueChanged, crf_input,
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_, &QSlider::setValue); &IntegerSlider::SetValue);
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_,
&QSlider::setValue);
} }
int AV1CRFSection::GetValue() const int AV1CRFSection::GetValue() const
{ {
return crf_slider_->value(); return crf_slider_->value();
} }
} }
+21 -23
View File
@@ -28,45 +28,43 @@
#include "codecsection.h" #include "codecsection.h"
#include "widget/slider/floatslider.h" #include "widget/slider/floatslider.h"
namespace olive { namespace olive
class AV1CRFSection : public QWidget
{ {
Q_OBJECT
class AV1CRFSection : public QWidget {
Q_OBJECT
public: public:
AV1CRFSection(int default_crf, QWidget* parent = nullptr); AV1CRFSection(int default_crf, QWidget *parent = nullptr);
int GetValue() const; int GetValue() const;
static const int kDefaultAV1CRF = 30; static const int kDefaultAV1CRF = 30;
private: private:
static const int kMinimumCRF = 0; static const int kMinimumCRF = 0;
static const int kMaximumCRF = 63; static const int kMaximumCRF = 63;
QSlider* crf_slider_;
QSlider *crf_slider_;
}; };
class AV1Section : public CodecSection class AV1Section : public CodecSection {
{ Q_OBJECT
Q_OBJECT
public: public:
enum CompressionMethod { enum CompressionMethod {
kConstantRateFactor, kConstantRateFactor,
}; };
AV1Section(QWidget* parent = nullptr); AV1Section(QWidget *parent = nullptr);
AV1Section(int default_crf, QWidget* parent); AV1Section(int default_crf, QWidget *parent);
virtual void AddOpts(EncodingParams* params) override; virtual void AddOpts(EncodingParams *params) override;
private: private:
QStackedWidget* compression_method_stack_; QStackedWidget *compression_method_stack_;
AV1CRFSection* crf_section_; AV1CRFSection *crf_section_;
QComboBox *preset_combobox_; QComboBox *preset_combobox_;
}; };
} }
+32 -28
View File
@@ -23,22 +23,23 @@
#include <QGridLayout> #include <QGridLayout>
#include <QLabel> #include <QLabel>
namespace olive { namespace olive
CineformSection::CineformSection(QWidget *parent) :
CodecSection(parent)
{ {
QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0); CineformSection::CineformSection(QWidget *parent)
: CodecSection(parent)
{
QGridLayout *layout = new QGridLayout(this);
int row = 0; layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(new QLabel(tr("Quality:")), row, 0); int row = 0;
quality_combobox_ = new QComboBox(); layout->addWidget(new QLabel(tr("Quality:")), row, 0);
/* Correspond to the following indexes for FFmpeg quality_combobox_ = new QComboBox();
/* Correspond to the following indexes for FFmpeg
* *
* -quality <int> E..V....... set quality (from 0 to 12) (default film3+) * -quality <int> E..V....... set quality (from 0 to 12) (default film3+)
* film3+ 0 E..V....... * film3+ 0 E..V.......
@@ -57,34 +58,37 @@ CineformSection::CineformSection(QWidget *parent) :
* *
*/ */
quality_combobox_->addItem(tr("Film Scan 3+")); quality_combobox_->addItem(tr("Film Scan 3+"));
quality_combobox_->addItem(tr("Film Scan 3")); quality_combobox_->addItem(tr("Film Scan 3"));
quality_combobox_->addItem(tr("Film Scan 2+")); quality_combobox_->addItem(tr("Film Scan 2+"));
quality_combobox_->addItem(tr("Film Scan 2")); quality_combobox_->addItem(tr("Film Scan 2"));
quality_combobox_->addItem(tr("Film Scan 1.5")); quality_combobox_->addItem(tr("Film Scan 1.5"));
quality_combobox_->addItem(tr("Film Scan 1+")); quality_combobox_->addItem(tr("Film Scan 1+"));
quality_combobox_->addItem(tr("Film Scan 1")); quality_combobox_->addItem(tr("Film Scan 1"));
quality_combobox_->addItem(tr("High+")); quality_combobox_->addItem(tr("High+"));
quality_combobox_->addItem(tr("High")); quality_combobox_->addItem(tr("High"));
quality_combobox_->addItem(tr("Medium+")); quality_combobox_->addItem(tr("Medium+"));
quality_combobox_->addItem(tr("Medium")); quality_combobox_->addItem(tr("Medium"));
quality_combobox_->addItem(tr("Low+")); quality_combobox_->addItem(tr("Low+"));
quality_combobox_->addItem(tr("Low")); quality_combobox_->addItem(tr("Low"));
// Default to "medium" // Default to "medium"
quality_combobox_->setCurrentIndex(10); quality_combobox_->setCurrentIndex(10);
layout->addWidget(quality_combobox_, row, 1); layout->addWidget(quality_combobox_, row, 1);
} }
void CineformSection::AddOpts(EncodingParams *params) void CineformSection::AddOpts(EncodingParams *params)
{ {
params->set_video_option(QStringLiteral("quality"), QString::number(quality_combobox_->currentIndex())); params->set_video_option(
QStringLiteral("quality"),
QString::number(quality_combobox_->currentIndex()));
} }
void CineformSection::SetOpts(const EncodingParams *p) void CineformSection::SetOpts(const EncodingParams *p)
{ {
quality_combobox_->setCurrentIndex(p->video_option(QStringLiteral("quality")).toInt()); quality_combobox_->setCurrentIndex(
p->video_option(QStringLiteral("quality")).toInt());
} }
} }
+8 -9
View File
@@ -25,21 +25,20 @@
#include "codecsection.h" #include "codecsection.h"
namespace olive { namespace olive
class CineformSection : public CodecSection
{ {
Q_OBJECT
class CineformSection : public CodecSection {
Q_OBJECT
public: public:
CineformSection(QWidget *parent = nullptr); CineformSection(QWidget *parent = nullptr);
virtual void AddOpts(EncodingParams* params) override; virtual void AddOpts(EncodingParams *params) override;
virtual void SetOpts(const EncodingParams *p) override; virtual void SetOpts(const EncodingParams *p) override;
private: private:
QComboBox *quality_combobox_; QComboBox *quality_combobox_;
}; };
} }
+4 -3
View File
@@ -20,10 +20,11 @@
#include "codecsection.h" #include "codecsection.h"
namespace olive { namespace olive
{
CodecSection::CodecSection(QWidget *parent) : CodecSection::CodecSection(QWidget *parent)
QWidget(parent) : QWidget(parent)
{ {
} }
+13 -8
View File
@@ -25,18 +25,23 @@
#include "codec/encoder.h" #include "codec/encoder.h"
namespace olive { namespace olive
class CodecSection : public QWidget
{ {
Q_OBJECT
class CodecSection : public QWidget {
Q_OBJECT
public: public:
CodecSection(QWidget* parent = nullptr); CodecSection(QWidget *parent = nullptr);
virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)} virtual void AddOpts(EncodingParams *params)
{
virtual void SetOpts(const EncodingParams *p){Q_UNUSED(p)} Q_UNUSED(params)
}
virtual void SetOpts(const EncodingParams *p)
{
Q_UNUSED(p)
}
}; };
} }

Some files were not shown because too many files have changed in this diff Show More