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
+69 -101
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
# BasedOnStyle: Google
AccessModifierOffset: -1
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
FixNamespaceComments: false
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentWidth: 2
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
# Taken from git's rules
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 8
UseTab: Never
TabWidth: 4
UseTab: Always
...
+147
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"
namespace olive {
namespace olive
{
AudioManager* AudioManager::instance_ = nullptr;
AudioManager *AudioManager::instance_ = nullptr;
void AudioManager::CreateInstance()
{
if (instance_ == nullptr) {
instance_ = new AudioManager();
}
if (instance_ == nullptr) {
instance_ = new AudioManager();
}
}
void AudioManager::DestroyInstance()
{
delete instance_;
instance_ = nullptr;
delete instance_;
instance_ = nullptr;
}
AudioManager *AudioManager::instance()
{
return instance_;
return instance_;
}
void AudioManager::SetOutputNotifyInterval(int n)
{
output_buffer_->set_notify_interval(n);
output_buffer_->set_notify_interval(n);
}
int OutputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
int OutputCallback(const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{
PreviewAudioDevice *device = static_cast<PreviewAudioDevice*>(userData);
PreviewAudioDevice *device = static_cast<PreviewAudioDevice *>(userData);
qint64 max_read = frameCount * device->bytes_per_frame();
qint64 read_count = device->read(reinterpret_cast<char*>(output), max_read);
if (read_count < max_read) {
memset(reinterpret_cast<uint8_t*>(output) + read_count, 0, max_read - read_count);
}
qint64 max_read = frameCount * device->bytes_per_frame();
qint64 read_count =
device->read(reinterpret_cast<char *>(output), max_read);
if (read_count < max_read) {
memset(reinterpret_cast<uint8_t *>(output) + read_count, 0,
max_read - read_count);
}
return paContinue;
return paContinue;
}
int InputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
int InputCallback(const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{
FFmpegEncoder *f = static_cast<FFmpegEncoder*>(userData);
FFmpegEncoder *f = static_cast<FFmpegEncoder *>(userData);
AudioParams our_params = f->params().audio_params();
our_params.set_format(f->params().audio_params().format().to_packed_equivalent());
AudioParams our_params = f->params().audio_params();
our_params.set_format(
f->params().audio_params().format().to_packed_equivalent());
f->WriteAudioData(our_params, reinterpret_cast<const uint8_t**>(&input), frameCount);
f->WriteAudioData(our_params, reinterpret_cast<const uint8_t **>(&input),
frameCount);
return paContinue;
return paContinue;
}
bool AudioManager::PushToOutput(const AudioParams &params, const QByteArray &samples, QString *error)
bool AudioManager::PushToOutput(const AudioParams &params,
const QByteArray &samples, QString *error)
{
if (output_device_ == paNoDevice) {
if (error) *error = tr("No output device is set");
return false;
}
if (output_device_ == paNoDevice) {
if (error)
*error = tr("No output device is set");
return false;
}
if (output_params_ != params || output_stream_ == nullptr) {
output_params_ = params;
if (output_params_ != params || output_stream_ == nullptr) {
output_params_ = params;
CloseOutputStream();
CloseOutputStream();
PaStreamParameters p = GetPortAudioParams(params, output_device_);
PaStreamParameters p = GetPortAudioParams(params, output_device_);
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_);
if (r != paNoError) {
// Unhandled error
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
if (error) *error = Pa_GetErrorText(r);
return false;
}
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p,
output_params_.sample_rate(),
paFramesPerBufferUnspecified, paNoFlag,
OutputCallback, output_buffer_);
if (r != paNoError) {
// Unhandled error
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
if (error)
*error = Pa_GetErrorText(r);
return false;
}
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
}
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
}
output_buffer_->write(samples);
output_buffer_->write(samples);
if (!Pa_IsStreamActive(output_stream_)) {
Pa_StartStream(output_stream_);
}
if (!Pa_IsStreamActive(output_stream_)) {
Pa_StartStream(output_stream_);
}
return true;
return true;
}
void AudioManager::ClearBufferedOutput()
{
output_buffer_->clear();
output_buffer_->clear();
}
PaSampleFormat AudioManager::GetPortAudioSampleFormat(SampleFormat fmt)
{
switch (fmt) {
case SampleFormat::U8:
case SampleFormat::U8P:
return paUInt8;
case SampleFormat::S16:
case SampleFormat::S16P:
return paInt16;
case SampleFormat::S32:
case SampleFormat::S32P:
return paInt32;
case SampleFormat::F32:
case SampleFormat::F32P:
return paFloat32;
case SampleFormat::S64:
case SampleFormat::S64P:
case SampleFormat::F64:
case SampleFormat::F64P:
case SampleFormat::INVALID:
case SampleFormat::COUNT:
break;
}
switch (fmt) {
case SampleFormat::U8:
case SampleFormat::U8P:
return paUInt8;
case SampleFormat::S16:
case SampleFormat::S16P:
return paInt16;
case SampleFormat::S32:
case SampleFormat::S32P:
return paInt32;
case SampleFormat::F32:
case SampleFormat::F32P:
return paFloat32;
case SampleFormat::S64:
case SampleFormat::S64P:
case SampleFormat::F64:
case SampleFormat::F64P:
case SampleFormat::INVALID:
case SampleFormat::COUNT:
break;
}
return 0;
return 0;
}
void AudioManager::CloseOutputStream()
{
if (output_stream_) {
if (Pa_IsStreamActive(output_stream_)) {
StopOutput();
}
Pa_CloseStream(output_stream_);
output_stream_ = nullptr;
}
if (output_stream_) {
if (Pa_IsStreamActive(output_stream_)) {
StopOutput();
}
Pa_CloseStream(output_stream_);
output_stream_ = nullptr;
}
}
void AudioManager::StopOutput()
{
// Abort the stream so playback stops immediately
if (output_stream_) {
Pa_AbortStream(output_stream_);
ClearBufferedOutput();
}
// Abort the stream so playback stops immediately
if (output_stream_) {
Pa_AbortStream(output_stream_);
ClearBufferedOutput();
}
}
void AudioManager::SetOutputDevice(PaDeviceIndex device)
{
if (device == paNoDevice) {
qInfo() << "No output device found";
} else {
qInfo() << "Setting output audio device to" << Pa_GetDeviceInfo(device)->name;
}
if (device == paNoDevice) {
qInfo() << "No output device found";
} else {
qInfo() << "Setting output audio device to"
<< Pa_GetDeviceInfo(device)->name;
}
output_device_ = device;
output_device_ = device;
CloseOutputStream();
CloseOutputStream();
}
void AudioManager::SetInputDevice(PaDeviceIndex device)
{
if (device == paNoDevice) {
qInfo() << "No input device found";
} else {
qInfo() << "Setting input audio device to" << Pa_GetDeviceInfo(device)->name;
}
if (device == paNoDevice) {
qInfo() << "No input device found";
} else {
qInfo() << "Setting input audio device to"
<< Pa_GetDeviceInfo(device)->name;
}
input_device_ = device;
input_device_ = device;
}
void AudioManager::HardReset()
{
CloseOutputStream();
Pa_Terminate();
Pa_Initialize();
CloseOutputStream();
Pa_Terminate();
Pa_Initialize();
}
bool AudioManager::StartRecording(const EncodingParams &params, QString *error_str)
bool AudioManager::StartRecording(const EncodingParams &params,
QString *error_str)
{
if (input_device_ == paNoDevice) {
return false;
}
if (input_device_ == paNoDevice) {
return false;
}
input_encoder_ = new FFmpegEncoder(params);
if (!input_encoder_->Open()) {
qCritical() << "Failed to open encoder for recording";
return false;
}
input_encoder_ = new FFmpegEncoder(params);
if (!input_encoder_->Open()) {
qCritical() << "Failed to open encoder for recording";
return false;
}
PaStreamParameters p = GetPortAudioParams(params.audio_params(), input_device_);
PaStreamParameters p =
GetPortAudioParams(params.audio_params(), input_device_);
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_);
if (r == paNoError) {
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
r = Pa_StartStream(input_stream_);
if (r == paNoError) {
return true;
}
}
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr,
params.audio_params().sample_rate(),
paFramesPerBufferUnspecified, paNoFlag,
InputCallback, input_encoder_);
if (r == paNoError) {
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
r = Pa_StartStream(input_stream_);
if (r == paNoError) {
return true;
}
}
if (error_str) {
*error_str = Pa_GetErrorText(r);
}
if (error_str) {
*error_str = Pa_GetErrorText(r);
}
StopRecording();
return false;
StopRecording();
return false;
}
void AudioManager::StopRecording()
{
if (input_stream_) {
if (Pa_IsStreamActive(input_stream_)) {
Pa_StopStream(input_stream_);
}
Pa_CloseStream(input_stream_);
if (input_stream_) {
if (Pa_IsStreamActive(input_stream_)) {
Pa_StopStream(input_stream_);
}
Pa_CloseStream(input_stream_);
input_stream_ = nullptr;
}
input_stream_ = nullptr;
}
if (input_encoder_) {
input_encoder_->Close();
delete input_encoder_;
input_encoder_ = nullptr;
}
if (input_encoder_) {
input_encoder_->Close();
delete input_encoder_;
input_encoder_ = nullptr;
}
}
PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device)
{
QString entry = is_output_device ? QStringLiteral("AudioOutput") : QStringLiteral("AudioInput");
QString entry = is_output_device ? QStringLiteral("AudioOutput") :
QStringLiteral("AudioInput");
return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(), is_output_device);
return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(),
is_output_device);
}
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s, bool is_output_device)
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s,
bool is_output_device)
{
if (!s.isEmpty()) {
for (PaDeviceIndex i=0, end=Pa_GetDeviceCount(); i<end; i++) {
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
if (!s.isEmpty()) {
for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) {
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
if (((is_output_device && device->maxOutputChannels) || (!is_output_device && device->maxInputChannels))
&& !s.compare(device->name)) {
return i;
}
}
}
if (((is_output_device && device->maxOutputChannels) ||
(!is_output_device && device->maxInputChannels)) &&
!s.compare(device->name)) {
return i;
}
}
}
return is_output_device ? Pa_GetDefaultOutputDevice() : Pa_GetDefaultInputDevice();
return is_output_device ? Pa_GetDefaultOutputDevice() :
Pa_GetDefaultInputDevice();
}
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams &params, PaDeviceIndex device)
PaStreamParameters AudioManager::GetPortAudioParams(const AudioParams &params,
PaDeviceIndex device)
{
PaStreamParameters p;
PaStreamParameters p;
p.channelCount = params.channel_count();
p.device = device;
p.hostApiSpecificStreamInfo = nullptr;
p.sampleFormat = GetPortAudioSampleFormat(params.format());
p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
p.channelCount = params.channel_count();
p.device = device;
p.hostApiSpecificStreamInfo = nullptr;
p.sampleFormat = GetPortAudioSampleFormat(params.format());
p.suggestedLatency = Pa_GetDeviceInfo(device)->defaultLowOutputLatency;
return p;
return p;
}
AudioManager::AudioManager() :
output_stream_(nullptr),
input_stream_(nullptr),
input_encoder_(nullptr)
AudioManager::AudioManager()
: output_stream_(nullptr)
, input_stream_(nullptr)
, input_encoder_(nullptr)
{
#ifdef PA_HAS_JACK
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
// a QString converted to UTF-8)
PaJack_SetClientName("Olive");
// PortAudio doesn't do a strcpy, so we need a const char that's readily accessible (i.e. not
// a QString converted to UTF-8)
PaJack_SetClientName("Olive");
#endif
Pa_Initialize();
Pa_Initialize();
// Get device from config
PaDeviceIndex output_device = FindConfigDeviceByName(true);
PaDeviceIndex input_device = FindConfigDeviceByName(false);
// Get device from config
PaDeviceIndex output_device = FindConfigDeviceByName(true);
PaDeviceIndex input_device = FindConfigDeviceByName(false);
SetOutputDevice(output_device);
SetInputDevice(input_device);
SetOutputDevice(output_device);
SetInputDevice(input_device);
output_buffer_ = new PreviewAudioDevice(this);
output_buffer_->open(PreviewAudioDevice::ReadWrite);
connect(output_buffer_, &PreviewAudioDevice::Notify, this, &AudioManager::OutputNotify);
output_buffer_ = new PreviewAudioDevice(this);
output_buffer_->open(PreviewAudioDevice::ReadWrite);
connect(output_buffer_, &PreviewAudioDevice::Notify, this,
&AudioManager::OutputNotify);
}
AudioManager::~AudioManager()
{
CloseOutputStream();
CloseOutputStream();
Pa_Terminate();
Pa_Terminate();
}
}
+45 -42
View File
@@ -33,7 +33,8 @@
#include "render/audioplaybackcache.h"
#include "render/previewaudiodevice.h"
namespace olive {
namespace olive
{
/**
* @brief Audio input and output management class
@@ -41,74 +42,76 @@ namespace olive {
* Wraps around a QAudioOutput and AudioHybridDevice, connecting them together and exposing audio functionality to
* the rest of the system.
*/
class AudioManager : public QObject
{
Q_OBJECT
class AudioManager : public QObject {
Q_OBJECT
public:
static void CreateInstance();
static void DestroyInstance();
static void CreateInstance();
static void DestroyInstance();
static AudioManager* instance();
static AudioManager *instance();
void SetOutputNotifyInterval(int n);
void SetOutputNotifyInterval(int n);
bool PushToOutput(const AudioParams &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
{
return output_device_;
}
PaDeviceIndex GetOutputDevice() const
{
return output_device_;
}
PaDeviceIndex GetInputDevice() const
{
return input_device_;
}
PaDeviceIndex GetInputDevice() const
{
return input_device_;
}
void SetOutputDevice(PaDeviceIndex device);
void SetOutputDevice(PaDeviceIndex device);
void SetInputDevice(PaDeviceIndex device);
void SetInputDevice(PaDeviceIndex device);
void HardReset();
void HardReset();
bool StartRecording(const EncodingParams &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 FindDeviceByName(const QString &s, bool is_output_device);
static PaDeviceIndex FindConfigDeviceByName(bool is_output_device);
static PaDeviceIndex FindDeviceByName(const QString &s,
bool is_output_device);
static PaStreamParameters GetPortAudioParams(const AudioParams &p, PaDeviceIndex device);
static PaStreamParameters GetPortAudioParams(const AudioParams &p,
PaDeviceIndex device);
signals:
void OutputNotify();
void OutputNotify();
void OutputParamsChanged();
void OutputParamsChanged();
private:
AudioManager();
AudioManager();
virtual ~AudioManager() override;
virtual ~AudioManager() override;
static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt);
static PaSampleFormat GetPortAudioSampleFormat(SampleFormat fmt);
void CloseOutputStream();
void CloseOutputStream();
static AudioManager* instance_;
static AudioManager *instance_;
PaDeviceIndex output_device_;
PaStream *output_stream_;
AudioParams output_params_;
PreviewAudioDevice *output_buffer_;
PaDeviceIndex output_device_;
PaStream *output_stream_;
AudioParams output_params_;
PreviewAudioDevice *output_buffer_;
PaDeviceIndex input_device_;
PaStream *input_stream_;
FFmpegEncoder *input_encoder_;
PaDeviceIndex input_device_;
PaStream *input_stream_;
FFmpegEncoder *input_encoder_;
};
}
+226 -207
View File
@@ -29,279 +29,298 @@ extern "C" {
#include "common/ffmpegutils.h"
namespace olive {
namespace olive
{
AudioProcessor::AudioProcessor()
{
filter_graph_ = nullptr;
in_frame_ = nullptr;
out_frame_ = nullptr;
filter_graph_ = nullptr;
in_frame_ = nullptr;
out_frame_ = nullptr;
}
AudioProcessor::~AudioProcessor()
{
Close();
Close();
}
bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double tempo)
bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to,
double tempo)
{
if (filter_graph_) {
qWarning() << "Tried to open a processor that was already open";
return false;
}
if (filter_graph_) {
qWarning() << "Tried to open a processor that was already open";
return false;
}
filter_graph_ = avfilter_graph_alloc();
if (!filter_graph_) {
qCritical() << "Failed to allocate filter graph";
return false;
}
filter_graph_ = avfilter_graph_alloc();
if (!filter_graph_) {
qCritical() << "Failed to allocate filter graph";
return false;
}
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
// Set up audio buffer args
char filter_args[200];
snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
1,
from.sample_rate(),
from.sample_rate(),
from_fmt_,
from.channel_layout().u.mask);
// Set up audio buffer args
char filter_args[200];
snprintf(
filter_args, 200,
"time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
1, from.sample_rate(), from.sample_rate(), from_fmt_,
from.channel_layout().u.mask);
int r;
int r;
// Create buffersrc (input)
r = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("abuffer"), "in", filter_args, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create buffersrc:" << r;
Close();
return false;
}
// Create buffersrc (input)
r = avfilter_graph_create_filter(&buffersrc_ctx_,
avfilter_get_by_name("abuffer"), "in",
filter_args, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create buffersrc:" << r;
Close();
return false;
}
// Store "previous" filter for linking
AVFilterContext *previous_filter = buffersrc_ctx_;
// Store "previous" filter for linking
AVFilterContext *previous_filter = buffersrc_ctx_;
// Create tempo
bool create_tempo;
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
// those boundaries, we need to daisychain more than one together.
double base = (tempo > 1.0) ? 2.0 : 0.5;
double speed_log = log(tempo) / log(base);
// Create tempo
bool create_tempo;
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
// those boundaries, we need to daisychain more than one together.
double base = (tempo > 1.0) ? 2.0 : 0.5;
double speed_log = log(tempo) / log(base);
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
int whole = std::floor(speed_log);
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
int whole = std::floor(speed_log);
// Set speed_log to the remainder
speed_log -= whole;
// Set speed_log to the remainder
speed_log -= whole;
for (int i=0;i<=whole;i++) {
double filter_tempo = (i == whole) ? std::pow(base, speed_log) : base;
/*
for (int i = 0; i <= whole; i++) {
double filter_tempo = (i == whole) ? std::pow(base, speed_log) :
base;
/*
if (qFuzzyCompare(filter_tempo, 1.0)) {
// This filter would do nothing
continue;
}*/
previous_filter = CreateTempoFilter(filter_graph_,
previous_filter,
filter_tempo);
previous_filter =
CreateTempoFilter(filter_graph_, previous_filter, filter_tempo);
if (!previous_filter) {
qCritical() << "Failed to create audio tempo filter";
Close();
return false;
}
}
}
if (!previous_filter) {
qCritical() << "Failed to create audio tempo filter";
Close();
return false;
}
}
}
// Create conversion filter
auto ch1=from.channel_layout();
auto ch2=to.channel_layout();
if (from.sample_rate() != to.sample_rate() || av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format()
|| (to.format().is_planar() && create_tempo)) { // Tempo processor automatically converts to packed,
// so if the desired output is planar, it'll need
// to be converted
snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
av_get_sample_fmt_name(to_fmt_),
to.sample_rate(),
to.channel_layout().u.mask);
// Create conversion filter
auto ch1 = from.channel_layout();
auto ch2 = to.channel_layout();
if (from.sample_rate() != to.sample_rate() ||
av_channel_layout_compare(&ch1, &ch2) || from.format() != to.format() ||
(to.format().is_planar() &&
create_tempo)) { // Tempo processor automatically converts to packed,
// so if the desired output is planar, it'll need
// to be converted
snprintf(filter_args, 200,
"sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
av_get_sample_fmt_name(to_fmt_), to.sample_rate(),
to.channel_layout().u.mask);
AVFilterContext *c;
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"), "fmt", filter_args, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create format conversion filter:" << r << filter_args;
Close();
return false;
}
AVFilterContext *c;
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"),
"fmt", filter_args, nullptr,
filter_graph_);
if (r < 0) {
qCritical() << "Failed to create format conversion filter:" << r
<< filter_args;
Close();
return false;
}
r = avfilter_link(previous_filter, 0, c, 0);
if (r < 0) {
qCritical() << "Failed to link filters:" << r;
Close();
return false;
}
r = avfilter_link(previous_filter, 0, c, 0);
if (r < 0) {
qCritical() << "Failed to link filters:" << r;
Close();
return false;
}
previous_filter = c;
}
previous_filter = c;
}
// Create buffersink (output)
r = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create buffersink:" << r;
Close();
return false;
}
// Create buffersink (output)
r = avfilter_graph_create_filter(&buffersink_ctx_,
avfilter_get_by_name("abuffersink"), "out",
nullptr, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create buffersink:" << r;
Close();
return false;
}
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
if (r < 0) {
qCritical() << "Failed to link filters:" << r;
Close();
return false;
}
char *dump = avfilter_graph_dump(filter_graph_, nullptr);
qDebug() << dump;
av_free(dump);
r = avfilter_graph_config(filter_graph_, nullptr);
if (r < 0) {
qCritical() << "Failed to configure graph:" << r;
Close();
return false;
}
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
if (r < 0) {
qCritical() << "Failed to link filters:" << r;
Close();
return false;
}
char *dump = avfilter_graph_dump(filter_graph_, nullptr);
qDebug() << dump;
av_free(dump);
r = avfilter_graph_config(filter_graph_, nullptr);
if (r < 0) {
qCritical() << "Failed to configure graph:" << r;
Close();
return false;
}
in_frame_ = av_frame_alloc();
if (in_frame_) {
in_frame_->sample_rate = from.sample_rate();
in_frame_->format = from_fmt_;
in_frame_->ch_layout = from.channel_layout();
in_frame_->pts = 0;
} else {
qCritical() << "Failed to allocate input frame";
Close();
return false;
}
in_frame_ = av_frame_alloc();
if (in_frame_) {
in_frame_->sample_rate = from.sample_rate();
in_frame_->format = from_fmt_;
in_frame_->ch_layout = from.channel_layout();
in_frame_->pts = 0;
} else {
qCritical() << "Failed to allocate input frame";
Close();
return false;
}
out_frame_ = av_frame_alloc();
if (!out_frame_) {
qCritical() << "Failed to allocate output frame";
Close();
return false;
}
out_frame_ = av_frame_alloc();
if (!out_frame_) {
qCritical() << "Failed to allocate output frame";
Close();
return false;
}
from_ = from;
to_ = to;
from_ = from;
to_ = to;
return true;
return true;
}
void AudioProcessor::Close()
{
if (filter_graph_) {
avfilter_graph_free(&filter_graph_);
filter_graph_ = nullptr;
buffersrc_ctx_ = nullptr;
buffersink_ctx_ = nullptr;
}
if (filter_graph_) {
avfilter_graph_free(&filter_graph_);
filter_graph_ = nullptr;
buffersrc_ctx_ = nullptr;
buffersink_ctx_ = nullptr;
}
if (in_frame_) {
av_frame_free(&in_frame_);
in_frame_ = nullptr;
}
if (in_frame_) {
av_frame_free(&in_frame_);
in_frame_ = nullptr;
}
if (out_frame_) {
av_frame_free(&out_frame_);
out_frame_ = nullptr;
}
if (out_frame_) {
av_frame_free(&out_frame_);
out_frame_ = nullptr;
}
}
int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output)
int AudioProcessor::Convert(float **in, int nb_in_samples,
AudioProcessor::Buffer *output)
{
if (!IsOpen()) {
qCritical() << "Tried to convert on closed processor";
return -1;
}
if (!IsOpen()) {
qCritical() << "Tried to convert on closed processor";
return -1;
}
int r = 0;
int r = 0;
if (in && nb_in_samples) {
// Set frame parameters
in_frame_->nb_samples = nb_in_samples;
for (int i=0; i<from_.channel_count(); i++) {
in_frame_->data[i] = reinterpret_cast<uint8_t*>(in[i]);
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
}
if (in && nb_in_samples) {
// Set frame parameters
in_frame_->nb_samples = nb_in_samples;
for (int i = 0; i < from_.channel_count(); i++) {
in_frame_->data[i] = reinterpret_cast<uint8_t *>(in[i]);
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
}
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_, AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
qCritical() << "Failed to add frame to buffersrc:" << r;
return r;
}
}
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_,
AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
qCritical() << "Failed to add frame to buffersrc:" << r;
return r;
}
}
if (output) {
int nb_channels = to_.channel_count();
if (output) {
int nb_channels = to_.channel_count();
if (to_.format().is_packed()) {
nb_channels = 1;
}
if (to_.format().is_packed()) {
nb_channels = 1;
}
AudioProcessor::Buffer &result = *output;
result.resize(nb_channels);
AudioProcessor::Buffer &result = *output;
result.resize(nb_channels);
int byte_offset = 0;
int byte_offset = 0;
while (true) {
av_frame_unref(out_frame_);
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
if (r < 0) {
if (r == AVERROR(EAGAIN)) {
r = 0;
} else {
// Handle unexpected error
qCritical() << "Failed to pull from buffersink:" << r;
}
break;
}
while (true) {
av_frame_unref(out_frame_);
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
if (r < 0) {
if (r == AVERROR(EAGAIN)) {
r = 0;
} else {
// Handle unexpected error
qCritical() << "Failed to pull from buffersink:" << r;
}
break;
}
int nb_bytes = out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
if (to_.format().is_packed()) {
nb_bytes *= to_.channel_count();
}
int nb_bytes =
out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
if (to_.format().is_packed()) {
nb_bytes *= to_.channel_count();
}
for (int i=0; i<nb_channels; i++) {
result[i].resize(byte_offset + nb_bytes);
memcpy(result[i].data() + byte_offset, out_frame_->data[i], nb_bytes);
}
byte_offset += nb_bytes;
}
av_frame_unref(out_frame_);
}
for (int i = 0; i < nb_channels; i++) {
result[i].resize(byte_offset + nb_bytes);
memcpy(result[i].data() + byte_offset, out_frame_->data[i],
nb_bytes);
}
byte_offset += nb_bytes;
}
av_frame_unref(out_frame_);
}
return r;
return r;
}
void AudioProcessor::Flush()
{
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
qCritical() << "Failed to flush:" << r;
}
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr,
AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
qCritical() << "Failed to flush:" << r;
}
}
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilterContext* link, const double &tempo)
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph *graph,
AVFilterContext *link,
const double &tempo)
{
// Set up tempo param, which is taken as a C string
char speed_param[20];
snprintf(speed_param, 20, "%f", tempo);
// Set up tempo param, which is taken as a C string
char speed_param[20];
snprintf(speed_param, 20, "%f", tempo);
AVFilterContext* tempo_ctx = nullptr;
AVFilterContext *tempo_ctx = nullptr;
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) {
return tempo_ctx;
}
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"),
"atempo", speed_param, nullptr,
graph) >= 0 &&
avfilter_link(link, 0, tempo_ctx, 0) == 0) {
return tempo_ctx;
}
return nullptr;
return nullptr;
}
}
+36 -25
View File
@@ -31,52 +31,63 @@ extern "C" {
#include "common/define.h"
namespace olive {
namespace olive
{
using namespace core;
class AudioProcessor
{
class AudioProcessor {
public:
AudioProcessor();
AudioProcessor();
~AudioProcessor();
~AudioProcessor();
DISABLE_COPY_MOVE(AudioProcessor)
DISABLE_COPY_MOVE(AudioProcessor)
bool Open(const AudioParams &from, const AudioParams &to, double tempo = 1.0);
bool Open(const AudioParams &from, const AudioParams &to,
double tempo = 1.0);
void Close();
void Close();
bool IsOpen() const { return filter_graph_; }
bool IsOpen() const
{
return filter_graph_;
}
using Buffer = QVector<QByteArray>;
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
using Buffer = QVector<QByteArray>;
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
void Flush();
void Flush();
const AudioParams &from() const { return from_; }
const AudioParams &to() const { return to_; }
const AudioParams &from() const
{
return from_;
}
const AudioParams &to() const
{
return to_;
}
private:
static AVFilterContext* CreateTempoFilter(AVFilterGraph *graph, AVFilterContext *link, const double& tempo);
static AVFilterContext *CreateTempoFilter(AVFilterGraph *graph,
AVFilterContext *link,
const double &tempo);
AVFilterGraph* filter_graph_;
AVFilterGraph *filter_graph_;
AVFilterContext* buffersrc_ctx_;
AVFilterContext *buffersrc_ctx_;
AVFilterContext* buffersink_ctx_;
AVFilterContext *buffersink_ctx_;
AudioParams from_;
AVSampleFormat from_fmt_;
AudioParams from_;
AVSampleFormat from_fmt_;
AudioParams to_;
AVSampleFormat to_fmt_;
AudioParams to_;
AVSampleFormat to_fmt_;
AVFrame *in_frame_;
AVFrame *out_frame_;
AVFrame *in_frame_;
AVFrame *out_frame_;
};
}
+371 -315
View File
@@ -25,480 +25,536 @@
#include "config/config.h"
namespace olive {
namespace olive
{
const rational AudioVisualWaveform::kMinimumSampleRate = rational(1, 8);
const rational AudioVisualWaveform::kMaximumSampleRate = 1024;
AudioVisualWaveform::AudioVisualWaveform() :
channels_(0)
AudioVisualWaveform::AudioVisualWaveform()
: channels_(0)
{
for (rational i=kMinimumSampleRate; i<=kMaximumSampleRate; i*=2) {
mipmapped_data_.insert({i, Sample()});
}
for (rational i = kMinimumSampleRate; i <= kMaximumSampleRate; i *= 2) {
mipmapped_data_.insert({ i, Sample() });
}
}
void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational &start, double target_rate, Sample& data, size_t &start_index, size_t &samples_length)
void AudioVisualWaveform::OverwriteSamplesFromBuffer(
const SampleBuffer &samples, int sample_rate, const rational &start,
double target_rate, Sample &data, size_t &start_index,
size_t &samples_length)
{
start_index = time_to_samples(start, target_rate);
samples_length = time_to_samples(static_cast<double>(samples.sample_count()) / static_cast<double>(sample_rate), target_rate);
start_index = time_to_samples(start, target_rate);
samples_length =
time_to_samples(static_cast<double>(samples.sample_count()) /
static_cast<double>(sample_rate),
target_rate);
size_t end_index = start_index + samples_length;
if (data.size() < end_index) {
data.resize(end_index);
}
size_t end_index = start_index + samples_length;
if (data.size() < end_index) {
data.resize(end_index);
}
double chunk_size = double(sample_rate) / double(target_rate);
double chunk_size = double(sample_rate) / double(target_rate);
for (size_t i=0; i<samples_length; i+=channels_) {
size_t src_start = qRound((double(i) * chunk_size)) / channels_;
size_t src_end = qMin(size_t(qRound64((double(i + channels_) * chunk_size))) / channels_, samples.sample_count());
for (size_t i = 0; i < samples_length; i += channels_) {
size_t src_start = qRound((double(i) * chunk_size)) / channels_;
size_t src_end = qMin(
size_t(qRound64((double(i + channels_) * chunk_size))) / channels_,
samples.sample_count());
Sample summary = SumSamples(samples,
src_start,
src_end - src_start);
Sample summary = SumSamples(samples, src_start, src_end - src_start);
memcpy(&data.data()[i + start_index],
summary.data(),
summary.size() * sizeof(SamplePerChannel));
}
memcpy(&data.data()[i + start_index], summary.data(),
summary.size() * sizeof(SamplePerChannel));
}
}
void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::Sample &input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational &start, double output_rate, AudioVisualWaveform::Sample &output_data)
void AudioVisualWaveform::OverwriteSamplesFromMipmap(
const AudioVisualWaveform::Sample &input, double input_sample_rate,
size_t &input_start, size_t &input_length, const rational &start,
double output_rate, AudioVisualWaveform::Sample &output_data)
{
size_t start_index = time_to_samples(start, output_rate);
size_t samples_length = time_to_samples(static_cast<double>(input_length / channels_) / input_sample_rate, output_rate);
size_t start_index = time_to_samples(start, output_rate);
size_t samples_length = time_to_samples(
static_cast<double>(input_length / channels_) / input_sample_rate,
output_rate);
size_t end_index = start_index + samples_length;
if (output_data.size() < end_index) {
output_data.resize(end_index);
}
size_t end_index = start_index + samples_length;
if (output_data.size() < end_index) {
output_data.resize(end_index);
}
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
size_t chunk_size = input_sample_rate / output_rate;
// We guarantee mipmaps are powers of two so integer division should be perfectly accurate here
size_t chunk_size = input_sample_rate / output_rate;
for (size_t i=0; i<samples_length; i+=channels_) {
Sample summary = ReSumSamples(&input.data()[input_start + (i*chunk_size)], chunk_size * channels_, channels_);
for (size_t i = 0; i < samples_length; i += channels_) {
Sample summary =
ReSumSamples(&input.data()[input_start + (i * chunk_size)],
chunk_size * channels_, channels_);
memcpy(&output_data.data()[i + start_index],
summary.data(),
summary.size() * sizeof(SamplePerChannel));
}
memcpy(&output_data.data()[i + start_index], summary.data(),
summary.size() * sizeof(SamplePerChannel));
}
input_start = start_index;
input_length = samples_length;
input_start = start_index;
input_length = samples_length;
}
void AudioVisualWaveform::ValidateVirtualStart(const rational &new_start)
{
if (length_ == 0) {
virtual_start_ = new_start;
} else if (virtual_start_ > new_start) {
TrimIn(new_start - virtual_start_);
}
if (length_ == 0) {
virtual_start_ = new_start;
} else if (virtual_start_ > new_start) {
TrimIn(new_start - virtual_start_);
}
}
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational &start)
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples,
int sample_rate,
const rational &start)
{
if (!channels_) {
qWarning() << "Failed to write samples - channel count is zero";
return;
}
if (!channels_) {
qWarning() << "Failed to write samples - channel count is zero";
return;
}
ValidateVirtualStart(start);
ValidateVirtualStart(start);
// Process the largest mipmap directly for the samples
auto current_mipmap = mipmapped_data_.rbegin();
size_t input_start, input_length;
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_, current_mipmap->first.toDouble(), current_mipmap->second, input_start, input_length);
// Process the largest mipmap directly for the samples
auto current_mipmap = mipmapped_data_.rbegin();
size_t input_start, input_length;
OverwriteSamplesFromBuffer(samples, sample_rate, start - virtual_start_,
current_mipmap->first.toDouble(),
current_mipmap->second, input_start,
input_length);
while (true) {
// For each smaller mipmap, we just process from the mipmap before it, making each one
// exponentially faster to create
auto previous_mipmap = current_mipmap;
current_mipmap++;
if (current_mipmap == mipmapped_data_.rend()) {
break;
}
while (true) {
// For each smaller mipmap, we just process from the mipmap before it, making each one
// exponentially faster to create
auto previous_mipmap = current_mipmap;
current_mipmap++;
if (current_mipmap == mipmapped_data_.rend()) {
break;
}
OverwriteSamplesFromMipmap(previous_mipmap->second, previous_mipmap->first.toDouble(),
input_start, input_length, start - virtual_start_, current_mipmap->first.toDouble(),
current_mipmap->second);
}
OverwriteSamplesFromMipmap(
previous_mipmap->second, previous_mipmap->first.toDouble(),
input_start, input_length, start - virtual_start_,
current_mipmap->first.toDouble(), current_mipmap->second);
}
rational sample_length(samples.sample_count(), sample_rate);
length_ = qMax(length_, start + sample_length);
rational sample_length(samples.sample_count(), sample_rate);
length_ = qMax(length_, start + sample_length);
}
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const rational &dest, const rational& offset, const rational& length)
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums,
const rational &dest,
const rational &offset,
const rational &length)
{
ValidateVirtualStart(dest);
ValidateVirtualStart(dest);
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
rational rate = it->first;
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first;
Sample& our_arr = it->second;
const Sample& their_arr = sums.mipmapped_data_.at(rate);
Sample &our_arr = it->second;
const Sample &their_arr = sums.mipmapped_data_.at(rate);
double rate_dbl = rate.toDouble();
double rate_dbl = rate.toDouble();
// Get our destination sample
size_t our_start_index = time_to_samples(dest - virtual_start_, rate_dbl);
// Get our destination sample
size_t our_start_index =
time_to_samples(dest - virtual_start_, rate_dbl);
// Get our source sample
size_t their_start_index = time_to_samples(offset, rate_dbl);
if (their_start_index >= their_arr.size()) {
continue;
}
// Get our source sample
size_t their_start_index = time_to_samples(offset, rate_dbl);
if (their_start_index >= their_arr.size()) {
continue;
}
// Determine how much we're copying
size_t copy_len = their_arr.size() - their_start_index;
if (!length.isNull()) {
copy_len = qMin(copy_len, time_to_samples(length, rate_dbl));
if (copy_len == 0) {
continue;
}
}
// Determine how much we're copying
size_t copy_len = their_arr.size() - their_start_index;
if (!length.isNull()) {
copy_len = qMin(copy_len, time_to_samples(length, rate_dbl));
if (copy_len == 0) {
continue;
}
}
// Determine end index of our array
size_t end_index = our_start_index + copy_len;
if (our_arr.size() < end_index) {
our_arr.resize(end_index);
}
// Determine end index of our array
size_t end_index = our_start_index + copy_len;
if (our_arr.size() < end_index) {
our_arr.resize(end_index);
}
memcpy(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel),
reinterpret_cast<const char*>(their_arr.data()) + their_start_index * sizeof(SamplePerChannel),
copy_len * sizeof(SamplePerChannel));
}
memcpy(reinterpret_cast<char *>(our_arr.data()) +
our_start_index * sizeof(SamplePerChannel),
reinterpret_cast<const char *>(their_arr.data()) +
their_start_index * sizeof(SamplePerChannel),
copy_len * sizeof(SamplePerChannel));
}
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset : length));
length_ = qMax(length_, dest + ((length.isNull()) ? sums.length() - offset :
length));
}
void AudioVisualWaveform::OverwriteSilence(const rational &start, const rational &length)
void AudioVisualWaveform::OverwriteSilence(const rational &start,
const rational &length)
{
ValidateVirtualStart(start);
ValidateVirtualStart(start);
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
rational rate = it->first;
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first;
Sample& our_arr = it->second;
Sample &our_arr = it->second;
double rate_dbl = rate.toDouble();
double rate_dbl = rate.toDouble();
// Get our destination sample
size_t our_start_index = time_to_samples(start - virtual_start_, rate_dbl);
size_t our_length_index = time_to_samples(length, rate_dbl);
size_t our_end_index = our_start_index + our_length_index;
// Get our destination sample
size_t our_start_index =
time_to_samples(start - virtual_start_, rate_dbl);
size_t our_length_index = time_to_samples(length, rate_dbl);
size_t our_end_index = our_start_index + our_length_index;
if (our_arr.size() < our_end_index) {
our_arr.resize(our_end_index);
}
if (our_arr.size() < our_end_index) {
our_arr.resize(our_end_index);
}
memset(reinterpret_cast<char*>(our_arr.data()) + our_start_index * sizeof(SamplePerChannel), 0, our_length_index * sizeof(SamplePerChannel));
}
memset(reinterpret_cast<char *>(our_arr.data()) +
our_start_index * sizeof(SamplePerChannel),
0, our_length_index * sizeof(SamplePerChannel));
}
length_ = qMax(length_, start + length);
length_ = qMax(length_, start + length);
}
void AudioVisualWaveform::TrimIn(rational length)
{
if (length == 0) {
return;
}
if (length == 0) {
return;
}
virtual_start_ += length;
virtual_start_ += length;
bool negative = (length < 0);
if (negative) {
length = -length;
}
bool negative = (length < 0);
if (negative) {
length = -length;
}
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
rational rate = it->first;
double rate_dbl = rate.toDouble();
Sample& data = it->second;
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first;
double rate_dbl = rate.toDouble();
Sample &data = it->second;
size_t chop_length = time_to_samples(length, rate_dbl);
if (chop_length == 0) {
continue;
}
size_t chop_length = time_to_samples(length, rate_dbl);
if (chop_length == 0) {
continue;
}
if (!negative) {
data = Sample(data.begin() + chop_length, data.end());
} else {
data.insert(data.begin(), chop_length, SamplePerChannel());
}
}
if (!negative) {
data = Sample(data.begin() + chop_length, data.end());
} else {
data.insert(data.begin(), chop_length, SamplePerChannel());
}
}
length_ = qMax(rational(0), length_ - length);
length_ = qMax(rational(0), length_ - length);
}
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset) const
{
AudioVisualWaveform mid = *this;
AudioVisualWaveform mid = *this;
mid.TrimIn(offset - virtual_start_);
mid.TrimIn(offset - virtual_start_);
return mid;
return mid;
}
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset, const rational &length) const
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &offset,
const rational &length) const
{
AudioVisualWaveform mid = *this;
AudioVisualWaveform mid = *this;
mid.TrimRange(offset - virtual_start_, length);
mid.TrimRange(offset - virtual_start_, length);
return mid;
return mid;
}
void AudioVisualWaveform::Resize(const rational &length)
{
if (length_ == length) {
return;
}
if (length_ == length) {
return;
}
for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) {
rational rate = it->first;
double rate_dbl = rate.toDouble();
Sample& data = it->second;
for (auto it = mipmapped_data_.begin(); it != mipmapped_data_.end(); it++) {
rational rate = it->first;
double rate_dbl = rate.toDouble();
Sample &data = it->second;
size_t chop_length = time_to_samples(length, rate_dbl);
size_t chop_length = time_to_samples(length, rate_dbl);
data.resize(chop_length);
}
data.resize(chop_length);
}
length_ = length;
length_ = length;
}
void AudioVisualWaveform::TrimRange(const rational &in, const rational &length)
{
TrimIn(in);
Resize(length);
TrimIn(in);
Resize(length);
}
AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const rational &start, const rational &length) const
AudioVisualWaveform::Sample
AudioVisualWaveform::GetSummaryFromTime(const rational &start,
const rational &length) const
{
// Find mipmap that requires
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
// Find mipmap that requires
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());
double rate_dbl = using_mipmap->first.toDouble();
double rate_dbl = using_mipmap->first.toDouble();
size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl);
size_t sample_length = time_to_samples(length, rate_dbl);
size_t start_sample = time_to_samples(start - virtual_start_, rate_dbl);
size_t sample_length = time_to_samples(length, rate_dbl);
const Sample &mipmap_data = using_mipmap->second;
const Sample &mipmap_data = using_mipmap->second;
// Determine if the array actually has this sample
sample_length = qMin(sample_length, mipmap_data.size() - start_sample);
// Determine if the array actually has this sample
sample_length = qMin(sample_length, mipmap_data.size() - start_sample);
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
// array and nothing can be returned.
if (sample_length > 0) {
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length, channels_);
}
// Based on the above `min`, if sample length <= 0, that means start_sample >= the size of the
// array and nothing can be returned.
if (sample_length > 0) {
return ReSumSamples(&mipmap_data.data()[start_sample], sample_length,
channels_);
}
// Return null samples
return AudioVisualWaveform::Sample(channel_count(), {0, 0});
// Return null samples
return AudioVisualWaveform::Sample(channel_count(), { 0, 0 });
}
void ExpandMinMaxChannel(const float *a, size_t length, float &min_val, float &max_val)
void ExpandMinMaxChannel(const float *a, size_t length, float &min_val,
float &max_val)
{
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
// SSE optimized
// SSE optimized
// load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits)
__m128 max = _mm_loadu_ps(a);
__m128 min = _mm_loadu_ps(a);
// load the first 4 elements of 'a' into min and max (they are 4 * 32 = 128 bits)
__m128 max = _mm_loadu_ps(a);
__m128 min = _mm_loadu_ps(a);
// loop over 'a' and compare current elements with min and max 4 by 4.
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
for(size_t i = 4; i < length-4; i+=4) {
__m128 cur = _mm_loadu_ps(a + i);
max = _mm_max_ps(max, cur);
min = _mm_min_ps(min, cur);
}
// so we read the last 4 (or less) elements in a safe manner.
__m128 cur = _mm_loadu_ps(a + length - 4);
max = _mm_max_ps(max, cur);
min = _mm_min_ps(min, cur);
// this potentially overlaps up to the last 3 elements but it's not an issue.
// loop over 'a' and compare current elements with min and max 4 by 4.
// we need to make sure we don't read out of boundaries should 'a' length be not mod. 4
for (size_t i = 4; i < length - 4; i += 4) {
__m128 cur = _mm_loadu_ps(a + i);
max = _mm_max_ps(max, cur);
min = _mm_min_ps(min, cur);
}
// so we read the last 4 (or less) elements in a safe manner.
__m128 cur = _mm_loadu_ps(a + length - 4);
max = _mm_max_ps(max, cur);
min = _mm_min_ps(min, cur);
// this potentially overlaps up to the last 3 elements but it's not an issue.
// min and max will contain 4 min and max. To get the absolute min and max
// we need to compare the 4 values over themselves by shuffling each time.
for (size_t i = 0; i < 3; i++) {
max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93));
min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93));
}
// now min and max contain 4 identical items each representing min and max value respectively.
// min and max will contain 4 min and max. To get the absolute min and max
// we need to compare the 4 values over themselves by shuffling each time.
for (size_t i = 0; i < 3; i++) {
max = _mm_max_ps(max, _mm_shuffle_ps(max, max, 0x93));
min = _mm_min_ps(min, _mm_shuffle_ps(min, min, 0x93));
}
// now min and max contain 4 identical items each representing min and max value respectively.
// and we store the first one into a float variable.
_mm_store_ss(&max_val, max);
_mm_store_ss(&min_val, min);
// I bet you don't find annotated low level code very often.
// and we store the first one into a float variable.
_mm_store_ss(&max_val, max);
_mm_store_ss(&min_val, min);
// I bet you don't find annotated low level code very often.
#else
// Standard unoptimized function
for (size_t i=0; i<length; i++) {
min_val = std::min(min_val, a[i]);
max_val = std::max(max_val, a[i]);
}
// Standard unoptimized function
for (size_t i = 0; i < length; i++) {
min_val = std::min(min_val, a[i]);
max_val = std::max(max_val, a[i]);
}
#endif
}
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index, size_t length)
AudioVisualWaveform::Sample
AudioVisualWaveform::SumSamples(const SampleBuffer &samples, size_t start_index,
size_t length)
{
int channels = samples.audio_params().channel_count();
AudioVisualWaveform::Sample summed_samples(channels);
int channels = samples.audio_params().channel_count();
AudioVisualWaveform::Sample summed_samples(channels);
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) {
ExpandMinMaxChannel(samples.data(channel) + start_index, length, summed_samples[channel].min, summed_samples[channel].max);
}
for (int channel = 0; channel < samples.audio_params().channel_count();
channel++) {
ExpandMinMaxChannel(samples.data(channel) + start_index, length,
summed_samples[channel].min,
summed_samples[channel].max);
}
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
// for (size_t i=start_index; i<end_index; i++) {
// ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]);
// }
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
// for (size_t i=start_index; i<end_index; i++) {
// ExpandMinMax(summed_samples[i%channels], samples->data(i%channels)[i]);
// }
return summed_samples;
return summed_samples;
}
AudioVisualWaveform::Sample AudioVisualWaveform::ReSumSamples(const SamplePerChannel* samples,
size_t nb_samples,
int nb_channels)
AudioVisualWaveform::Sample
AudioVisualWaveform::ReSumSamples(const SamplePerChannel *samples,
size_t nb_samples, int nb_channels)
{
AudioVisualWaveform::Sample summed_samples(nb_channels);
AudioVisualWaveform::Sample summed_samples(nb_channels);
for (size_t i=0;i<nb_samples;i+=nb_channels) {
for (int j=0;j<nb_channels;j++) {
const AudioVisualWaveform::SamplePerChannel& sample = samples[i + j];
for (size_t i = 0; i < nb_samples; i += nb_channels) {
for (int j = 0; j < nb_channels; j++) {
const AudioVisualWaveform::SamplePerChannel &sample =
samples[i + j];
if (sample.min < summed_samples[j].min) {
summed_samples[j].min = sample.min;
}
if (sample.min < summed_samples[j].min) {
summed_samples[j].min = sample.min;
}
if (sample.max > summed_samples[j].max) {
summed_samples[j].max = sample.max;
}
}
}
if (sample.max > summed_samples[j].max) {
summed_samples[j].max = sample.max;
}
}
}
return summed_samples;
return summed_samples;
}
template <typename T>
inline int round_away_from_zero(T t)
template <typename T> inline int round_away_from_zero(T t)
{
return (t < 0) ? std::floor(t) : std::ceil(t);
return (t < 0) ? std::floor(t) : std::ceil(t);
}
void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample& sample, int x, int y, int height, bool rectified)
void AudioVisualWaveform::DrawSample(QPainter *painter, const Sample &sample,
int x, int y, int height, bool rectified)
{
if (sample.empty()) {
return;
}
if (sample.empty()) {
return;
}
int channel_height = height / sample.size();
int channel_half_height = channel_height / 2;
int channel_height = height / sample.size();
int channel_half_height = channel_height / 2;
for (size_t i=0;i<sample.size();i++) {
float max = qMin(sample.at(i).max, 1.0f);
float min = qMax(sample.at(i).min, -1.0f);
for (size_t i = 0; i < sample.size(); i++) {
float max = qMin(sample.at(i).max, 1.0f);
float min = qMax(sample.at(i).min, -1.0f);
if (rectified) {
int channel_bottom = y + channel_height * (i + 1);
if (rectified) {
int channel_bottom = y + channel_height * (i + 1);
int diff = round_away_from_zero((max - min) * channel_half_height);
int diff = round_away_from_zero((max - min) * channel_half_height);
painter->drawLine(x,
channel_bottom - diff,
x,
channel_bottom);
} else {
int channel_mid = y + channel_height * i + channel_half_height;
painter->drawLine(x, channel_bottom - diff, x, channel_bottom);
} else {
int channel_mid = y + channel_height * i + channel_half_height;
// We subtract the sample so that positive Y values go up on the screen rather than down,
// which is how waveforms are usually rendered
painter->drawLine(x,
channel_mid - round_away_from_zero(min * static_cast<float>(channel_half_height)),
x,
channel_mid - round_away_from_zero(max * static_cast<float>(channel_half_height)));
}
}
// We subtract the sample so that positive Y values go up on the screen rather than down,
// which is how waveforms are usually rendered
painter->drawLine(
x,
channel_mid -
round_away_from_zero(
min * static_cast<float>(channel_half_height)),
x,
channel_mid -
round_away_from_zero(
max * static_cast<float>(channel_half_height)));
}
}
}
void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, const double& scale, const AudioVisualWaveform &samples, const rational& start_time)
void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect &rect,
const double &scale,
const AudioVisualWaveform &samples,
const rational &start_time)
{
if (samples.mipmapped_data_.empty()) {
return;
}
if (samples.mipmapped_data_.empty()) {
return;
}
auto using_mipmap = samples.GetMipmapForScale(scale);
auto using_mipmap = samples.GetMipmapForScale(scale);
rational rate = using_mipmap->first;
double rate_dbl = rate.toDouble();
const Sample& arr = using_mipmap->second;
rational rate = using_mipmap->first;
double rate_dbl = rate.toDouble();
const Sample &arr = using_mipmap->second;
size_t start_sample_index = samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl);
size_t start_sample_index =
samples.time_to_samples(start_time - samples.virtual_start_, rate_dbl);
if (start_sample_index >= arr.size()) {
return;
}
if (start_sample_index >= arr.size()) {
return;
}
size_t next_sample_index = start_sample_index;
size_t sample_index;
size_t next_sample_index = start_sample_index;
size_t sample_index;
Sample summary;
size_t summary_index = -1;
Sample summary;
size_t summary_index = -1;
const QRect& viewport = painter->viewport();
QPoint top_left = painter->transform().map(viewport.topLeft());
const QRect &viewport = painter->viewport();
QPoint top_left = painter->transform().map(viewport.topLeft());
size_t start = qMax(rect.x(), -top_left.x());
size_t end = qMin(rect.right(), -top_left.x() + viewport.width());
size_t start = qMax(rect.x(), -top_left.x());
size_t end = qMin(rect.right(), -top_left.x() + viewport.width());
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
for (size_t i=start;i<end;i++) {
sample_index = next_sample_index;
for (size_t i = start; i < end; i++) {
sample_index = next_sample_index;
if (sample_index == arr.size()) {
break;
}
if (sample_index == arr.size()) {
break;
}
next_sample_index = std::min(arr.size(),
size_t(start_sample_index + std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) / scale) * samples.channel_count()));
next_sample_index = std::min(
arr.size(),
size_t(start_sample_index +
std::floor(rate_dbl * static_cast<double>(i - rect.x() + 1) /
scale) *
samples.channel_count()));
if (summary_index != sample_index) {
summary = AudioVisualWaveform::ReSumSamples(&arr.at(sample_index),
qMax(size_t(samples.channel_count()), next_sample_index - sample_index),
samples.channel_count());
summary_index = sample_index;
}
if (summary_index != sample_index) {
summary = AudioVisualWaveform::ReSumSamples(
&arr.at(sample_index),
qMax(size_t(samples.channel_count()),
next_sample_index - sample_index),
samples.channel_count());
summary_index = sample_index;
}
DrawSample(painter, summary, i, rect.y(), rect.height(), rectified);
}
DrawSample(painter, summary, i, rect.y(), rect.height(), rectified);
}
}
size_t AudioVisualWaveform::time_to_samples(const rational &time, double sample_rate) const
size_t AudioVisualWaveform::time_to_samples(const rational &time,
double sample_rate) const
{
return time_to_samples(time.toDouble(), sample_rate);
return time_to_samples(time.toDouble(), sample_rate);
}
size_t AudioVisualWaveform::time_to_samples(const double &time, double sample_rate) const
size_t AudioVisualWaveform::time_to_samples(const double &time,
double sample_rate) const
{
return std::floor(time * sample_rate) * channels_;
return std::floor(time * sample_rate) * channels_;
}
std::map<rational, AudioVisualWaveform::Sample>::const_iterator AudioVisualWaveform::GetMipmapForScale(double scale) const
std::map<rational, AudioVisualWaveform::Sample>::const_iterator
AudioVisualWaveform::GetMipmapForScale(double scale) const
{
// Find largest mipmap for this scale (or the largest if we don't find one sufficient)
for (auto it=mipmapped_data_.cbegin(); it!=mipmapped_data_.cend(); it++) {
if (it->first.toDouble() >= scale) {
return it;
}
}
// Find largest mipmap for this scale (or the largest if we don't find one sufficient)
for (auto it = mipmapped_data_.cbegin(); it != mipmapped_data_.cend();
it++) {
if (it->first.toDouble() >= scale) {
return it;
}
}
// We don't have a mipmap large enough for this scale, so just return the largest we have
return std::prev(mipmapped_data_.cend());
// We don't have a mipmap large enough for this scale, so just return the largest we have
return std::prev(mipmapped_data_.cend());
}
}
+67 -48
View File
@@ -25,7 +25,8 @@
#include <QPainter>
#include <QVector>
namespace olive {
namespace olive
{
using namespace core;
@@ -37,38 +38,39 @@ using namespace core;
*/
class AudioVisualWaveform {
public:
AudioVisualWaveform();
AudioVisualWaveform();
struct SamplePerChannel {
float min;
float max;
};
struct SamplePerChannel {
float min;
float max;
};
using Sample = std::vector<SamplePerChannel>;
using Sample = std::vector<SamplePerChannel>;
int channel_count() const
{
return channels_;
}
int channel_count() const
{
return channels_;
}
void set_channel_count(int channels)
{
channels_ = channels;
}
void set_channel_count(int channels)
{
channels_ = channels;
}
const rational& length() const
{
return length_;
}
const rational &length() const
{
return length_;
}
/**
/**
* @brief Writes samples into the visual waveform buffer
*
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
*/
void OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational& start = 0);
void OverwriteSamples(const SampleBuffer &samples, int sample_rate,
const rational &start = 0);
/**
/**
* @brief Replaces sums at a certain range in this visual waveform
*
* @param sums
@@ -87,53 +89,70 @@ public:
*
* Maximum length of `sums` to overwrite with.
*/
void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = 0, const rational &length = 0);
void OverwriteSums(const AudioVisualWaveform &sums, const rational &dest,
const rational &offset = 0, const rational &length = 0);
void OverwriteSilence(const rational &start, const rational &length);
void OverwriteSilence(const rational &start, const rational &length);
void TrimIn(rational length);
void TrimIn(rational length);
AudioVisualWaveform Mid(const rational &offset) const;
AudioVisualWaveform Mid(const rational &offset, const rational &length) const;
AudioVisualWaveform Mid(const rational &offset) const;
AudioVisualWaveform Mid(const rational &offset,
const rational &length) const;
void Resize(const rational &length);
void Resize(const rational &length);
void TrimRange(const rational &in, const rational &length);
void TrimRange(const rational &in, const rational &length);
Sample GetSummaryFromTime(const rational& start, const rational& length) const;
Sample GetSummaryFromTime(const rational &start,
const rational &length) const;
static Sample SumSamples(const SampleBuffer &samples, size_t start_index, size_t length);
static Sample SumSamples(const SampleBuffer &samples, size_t start_index,
size_t length);
static Sample ReSumSamples(const SamplePerChannel *samples, size_t nb_samples, int nb_channels);
static Sample ReSumSamples(const SamplePerChannel *samples,
size_t nb_samples, int nb_channels);
static void DrawSample(QPainter* painter, const Sample &sample, int x, int y, int height, bool rectified);
static void DrawSample(QPainter *painter, const Sample &sample, int x,
int y, int height, bool rectified);
static void DrawWaveform(QPainter* painter, const QRect &rect, const double &scale, const AudioVisualWaveform& samples, const rational &start_time);
static void DrawWaveform(QPainter *painter, const QRect &rect,
const double &scale,
const AudioVisualWaveform &samples,
const rational &start_time);
// Must be a power of 2
static const rational kMinimumSampleRate;
static const rational kMaximumSampleRate;
// Must be a power of 2
static const rational kMinimumSampleRate;
static const rational kMaximumSampleRate;
private:
void OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational& start, double target_rate, Sample &data, size_t &start_index, size_t &samples_length);
void OverwriteSamplesFromBuffer(const SampleBuffer &samples,
int sample_rate, const rational &start,
double target_rate, Sample &data,
size_t &start_index,
size_t &samples_length);
void OverwriteSamplesFromMipmap(const Sample& input, double input_sample_rate, size_t &input_start, size_t &input_length, const rational& start, double output_rate, Sample &output_data);
void OverwriteSamplesFromMipmap(const Sample &input,
double input_sample_rate,
size_t &input_start, size_t &input_length,
const rational &start, double output_rate,
Sample &output_data);
size_t time_to_samples(const rational& time, double sample_rate) const;
size_t time_to_samples(const double& time, double sample_rate) const;
size_t time_to_samples(const rational &time, double sample_rate) const;
size_t time_to_samples(const double &time, double sample_rate) const;
std::map<rational, Sample>::const_iterator GetMipmapForScale(double scale) const;
std::map<rational, Sample>::const_iterator
GetMipmapForScale(double scale) const;
void ValidateVirtualStart(const rational &new_start);
void ValidateVirtualStart(const rational &new_start);
rational virtual_start_;
rational virtual_start_;
int channels_;
int channels_;
std::map<rational, Sample> mipmapped_data_;
rational length_;
std::map<rational, Sample> mipmapped_data_;
rational length_;
};
}
+2 -2
View File
@@ -20,11 +20,11 @@
#include "cliexportmanager.h"
namespace olive {
namespace olive
{
CLIExportManager::CLIExportManager()
{
}
}
+4 -4
View File
@@ -23,12 +23,12 @@
#include "task/export/export.h"
namespace olive {
class CLIExportManager : public QObject
namespace olive
{
class CLIExportManager : public QObject {
public:
CLIExportManager();
CLIExportManager();
};
}
+56 -54
View File
@@ -22,85 +22,87 @@
#include <iostream>
namespace olive {
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) :
QObject(parent),
title_(title),
progress_(-1),
drawn_(false)
namespace olive
{
SetProgress(0);
CLIProgressDialog::CLIProgressDialog(const QString &title, QObject *parent)
: QObject(parent)
, title_(title)
, progress_(-1)
, drawn_(false)
{
SetProgress(0);
}
void CLIProgressDialog::Update()
{
if (drawn_) {
// We've been here before, do a carriage return back to the start of the terminal line
std::cout << "\r";
} else {
drawn_ = true;
}
if (drawn_) {
// We've been here before, do a carriage return back to the start of the terminal line
std::cout << "\r";
} else {
drawn_ = true;
}
// FIXME: Get real column count
int columns = 80;
// FIXME: Get real column count
int columns = 80;
int title_columns = columns / 2 - 1;
int title_columns = columns / 2 - 1;
// Print "title" text
QString sized_title = title_;
// Print "title" text
QString sized_title = title_;
if (title_.size() > title_columns) {
sized_title = title_.left(title_columns - 3).append(QStringLiteral("..."));
} else {
sized_title = title_;
}
if (title_.size() > title_columns) {
sized_title =
title_.left(title_columns - 3).append(QStringLiteral("..."));
} else {
sized_title = title_;
}
std::cout << sized_title.toUtf8().constData();
std::cout << sized_title.toUtf8().constData();
// Pad out the rest of the title area if necessary
for (int i=sized_title.size(); i<title_columns; i++) {
std::cout << " ";
}
// Pad out the rest of the title area if necessary
for (int i = sized_title.size(); i < title_columns; i++) {
std::cout << " ";
}
// Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters
int progress_bar_columns = columns / 2 - 7;
// Percentage counter " 100% " is 5 characters + the enclosing brackets [] are 2 characters
int progress_bar_columns = columns / 2 - 7;
std::cout << "[";
std::cout << "[";
// Get UI bar progress
int bar_prog = qRound(progress_ * progress_bar_columns);
// Get UI bar progress
int bar_prog = qRound(progress_ * progress_bar_columns);
// Draw filled in bar
for (int i=0;i<bar_prog;i++) {
std::cout << "=";
}
// Draw filled in bar
for (int i = 0; i < bar_prog; i++) {
std::cout << "=";
}
// Draw empty space
for (int i=bar_prog;i<progress_bar_columns;i++) {
std::cout << " ";
}
// Draw empty space
for (int i = bar_prog; i < progress_bar_columns; i++) {
std::cout << " ";
}
std::cout << "] ";
std::cout << "] ";
if (progress_ < 100) {
std::cout << " ";
}
if (progress_ < 100) {
std::cout << " ";
}
if (progress_ < 10) {
std::cout << " ";
}
if (progress_ < 10) {
std::cout << " ";
}
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
}
void CLIProgressDialog::SetProgress(double p)
{
if (progress_ != p) {
progress_ = p;
if (progress_ != p) {
progress_ = p;
Update();
}
Update();
}
}
}
+9 -10
View File
@@ -26,25 +26,24 @@
#include "common/define.h"
namespace olive {
class CLIProgressDialog : public QObject
namespace olive
{
class CLIProgressDialog : public QObject {
public:
CLIProgressDialog(const QString &title, QObject* parent = nullptr);
CLIProgressDialog(const QString &title, QObject *parent = nullptr);
public slots:
void SetProgress(double p);
void SetProgress(double p);
private:
void Update();
void Update();
QString title_;
QString title_;
double progress_;
bool drawn_;
double progress_;
bool drawn_;
};
}
+8 -7
View File
@@ -20,18 +20,19 @@
#include "clitaskdialog.h"
namespace olive {
CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) :
CLIProgressDialog(task->GetTitle(), parent),
task_(task)
namespace olive
{
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
CLITaskDialog::CLITaskDialog(Task *task, QObject *parent)
: CLIProgressDialog(task->GetTitle(), parent)
, task_(task)
{
connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
}
bool CLITaskDialog::Run()
{
return task_->Start();
return task_->Start();
}
}
+8 -9
View File
@@ -24,19 +24,18 @@
#include "cli/cliprogress/cliprogressdialog.h"
#include "task/task.h"
namespace olive {
class CLITaskDialog : public CLIProgressDialog
namespace olive
{
Q_OBJECT
public:
CLITaskDialog(Task *task, QObject* parent = nullptr);
bool Run();
class CLITaskDialog : public CLIProgressDialog {
Q_OBJECT
public:
CLITaskDialog(Task *task, QObject *parent = nullptr);
bool Run();
private:
Task* task_;
Task *task_;
};
}
+98 -84
View File
@@ -4,123 +4,137 @@
#include "task/taskmanager.h"
namespace olive {
namespace olive
{
ConformManager *ConformManager::instance_ = nullptr;
ConformManager::Conform ConformManager::GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &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
QMutexLocker locker(&mutex_);
// Mutex because we'll need to check the status of a conform task
QMutexLocker locker(&mutex_);
// Return existing conform if exists
QVector<QString> filenames = GetConformedFilename(cache_path, stream, params);
if (AllConformsExist(filenames)) {
return {kConformExists, filenames, nullptr};
}
// Return existing conform if exists
QVector<QString> filenames =
GetConformedFilename(cache_path, stream, params);
if (AllConformsExist(filenames)) {
return { kConformExists, filenames, nullptr };
}
ConformTask *conforming_task = nullptr;
ConformTask *conforming_task = nullptr;
foreach (const ConformData &data, conforming_) {
if (data.stream == stream && data.params == params) {
// Already creating conform in a task
conforming_task = data.task;
break;
}
}
foreach (const ConformData &data, conforming_) {
if (data.stream == stream && data.params == params) {
// Already creating conform in a task
conforming_task = data.task;
break;
}
}
if (!conforming_task) {
// Not conforming yet, create a task to do so
if (!conforming_task) {
// Not conforming yet, create a task to do so
// We conform to a different filename until it's done to make it clear even across sessions
// whether this conform is ready or not
QVector<QString> working_filenames = filenames;
for (int i=0; i<working_filenames.size(); i++) {
working_filenames[i].append(QStringLiteral(".working"));
}
// We conform to a different filename until it's done to make it clear even across sessions
// whether this conform is ready or not
QVector<QString> working_filenames = filenames;
for (int i = 0; i < working_filenames.size(); i++) {
working_filenames[i].append(QStringLiteral(".working"));
}
conforming_task = new ConformTask(decoder_id, stream, params, working_filenames);
connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished);
conforming_task->moveToThread(TaskManager::instance()->thread());
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task));
conforming_task =
new ConformTask(decoder_id, stream, params, working_filenames);
connect(conforming_task, &ConformTask::Finished, this,
&ConformManager::ConformTaskFinished);
conforming_task->moveToThread(TaskManager::instance()->thread());
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask",
Qt::QueuedConnection,
Q_ARG(Task *, conforming_task));
conforming_.append({stream, params, conforming_task, working_filenames, filenames});
}
conforming_.append(
{ stream, params, conforming_task, working_filenames, filenames });
}
if (wait) {
do {
conform_done_condition_.wait(&mutex_);
} while (!AllConformsExist(filenames));
return {kConformExists, filenames, nullptr};
}
if (wait) {
do {
conform_done_condition_.wait(&mutex_);
} while (!AllConformsExist(filenames));
return { kConformExists, filenames, nullptr };
}
return {kConformGenerating, QVector<QString>(), conforming_task};
return { kConformGenerating, QVector<QString>(), conforming_task };
}
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &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++) {
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
QString::number(stream.stream()),
QString::number(params.sample_rate()),
QString::number(params.format()),
QString::number(params.channel_layout().u.mask),
QString::number(i));
for (int i = 0; i < filenames.size(); i++) {
QString index_fn =
QStringLiteral("%1-%2.%3.%4.%5.%6.pcm")
.arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
QString::number(stream.stream()),
QString::number(params.sample_rate()),
QString::number(params.format()),
QString::number(params.channel_layout().u.mask),
QString::number(i));
filenames[i] = QDir(cache_path).filePath(index_fn);
}
filenames[i] = QDir(cache_path).filePath(index_fn);
}
return filenames;
return filenames;
}
bool ConformManager::AllConformsExist(const QVector<QString> &filenames)
{
foreach (const QString &fn, filenames) {
if (!QFileInfo::exists(fn)) {
return false;
}
}
foreach (const QString &fn, filenames) {
if (!QFileInfo::exists(fn)) {
return false;
}
}
return true;
return true;
}
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
ConformData data;
ConformData data;
// Remove conform data from list
for (int i=0; i<conforming_.size(); i++) {
const ConformData &c = conforming_.at(i);
if (c.task == task) {
data = c;
conforming_.removeAt(i);
break;
}
}
// Remove conform data from list
for (int i = 0; i < conforming_.size(); i++) {
const ConformData &c = conforming_.at(i);
if (c.task == task) {
data = c;
conforming_.removeAt(i);
break;
}
}
if (succeeded) {
// Move file to standard conform name, making it clear this conform is ready for use
for (int i=0; i<data.finished_filename.size(); i++) {
const QString &finished = data.finished_filename.at(i);
const QString &working = data.working_filename.at(i);
if (succeeded) {
// Move file to standard conform name, making it clear this conform is ready for use
for (int i = 0; i < data.finished_filename.size(); i++) {
const QString &finished = data.finished_filename.at(i);
const QString &working = data.working_filename.at(i);
QFile::remove(finished);
QFile::rename(working, finished);
}
QFile::remove(finished);
QFile::rename(working, finished);
}
conform_done_condition_.wakeAll();
locker.unlock();
emit ConformReady();
} else {
// Failed, just delete the working filename if exists
for (int i=0; i<data.working_filename.size(); i++) {
QFile::remove(data.working_filename.at(i));
}
}
conform_done_condition_.wakeAll();
locker.unlock();
emit ConformReady();
} else {
// Failed, just delete the working filename if exists
for (int i = 0; i < data.working_filename.size(); i++) {
QFile::remove(data.working_filename.at(i));
}
}
}
}
+57 -55
View File
@@ -7,82 +7,84 @@
#include "decoder.h"
#include "task/conform/conform.h"
namespace olive {
class ConformManager : public QObject
namespace olive
{
Q_OBJECT
class ConformManager : public QObject {
Q_OBJECT
public:
static void CreateInstance()
{
if (!instance_) {
instance_ = new ConformManager();
}
}
static void CreateInstance()
{
if (!instance_) {
instance_ = new ConformManager();
}
}
static void DestroyInstance()
{
delete instance_;
instance_ = nullptr;
}
static void DestroyInstance()
{
delete instance_;
instance_ = nullptr;
}
static ConformManager *instance()
{
return instance_;
}
static ConformManager *instance()
{
return instance_;
}
enum ConformState {
kConformExists,
kConformGenerating
};
enum ConformState { kConformExists, kConformGenerating };
struct Conform {
ConformState state;
QVector<QString> filenames;
ConformTask *task;
};
struct Conform {
ConformState state;
QVector<QString> filenames;
ConformTask *task;
};
/**
* @brief Get conform state, and start conforming if no conform exists
*
* Thread-safe.
*/
Conform GetConformState(const QString &decoder_id, const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params, bool wait);
/**
* @brief Get conform state, and start conforming if no conform exists
*
* Thread-safe.
*/
Conform GetConformState(const QString &decoder_id,
const QString &cache_path,
const Decoder::CodecStream &stream,
const AudioParams &params, bool wait);
signals:
void ConformReady();
void ConformReady();
private:
ConformManager() = default;
ConformManager() = default;
static ConformManager *instance_;
static ConformManager *instance_;
QMutex mutex_;
QMutex mutex_;
QWaitCondition conform_done_condition_;
QWaitCondition conform_done_condition_;
struct ConformData {
Decoder::CodecStream stream;
AudioParams params;
ConformTask *task;
QVector<QString> working_filename;
QVector<QString> finished_filename;
};
struct ConformData {
Decoder::CodecStream stream;
AudioParams params;
ConformTask *task;
QVector<QString> working_filename;
QVector<QString> finished_filename;
};
QVector<ConformData> conforming_;
QVector<ConformData> conforming_;
/**
* @brief Get the destination filename of an audio stream conformed to a set of parameters
*/
static QVector<QString> GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params);
/**
* @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 bool AllConformsExist(const QVector<QString> &filenames);
static bool AllConformsExist(const QVector<QString> &filenames);
private slots:
void ConformTaskFinished(Task *task, bool succeeded);
void ConformTaskFinished(Task *task, bool succeeded);
};
}
} // namespace olive
#endif // CONFORMMANAGER_H
+215 -185
View File
@@ -33,151 +33,162 @@
#include "node/project.h"
#include "task/taskmanager.h"
namespace olive {
namespace olive
{
const rational Decoder::kAnyTimecode = RATIONAL_MIN;
Decoder::Decoder() :
cached_texture_(nullptr)
Decoder::Decoder()
: cached_texture_(nullptr)
{
UpdateLastAccessed();
UpdateLastAccessed();
}
void Decoder::IncrementAccessTime(qint64 t)
{
last_accessed_ += t;
last_accessed_ += t;
}
bool Decoder::Open(const CodecStream &stream)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
UpdateLastAccessed();
UpdateLastAccessed();
if (stream_.IsValid()) {
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
if (stream_ == stream) {
return true;
} else {
qWarning() << "Tried to open a decoder that was already open with another stream";
return false;
}
} else {
// Stream was not open, try opening it now
if (!stream.IsValid()) {
// Cannot open null stream
qCritical() << "Decoder attempted to open null stream";
return false;
}
if (stream_.IsValid()) {
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
if (stream_ == stream) {
return true;
} else {
qWarning()
<< "Tried to open a decoder that was already open with another stream";
return false;
}
} else {
// Stream was not open, try opening it now
if (!stream.IsValid()) {
// Cannot open null stream
qCritical() << "Decoder attempted to open null stream";
return false;
}
if (!stream.Exists()) {
// Cannot open file that doesn't exist
qCritical() << "Decoder attempted to open file that doesn't exist";
return false;
}
if (!stream.Exists()) {
// Cannot open file that doesn't exist
qCritical() << "Decoder attempted to open file that doesn't exist";
return false;
}
// Set stream
stream_ = stream;
// Set stream
stream_ = stream;
// Try open internal
if (OpenInternal()) {
return true;
} else {
// Unset stream
qCritical() << "Failed to open" << stream_.filename() << "stream" << stream_.stream();
CloseInternal();
stream_.Reset();
return false;
}
}
// Try open internal
if (OpenInternal()) {
return true;
} else {
// Unset stream
qCritical() << "Failed to open" << stream_.filename() << "stream"
<< stream_.stream();
CloseInternal();
stream_.Reset();
return false;
}
}
}
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
UpdateLastAccessed();
UpdateLastAccessed();
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve video on a closed decoder";
return nullptr;
}
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve video on a closed decoder";
return nullptr;
}
if (!SupportsVideo()) {
qCritical() << "Decoder doesn't support video";
return nullptr;
}
if (!SupportsVideo()) {
qCritical() << "Decoder doesn't support video";
return nullptr;
}
if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr;
}
if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr;
}
if (cached_texture_ && cached_time_ == p.time && cached_divider_ == p.divider) {
return cached_texture_;
}
if (cached_texture_ && cached_time_ == p.time &&
cached_divider_ == p.divider) {
return cached_texture_;
}
cached_texture_ = RetrieveVideoInternal(p);
cached_time_ = p.time;
cached_divider_ = p.divider;
cached_texture_ = RetrieveVideoInternal(p);
cached_time_ = p.time;
cached_divider_ = p.divider;
return cached_texture_;
return cached_texture_;
}
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams &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()) {
qCritical() << "Can't retrieve audio on a closed decoder";
return kInvalid;
}
if (!stream_.IsValid()) {
qCritical() << "Can't retrieve audio on a closed decoder";
return kInvalid;
}
if (!SupportsAudio()) {
qCritical() << "Decoder doesn't support audio";
return kInvalid;
}
if (!SupportsAudio()) {
qCritical() << "Decoder doesn't support audio";
return kInvalid;
}
// Get conform state from ConformManager
ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
if (conform.state == ConformManager::kConformGenerating) {
// If we need the task, it's available in `conform.task`
return kWaitingForConform;
}
// Get conform state from ConformManager
ConformManager::Conform conform =
ConformManager::instance()->GetConformState(
id(), cache_path, stream_, params, (mode == RenderMode::kOnline));
if (conform.state == ConformManager::kConformGenerating) {
// If we need the task, it's available in `conform.task`
return kWaitingForConform;
}
// See if we got the conform
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) {
return kOK;
} else {
return kUnknownError;
}
// See if we got the conform
if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode,
params)) {
return kOK;
} else {
return kUnknownError;
}
}
qint64 Decoder::GetLastAccessedTime()
{
return last_accessed_;
return last_accessed_;
}
void Decoder::Close()
{
QMutexLocker locker(&mutex_);
QMutexLocker locker(&mutex_);
UpdateLastAccessed();
UpdateLastAccessed();
cached_texture_ = nullptr;
cached_texture_ = nullptr;
if (stream_.IsValid()) {
CloseInternal();
stream_.Reset();
} else {
qWarning() << "Tried to close a decoder that wasn't open";
}
if (stream_.IsValid()) {
CloseInternal();
stream_.Reset();
} else {
qWarning() << "Tried to close a decoder that wasn't open";
}
}
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams &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> decoders;
QVector<DecoderPtr> decoders;
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
decoders.append(std::make_shared<OIIODecoder>());
decoders.append(std::make_shared<FFmpegDecoder>());
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
// since it supports so many formats and we presumably want to override those formats with a more specific decoder.
decoders.append(std::make_shared<OIIODecoder>());
decoders.append(std::make_shared<FFmpegDecoder>());
return decoders;
return decoders;
}
DecoderPtr Decoder::CreateFromID(const QString &id)
{
if (id.isEmpty()) {
return nullptr;
}
if (id.isEmpty()) {
return nullptr;
}
// Create list to iterate through
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
// Create list to iterate through
QVector<DecoderPtr> decoder_list = ReceiveListOfAllDecoders();
foreach (DecoderPtr d, decoder_list) {
if (d->id() == id) {
return d;
}
}
foreach (DecoderPtr d, decoder_list) {
if (d->id() == id) {
return d;
}
}
return nullptr;
return nullptr;
}
void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration)
{
if (duration != AV_NOPTS_VALUE && duration != 0) {
emit IndexProgress(static_cast<double>(ts) / static_cast<double>(duration));
}
if (duration != AV_NOPTS_VALUE && duration != 0) {
emit IndexProgress(static_cast<double>(ts) /
static_cast<double>(duration));
}
}
QString Decoder::TransformImageSequenceFileName(const QString &filename, const int64_t& number)
QString Decoder::TransformImageSequenceFileName(const QString &filename,
const int64_t &number)
{
int digit_count = GetImageSequenceDigitCount(filename);
int digit_count = GetImageSequenceDigitCount(filename);
QFileInfo file_info(filename);
QFileInfo file_info(filename);
QString original_basename = file_info.completeBaseName();
QString original_basename = file_info.completeBaseName();
QString new_basename = original_basename.left(original_basename.size() - digit_count)
.append(QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
QString new_basename =
original_basename.left(original_basename.size() - digit_count)
.append(
QStringLiteral("%1").arg(number, digit_count, 10, QChar('0')));
return file_info.dir().filePath(file_info.fileName().replace(original_basename, new_basename));
return file_info.dir().filePath(
file_info.fileName().replace(original_basename, new_basename));
}
int Decoder::GetImageSequenceDigitCount(const QString &filename)
{
QString basename = QFileInfo(filename).completeBaseName();
QString basename = QFileInfo(filename).completeBaseName();
// See if basename contains a number at the end
int digit_count = 0;
// See if basename contains a number at the end
int digit_count = 0;
for (int i=basename.size()-1;i>=0;i--) {
if (basename.at(i).isDigit()) {
digit_count++;
} else {
break;
}
}
for (int i = basename.size() - 1; i >= 0; i--) {
if (basename.at(i).isDigit()) {
digit_count++;
} else {
break;
}
}
return digit_count;
return digit_count;
}
int64_t Decoder::GetImageSequenceIndex(const QString &filename)
{
int digit_count = GetImageSequenceDigitCount(filename);
int digit_count = GetImageSequenceDigitCount(filename);
QFileInfo file_info(filename);
QFileInfo file_info(filename);
QString original_basename = file_info.completeBaseName();
QString original_basename = file_info.completeBaseName();
QString number_only = original_basename.mid(original_basename.size() - digit_count);
QString number_only =
original_basename.mid(original_basename.size() - digit_count);
return number_only.toLongLong();
return number_only.toLongLong();
}
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
Q_UNUSED(p)
return nullptr;
Q_UNUSED(p)
return nullptr;
}
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, CancelAtom *cancelled)
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled)
{
Q_UNUSED(filenames)
Q_UNUSED(cancelled)
Q_UNUSED(params)
return false;
Q_UNUSED(filenames)
Q_UNUSED(cancelled)
Q_UNUSED(params)
return false;
}
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
bool Decoder::RetrieveAudioFromConform(
SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames,
TimeRange range, LoopMode loop_mode, const AudioParams &input_params)
{
PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) {
// Offset range by audio start offset
range -= GetAudioStartOffset();
PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) {
// Offset range by audio start offset
range -= GetAudioStartOffset();
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
qint64 write_index = 0;
qint64 read_index = input_params.time_to_bytes(range.in()) /
input_params.channel_count();
qint64 write_index = 0;
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel();
const qint64 buffer_length_in_bytes =
sample_buffer.sample_count() *
input_params.bytes_per_sample_per_channel();
while (write_index < buffer_length_in_bytes) {
if (loop_mode == LoopMode::kLoopModeLoop) {
while (read_index >= input.size()) {
read_index -= input.size();
}
while (write_index < buffer_length_in_bytes) {
if (loop_mode == LoopMode::kLoopModeLoop) {
while (read_index >= input.size()) {
read_index -= input.size();
}
while (read_index < 0) {
read_index += input.size();
}
}
while (read_index < 0) {
read_index += input.size();
}
}
qint64 write_count = 0;
qint64 write_count = 0;
if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer.silence_bytes(write_index, write_index + write_count);
} else if (read_index >= input.size()) {
// Reading after data length, write silence until the end of the buffer
write_count = buffer_length_in_bytes - write_index;
sample_buffer.silence_bytes(write_index, write_index + write_count);
} else {
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
input.seek(read_index);
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index);
}
if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer.silence_bytes(write_index,
write_index + write_count);
} else if (read_index >= input.size()) {
// Reading after data length, write silence until the end of the buffer
write_count = buffer_length_in_bytes - write_index;
sample_buffer.silence_bytes(write_index,
write_index + write_count);
} else {
write_count = qMin(input.size() - read_index,
buffer_length_in_bytes - write_index);
input.seek(read_index);
input.read(reinterpret_cast<char **>(
sample_buffer.to_raw_ptrs().data()),
write_count, write_index);
}
read_index += write_count;
write_index += write_count;
}
read_index += write_count;
write_index += write_count;
}
input.close();
input.close();
return true;
}
return true;
}
return false;
return false;
}
void Decoder::UpdateLastAccessed()
{
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
last_accessed_ = QDateTime::currentMSecsSinceEpoch();
}
uint qHash(Decoder::CodecStream stream, uint seed)
{
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^ qHash(stream.block(), seed);
return qHash(stream.filename(), seed) ^ ::qHash(stream.stream(), seed) ^
qHash(stream.block(), seed);
}
}
+143 -126
View File
@@ -36,12 +36,17 @@ extern "C" {
#include "render/cancelatom.h"
#include "render/rendermodes.h"
namespace olive {
namespace olive
{
class Decoder;
using DecoderPtr = std::shared_ptr<Decoder>;
#define DECODER_DEFAULT_DESTRUCTOR(x) virtual ~x() override {CloseInternal();}
#define DECODER_DEFAULT_DESTRUCTOR(x) \
virtual ~x() override \
{ \
CloseInternal(); \
}
/**
* @brief A decoder's is the main class for bringing external media into Olive
@@ -59,89 +64,88 @@ using DecoderPtr = std::shared_ptr<Decoder>;
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
* to be utilized in the rest of the rendering pipeline.
*/
class Decoder : public QObject
{
Q_OBJECT
class Decoder : public QObject {
Q_OBJECT
public:
enum RetrieveState {
kReady,
kFailedToOpen,
kIndexUnavailable
};
enum RetrieveState { kReady, kFailedToOpen, kIndexUnavailable };
Decoder();
Decoder();
/**
/**
* @brief Unique decoder ID
*/
virtual QString id() const = 0;
virtual QString id() const = 0;
virtual bool SupportsVideo(){return false;}
virtual bool SupportsAudio(){return false;}
virtual bool SupportsVideo()
{
return false;
}
virtual bool SupportsAudio()
{
return false;
}
void IncrementAccessTime(qint64 t);
void IncrementAccessTime(qint64 t);
class CodecStream
{
public:
CodecStream() :
stream_(-1),
block_(nullptr)
{
}
class CodecStream {
public:
CodecStream()
: stream_(-1)
, block_(nullptr)
{
}
CodecStream(const QString& filename, int stream, Block *block) :
filename_(filename),
stream_(stream),
block_(block)
{
}
CodecStream(const QString &filename, int stream, Block *block)
: filename_(filename)
, stream_(stream)
, block_(block)
{
}
bool IsValid() const
{
return !filename_.isEmpty() && stream_ >= 0;
}
bool IsValid() const
{
return !filename_.isEmpty() && stream_ >= 0;
}
bool Exists() const
{
return QFileInfo::exists(filename_);
}
bool Exists() const
{
return QFileInfo::exists(filename_);
}
void Reset()
{
*this = CodecStream();
}
void Reset()
{
*this = CodecStream();
}
bool operator==(const CodecStream& rhs) const
{
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
}
bool operator==(const CodecStream &rhs) const
{
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
}
const QString& filename() const
{
return filename_;
}
const QString &filename() const
{
return filename_;
}
int stream() const
{
return stream_;
}
int stream() const
{
return stream_;
}
Block *block() const
{
return block_;
}
Block *block() const
{
return block_;
}
private:
QString filename_;
private:
QString filename_;
int stream_;
int stream_;
Block *block_;
Block *block_;
};
};
/**
/**
* @brief Open stream for decoding
*
* This function is thread safe.
@@ -150,22 +154,21 @@ public:
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
* be opened OR if already open and the stream is NOT the same.
*/
bool Open(const CodecStream& stream);
bool Open(const CodecStream &stream);
static const rational kAnyTimecode;
static const rational kAnyTimecode;
struct RetrieveVideoParams
{
Renderer *renderer = nullptr;
rational time;
int divider = 1;
PixelFormat maximum_format = PixelFormat::INVALID;
CancelAtom *cancelled = nullptr;
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
};
struct RetrieveVideoParams {
Renderer *renderer = nullptr;
rational time;
int divider = 1;
PixelFormat maximum_format = PixelFormat::INVALID;
CancelAtom *cancelled = nullptr;
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone;
};
/**
/**
* @brief Retrieves a video frame from footage
*
* This function will always return a valid frame unless a fatal error occurs (in such case,
@@ -175,16 +178,16 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
TexturePtr RetrieveVideo(const RetrieveVideoParams& p);
TexturePtr RetrieveVideo(const RetrieveVideoParams &p);
enum RetrieveAudioStatus {
kInvalid = -1,
kOK,
kWaitingForConform,
kUnknownError
};
enum RetrieveAudioStatus {
kInvalid = -1,
kOK,
kWaitingForConform,
kUnknownError
};
/**
/**
* @brief Retrieve audio data from footage
*
* This function will always return a sample buffer unless a fatal error occurs (in such case,
@@ -192,14 +195,17 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode);
RetrieveAudioStatus
RetrieveAudio(SampleBuffer &dest, const TimeRange &range,
const AudioParams &params, const QString &cache_path,
LoopMode loop_mode, RenderMode::Mode mode);
/**
/**
* @brief Determine the last time this decoder instance was used in any way
*/
qint64 GetLastAccessedTime();
qint64 GetLastAccessedTime();
/**
/**
* @brief Generate a Footage object from a file
*
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
@@ -210,39 +216,43 @@ public:
*
* This function is re-entrant.
*/
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const = 0;
virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const = 0;
/**
/**
* @brief Closes media/deallocates memory
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
void Close();
void Close();
/**
/**
* @brief Conform audio stream
*/
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams &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
*
* @return
*
* A Decoder instance or nullptr if a Decoder with this ID does not exist
*/
static DecoderPtr CreateFromID(const QString& id);
static DecoderPtr CreateFromID(const QString &id);
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number);
static QString TransformImageSequenceFileName(const QString &filename,
const int64_t &number);
static int GetImageSequenceDigitCount(const QString& filename);
static int GetImageSequenceDigitCount(const QString &filename);
static int64_t GetImageSequenceIndex(const QString& filename);
static int64_t GetImageSequenceIndex(const QString &filename);
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
static QVector<DecoderPtr> ReceiveListOfAllDecoders();
protected:
/**
/**
* @brief Internal open function
*
* Sub-classes must override this function. Function will already be mutexed, so there is no need
@@ -254,62 +264,69 @@ protected:
* return FALSE. If this function returns false, Decoder will call CloseInternal to clean any
* memory allocated during OpenInternal.
*/
virtual bool OpenInternal() = 0;
virtual bool OpenInternal() = 0;
/**
/**
* @brief Internal close function
*
* Sub-classes must override this function. Function should be able to safely clear all allocated
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
*/
virtual void CloseInternal() = 0;
virtual void CloseInternal() = 0;
/**
/**
* @brief Internal frame retrieval function
*
* Sub-classes must override this function IF they support video. Function is already mutexed
* so sub-classes don't need to worry about thread safety.
*/
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p);
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams &p);
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &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
*
* This function is NOT thread safe and should therefore only be called by thread safe functions.
*/
const CodecStream& stream() const
{
return stream_;
}
const CodecStream &stream() const
{
return stream_;
}
virtual rational GetAudioStartOffset() const { return 0; }
virtual rational GetAudioStartOffset() const
{
return 0;
}
signals:
/**
/**
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
* available
*/
void IndexProgress(double);
void IndexProgress(double);
private:
void UpdateLastAccessed();
void UpdateLastAccessed();
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &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_;
TexturePtr cached_texture_;
rational cached_time_;
int cached_divider_;
std::atomic_int64_t last_accessed_;
TexturePtr cached_texture_;
rational cached_time_;
int cached_divider_;
};
uint qHash(Decoder::CodecStream stream, uint seed = 0);
+415 -339
View File
@@ -26,492 +26,568 @@
#include "ffmpeg/ffmpegencoder.h"
#include "oiio/oiioencoder.h"
namespace olive {
namespace olive
{
const QRegularExpression Encoder::kImageSequenceContainsDigits = QRegularExpression(QStringLiteral("\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceRemoveDigits = QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceContainsDigits =
QRegularExpression(QStringLiteral("\\[[#]+\\]"));
const QRegularExpression Encoder::kImageSequenceRemoveDigits =
QRegularExpression(QStringLiteral("[\\-\\.\\ \\_]?\\[[#]+\\]"));
Encoder::Encoder(const EncodingParams &params) :
params_(params)
Encoder::Encoder(const EncodingParams &params)
: params_(params)
{
}
const EncodingParams &Encoder::params() const
{
return params_;
return params_;
}
QString Encoder::GetFilenameForFrame(const rational &frame)
{
if (params().video_is_image_sequence()) {
// Transform!
int64_t frame_index = Timecode::time_to_timestamp(frame, params().video_params().frame_rate_as_time_base());
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
QString frame_index_str = QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
if (params().video_is_image_sequence()) {
// Transform!
int64_t frame_index = Timecode::time_to_timestamp(
frame, params().video_params().frame_rate_as_time_base());
int digits = GetImageSequencePlaceholderDigitCount(params().filename());
QString frame_index_str =
QStringLiteral("%1").arg(frame_index, digits, 10, QChar('0'));
QString f = params_.filename();
f.replace(kImageSequenceContainsDigits, frame_index_str);
return f;
} else {
// Keep filename
return params_.filename();
}
QString f = params_.filename();
f.replace(kImageSequenceContainsDigits, frame_index_str);
return f;
} else {
// Keep filename
return params_.filename();
}
}
int Encoder::GetImageSequencePlaceholderDigitCount(const QString &filename)
{
int start = filename.indexOf(kImageSequenceContainsDigits);
int digit_count = 0;
for (int i=start+1; i<filename.size(); i++) {
if (filename.at(i) == '#') {
digit_count++;
} else {
break;
}
}
return digit_count;
int start = filename.indexOf(kImageSequenceContainsDigits);
int digit_count = 0;
for (int i = start + 1; i < filename.size(); i++) {
if (filename.at(i) == '#') {
digit_count++;
} else {
break;
}
}
return digit_count;
}
bool Encoder::FilenameContainsDigitPlaceholder(const QString& filename)
bool Encoder::FilenameContainsDigitPlaceholder(const QString &filename)
{
return filename.contains(kImageSequenceContainsDigits);
return filename.contains(kImageSequenceContainsDigits);
}
QString Encoder::FilenameRemoveDigitPlaceholder(QString filename)
{
return filename.remove(kImageSequenceRemoveDigits);
return filename.remove(kImageSequenceRemoveDigits);
}
EncodingParams::EncodingParams() :
video_enabled_(false),
video_bit_rate_(0),
video_min_bit_rate_(0),
video_max_bit_rate_(0),
video_buffer_size_(0),
video_threads_(0),
video_is_image_sequence_(false),
audio_enabled_(false),
audio_bit_rate_(0),
subtitles_enabled_(false),
subtitles_are_sidecar_(false),
video_scaling_method_(kStretch),
has_custom_range_(false)
EncodingParams::EncodingParams()
: video_enabled_(false)
, video_bit_rate_(0)
, video_min_bit_rate_(0)
, video_max_bit_rate_(0)
, video_buffer_size_(0)
, video_threads_(0)
, video_is_image_sequence_(false)
, audio_enabled_(false)
, audio_bit_rate_(0)
, subtitles_enabled_(false)
, subtitles_are_sidecar_(false)
, video_scaling_method_(kStretch)
, has_custom_range_(false)
{
}
QDir EncodingParams::GetPresetPath()
{
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets"));
return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("exportpresets"));
}
QStringList EncodingParams::GetListOfPresets()
{
QDir d = EncodingParams::GetPresetPath();
return d.entryList(QDir::Files);
QDir d = EncodingParams::GetPresetPath();
return d.entryList(QDir::Files);
}
void EncodingParams::EnableVideo(const VideoParams &video_params, const ExportCodec::Codec &vcodec)
void EncodingParams::EnableVideo(const VideoParams &video_params,
const ExportCodec::Codec &vcodec)
{
video_enabled_ = true;
video_params_ = video_params;
video_codec_ = vcodec;
video_enabled_ = true;
video_params_ = video_params;
video_codec_ = vcodec;
}
void EncodingParams::EnableAudio(const AudioParams &audio_params, const ExportCodec::Codec &acodec)
void EncodingParams::EnableAudio(const AudioParams &audio_params,
const ExportCodec::Codec &acodec)
{
audio_enabled_ = true;
audio_params_ = audio_params;
audio_codec_ = acodec;
audio_enabled_ = true;
audio_params_ = audio_params;
audio_codec_ = acodec;
}
void EncodingParams::EnableSubtitles(const ExportCodec::Codec &scodec)
{
subtitles_enabled_ = true;
subtitles_codec_ = scodec;
subtitles_enabled_ = true;
subtitles_codec_ = scodec;
}
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec)
void EncodingParams::EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec)
{
subtitles_enabled_ = true;
subtitles_are_sidecar_ = true;
subtitle_sidecar_fmt_ = sfmt;
subtitles_codec_ = scodec;
subtitles_enabled_ = true;
subtitles_are_sidecar_ = true;
subtitle_sidecar_fmt_ = sfmt;
subtitles_codec_ = scodec;
}
void EncodingParams::DisableVideo()
{
video_enabled_ = false;
video_enabled_ = false;
}
void EncodingParams::DisableAudio()
{
audio_enabled_ = false;
audio_enabled_ = false;
}
void EncodingParams::DisableSubtitles()
{
subtitles_enabled_ = false;
subtitles_enabled_ = false;
}
bool EncodingParams::Load(QXmlStreamReader *reader)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("export")) {
int version = 0;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("export")) {
int version = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("version")) {
version = attr.value().toInt();
}
}
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("version")) {
version = attr.value().toInt();
}
}
switch (version) {
case 1:
return LoadV1(reader);
}
} else {
reader->skipCurrentElement();
}
}
switch (version) {
case 1:
return LoadV1(reader);
}
} else {
reader->skipCurrentElement();
}
}
return false;
return false;
}
bool EncodingParams::Load(QIODevice *device)
{
QXmlStreamReader reader(device);
return Load(&reader);
QXmlStreamReader reader(device);
return Load(&reader);
}
void EncodingParams::Save(QIODevice *device) const
{
QXmlStreamWriter writer(device);
Save(&writer);
QXmlStreamWriter writer(device);
Save(&writer);
}
void EncodingParams::Save(QXmlStreamWriter *writer) const
{
writer->writeStartDocument();
writer->writeStartDocument();
writer->writeStartElement(QStringLiteral("export"));
writer->writeStartElement(QStringLiteral("export"));
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion));
writer->writeAttribute(QStringLiteral("version"),
QString::number(kEncoderParamsVersion));
writer->writeTextElement(QStringLiteral("filename"), filename_);
writer->writeTextElement(QStringLiteral("format"), QString::number(format_));
writer->writeTextElement(QStringLiteral("filename"), filename_);
writer->writeTextElement(QStringLiteral("format"),
QString::number(format_));
writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_));
writer->writeTextElement(QStringLiteral("customrangein"), QString::fromStdString(custom_range_.in().toString()));
writer->writeTextElement(QStringLiteral("customrangeout"), QString::fromStdString(custom_range_.out().toString()));
writer->writeTextElement(QStringLiteral("range"),
QString::number(has_custom_range_));
writer->writeTextElement(
QStringLiteral("customrangein"),
QString::fromStdString(custom_range_.in().toString()));
writer->writeTextElement(
QStringLiteral("customrangeout"),
QString::fromStdString(custom_range_.out().toString()));
writer->writeStartElement(QStringLiteral("video"));
writer->writeStartElement(QStringLiteral("video"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(video_enabled_));
writer->writeAttribute(QStringLiteral("enabled"),
QString::number(video_enabled_));
if (video_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_));
writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width()));
writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height()));
writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format()));
writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(video_params_.pixel_aspect_ratio().toString()));
writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(video_params_.time_base().toString()));
writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider()));
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_));
writer->writeTextElement(QStringLiteral("minbitrate"), QString::number(video_min_bit_rate_));
writer->writeTextElement(QStringLiteral("maxbitrate"), QString::number(video_max_bit_rate_));
writer->writeTextElement(QStringLiteral("bufsize"), QString::number(video_buffer_size_));
writer->writeTextElement(QStringLiteral("threads"), QString::number(video_threads_));
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
writer->writeTextElement(QStringLiteral("imgseq"), QString::number(video_is_image_sequence_));
if (video_enabled_) {
writer->writeTextElement(QStringLiteral("codec"),
QString::number(video_codec_));
writer->writeTextElement(QStringLiteral("width"),
QString::number(video_params_.width()));
writer->writeTextElement(QStringLiteral("height"),
QString::number(video_params_.height()));
writer->writeTextElement(QStringLiteral("format"),
QString::number(video_params_.format()));
writer->writeTextElement(
QStringLiteral("pixelaspect"),
QString::fromStdString(
video_params_.pixel_aspect_ratio().toString()));
writer->writeTextElement(
QStringLiteral("timebase"),
QString::fromStdString(video_params_.time_base().toString()));
writer->writeTextElement(QStringLiteral("divider"),
QString::number(video_params_.divider()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(video_bit_rate_));
writer->writeTextElement(QStringLiteral("minbitrate"),
QString::number(video_min_bit_rate_));
writer->writeTextElement(QStringLiteral("maxbitrate"),
QString::number(video_max_bit_rate_));
writer->writeTextElement(QStringLiteral("bufsize"),
QString::number(video_buffer_size_));
writer->writeTextElement(QStringLiteral("threads"),
QString::number(video_threads_));
writer->writeTextElement(QStringLiteral("pixfmt"), video_pix_fmt_);
writer->writeTextElement(QStringLiteral("imgseq"),
QString::number(video_is_image_sequence_));
writer->writeStartElement(QStringLiteral("color"));
writer->writeTextElement(QStringLiteral("output"), color_transform_.output());
writer->writeEndElement(); // colortransform
writer->writeStartElement(QStringLiteral("color"));
writer->writeTextElement(QStringLiteral("output"),
color_transform_.output());
writer->writeEndElement(); // colortransform
writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
writer->writeTextElement(QStringLiteral("vscale"),
QString::number(video_scaling_method_));
if (!video_opts_.isEmpty()) {
writer->writeStartElement(QStringLiteral("opts"));
if (!video_opts_.isEmpty()) {
writer->writeStartElement(QStringLiteral("opts"));
QHash<QString, QString>::const_iterator i;
for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) {
writer->writeStartElement(QStringLiteral("entry"));
QHash<QString, QString>::const_iterator i;
for (i = video_opts_.constBegin(); i != video_opts_.constEnd();
i++) {
writer->writeStartElement(QStringLiteral("entry"));
writer->writeTextElement(QStringLiteral("key"), i.key());
writer->writeTextElement(QStringLiteral("value"), i.value());
writer->writeTextElement(QStringLiteral("key"), i.key());
writer->writeTextElement(QStringLiteral("value"), i.value());
writer->writeEndElement(); // entry
}
writer->writeEndElement(); // entry
}
writer->writeEndElement(); // opts
}
}
writer->writeEndElement(); // opts
}
}
writer->writeEndElement(); // video
writer->writeEndElement(); // video
writer->writeStartElement(QStringLiteral("audio"));
writer->writeStartElement(QStringLiteral("audio"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_));
writer->writeAttribute(QStringLiteral("enabled"),
QString::number(audio_enabled_));
if (audio_enabled_) {
writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_));
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate()));
if (audio_enabled_) {
writer->writeTextElement(QStringLiteral("codec"),
QString::number(audio_codec_));
writer->writeTextElement(QStringLiteral("samplerate"),
QString::number(audio_params_.sample_rate()));
writer->writeTextElement(QStringLiteral("channellayout"),
QString::number(audio_params().channel_layout().u.mask));
writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string()));
writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_));
}
writer->writeTextElement(
QStringLiteral("channellayout"),
QString::number(audio_params().channel_layout().u.mask));
writer->writeTextElement(
QStringLiteral("format"),
QString::fromStdString(audio_params_.format().to_string()));
writer->writeTextElement(QStringLiteral("bitrate"),
QString::number(audio_bit_rate_));
}
writer->writeStartElement(QStringLiteral("subtitles"));
writer->writeStartElement(QStringLiteral("subtitles"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(subtitles_enabled_));
writer->writeAttribute(QStringLiteral("enabled"),
QString::number(subtitles_enabled_));
if (subtitles_enabled_) {
writer->writeTextElement(QStringLiteral("sidecar"), QString::number(subtitles_are_sidecar_));
writer->writeTextElement(QStringLiteral("sidecarformat"), QString::number(subtitle_sidecar_fmt_));
if (subtitles_enabled_) {
writer->writeTextElement(QStringLiteral("sidecar"),
QString::number(subtitles_are_sidecar_));
writer->writeTextElement(QStringLiteral("sidecarformat"),
QString::number(subtitle_sidecar_fmt_));
writer->writeTextElement(QStringLiteral("codec"), QString::number(subtitles_codec_));
}
writer->writeTextElement(QStringLiteral("codec"),
QString::number(subtitles_codec_));
}
writer->writeEndElement(); // subtitles
writer->writeEndElement(); // subtitles
writer->writeEndElement(); // audio
writer->writeEndElement(); // audio
writer->writeEndElement(); // export
writer->writeEndElement(); // export
writer->writeEndDocument();
writer->writeEndDocument();
}
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params)
Encoder *Encoder::CreateFromID(Type id, const EncodingParams &params)
{
switch (id) {
case kEncoderTypeNone:
break;
case kEncoderTypeFFmpeg:
return new FFmpegEncoder(params);
case kEncoderTypeOIIO:
return new OIIOEncoder(params);
}
switch (id) {
case kEncoderTypeNone:
break;
case kEncoderTypeFFmpeg:
return new FFmpegEncoder(params);
case kEncoderTypeOIIO:
return new OIIOEncoder(params);
}
return nullptr;
return nullptr;
}
Encoder::Type Encoder::GetTypeFromFormat(ExportFormat::Format f)
{
switch (f) {
case ExportFormat::kFormatDNxHD:
case ExportFormat::kFormatMatroska:
case ExportFormat::kFormatQuickTime:
case ExportFormat::kFormatMPEG4Video:
case ExportFormat::kFormatMPEG4Audio:
case ExportFormat::kFormatWAV:
case ExportFormat::kFormatAIFF:
case ExportFormat::kFormatMP3:
case ExportFormat::kFormatFLAC:
case ExportFormat::kFormatOgg:
case ExportFormat::kFormatWebM:
case ExportFormat::kFormatSRT:
return kEncoderTypeFFmpeg;
case ExportFormat::kFormatOpenEXR:
case ExportFormat::kFormatPNG:
case ExportFormat::kFormatTIFF:
return kEncoderTypeOIIO;
case ExportFormat::kFormatCount:
break;
}
switch (f) {
case ExportFormat::kFormatDNxHD:
case ExportFormat::kFormatMatroska:
case ExportFormat::kFormatQuickTime:
case ExportFormat::kFormatMPEG4Video:
case ExportFormat::kFormatMPEG4Audio:
case ExportFormat::kFormatWAV:
case ExportFormat::kFormatAIFF:
case ExportFormat::kFormatMP3:
case ExportFormat::kFormatFLAC:
case ExportFormat::kFormatOgg:
case ExportFormat::kFormatWebM:
case ExportFormat::kFormatSRT:
return kEncoderTypeFFmpeg;
case ExportFormat::kFormatOpenEXR:
case ExportFormat::kFormatPNG:
case ExportFormat::kFormatTIFF:
return kEncoderTypeOIIO;
case ExportFormat::kFormatCount:
break;
}
return kEncoderTypeNone;
return kEncoderTypeNone;
}
Encoder *Encoder::CreateFromFormat(ExportFormat::Format f, const EncodingParams &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)
{
return CreateFromFormat(params.format(), params);
return CreateFromFormat(params.format(), params);
}
QStringList Encoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
{
return QStringList();
return QStringList();
}
std::vector<SampleFormat> Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
std::vector<SampleFormat>
Encoder::GetSampleFormatsForCodec(ExportCodec::Codec c) const
{
return std::vector<SampleFormat>();
return std::vector<SampleFormat>();
}
QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height)
QMatrix4x4
EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height)
{
QMatrix4x4 preview_matrix;
QMatrix4x4 preview_matrix;
if (method == EncodingParams::kStretch) {
return preview_matrix;
}
if (method == EncodingParams::kStretch) {
return preview_matrix;
}
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height);
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height);
float export_ar =
static_cast<float>(dest_width) / static_cast<float>(dest_height);
float source_ar =
static_cast<float>(source_width) / static_cast<float>(source_height);
if (qFuzzyCompare(export_ar, source_ar)) {
return preview_matrix;
}
if (qFuzzyCompare(export_ar, source_ar)) {
return preview_matrix;
}
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
preview_matrix.scale(source_ar / export_ar, 1.0F);
} else {
preview_matrix.scale(1.0F, export_ar / source_ar);
}
if ((export_ar > source_ar) == (method == EncodingParams::kFit)) {
preview_matrix.scale(source_ar / export_ar, 1.0F);
} else {
preview_matrix.scale(1.0F, export_ar / source_ar);
}
return preview_matrix;
return preview_matrix;
}
bool EncodingParams::LoadV1(QXmlStreamReader *reader)
{
rational custom_range_in, custom_range_out;
rational custom_range_in, custom_range_out;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("filename")) {
filename_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("format")) {
format_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("range")) {
has_custom_range_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("customrangein")) {
custom_range_in = rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("customrangeout")) {
custom_range_out = rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("video")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
video_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("filename")) {
filename_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("format")) {
format_ = static_cast<ExportFormat::Format>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("range")) {
has_custom_range_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("customrangein")) {
custom_range_in =
rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("customrangeout")) {
custom_range_out =
rational::fromString(reader->readElementText().toStdString());
} else if (reader->name() == QStringLiteral("video")) {
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) {
video_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
video_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("width")) {
video_params_.set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
video_params_.set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("format")) {
video_params_.set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("pixelaspect")) {
video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("timebase")) {
video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("divider")) {
video_params_.set_divider(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("bitrate")) {
video_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("minbitrate")) {
video_min_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("maxbitrate")) {
video_max_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("bufsize")) {
video_buffer_size_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("threads")) {
video_threads_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("pixfmt")) {
video_pix_fmt_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("imgseq")) {
video_is_image_sequence_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("color")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("output")) {
color_transform_ = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("vscale")) {
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("opts")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("entry")) {
QString key, value;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
key = reader->readElementText();
} else if (reader->name() == QStringLiteral("value")) {
value = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
set_video_option(key, value);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
video_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("width")) {
video_params_.set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
video_params_.set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("format")) {
video_params_.set_format(static_cast<PixelFormat::Format>(
reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("pixelaspect")) {
video_params_.set_pixel_aspect_ratio(rational::fromString(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("timebase")) {
video_params_.set_time_base(rational::fromString(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("divider")) {
video_params_.set_divider(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("bitrate")) {
video_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("minbitrate")) {
video_min_bit_rate_ =
reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("maxbitrate")) {
video_max_bit_rate_ =
reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("bufsize")) {
video_buffer_size_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("threads")) {
video_threads_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("pixfmt")) {
video_pix_fmt_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("imgseq")) {
video_is_image_sequence_ =
reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("color")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("output")) {
color_transform_ = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("vscale")) {
video_scaling_method_ = static_cast<VideoScalingMethod>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("opts")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("entry")) {
QString key, value;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
key = reader->readElementText();
} else if (reader->name() ==
QStringLiteral("value")) {
value = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
set_video_option(key, value);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
if (video_params_.pixel_aspect_ratio().isNull()) {
video_params_.set_pixel_aspect_ratio(1);
}
} else if (reader->name() == QStringLiteral("audio")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
audio_enabled_ = attr.value().toInt();
}
}
// HACK: Resolve bug where I forgot to serialize pixel aspect ratio
if (video_params_.pixel_aspect_ratio().isNull()) {
video_params_.set_pixel_aspect_ratio(1);
}
} else if (reader->name() == QStringLiteral("audio")) {
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) {
audio_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
audio_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("samplerate")) {
audio_params_.set_sample_rate(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("channellayout")) {
AVChannelLayout av_channel_layout;
av_channel_layout_from_mask(&av_channel_layout, reader->readElementText().toLongLong());
audio_params_.set_channel_layout(av_channel_layout);
av_channel_layout_uninit(&av_channel_layout);
} else if (reader->name() == QStringLiteral("format")) {
audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("bitrate")) {
audio_bit_rate_ = reader->readElementText().toLongLong();
} else {
reader->skipCurrentElement();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
audio_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("samplerate")) {
audio_params_.set_sample_rate(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("channellayout")) {
AVChannelLayout av_channel_layout;
av_channel_layout_from_mask(
&av_channel_layout,
reader->readElementText().toLongLong());
audio_params_.set_channel_layout(av_channel_layout);
av_channel_layout_uninit(&av_channel_layout);
} else if (reader->name() == QStringLiteral("format")) {
audio_params_.set_format(SampleFormat::from_string(
reader->readElementText().toStdString()));
} else if (reader->name() == QStringLiteral("bitrate")) {
audio_bit_rate_ = reader->readElementText().toLongLong();
} else {
reader->skipCurrentElement();
}
}
// HACK: Resolve bug where I forgot to serialize the audio bit rate
if (!audio_bit_rate_) {
audio_bit_rate_ = 320000;
}
} else if (reader->name() == QStringLiteral("subtitles")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
subtitles_enabled_ = attr.value().toInt();
}
}
// HACK: Resolve bug where I forgot to serialize the audio bit rate
if (!audio_bit_rate_) {
audio_bit_rate_ = 320000;
}
} else if (reader->name() == QStringLiteral("subtitles")) {
XMLAttributeLoop(reader, attr)
{
if (attr.name() == QStringLiteral("enabled")) {
subtitles_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sidecar")) {
subtitles_are_sidecar_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("sidecarformat")) {
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("codec")) {
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sidecar")) {
subtitles_are_sidecar_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("sidecarformat")) {
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("codec")) {
subtitles_codec_ = static_cast<ExportCodec::Codec>(
reader->readElementText().toInt());
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
return true;
return true;
}
}
+270 -148
View File
@@ -34,218 +34,340 @@
#include "render/subtitleparams.h"
#include "render/videoparams.h"
//这个代码也许是导出编码视频用的?
namespace olive {
namespace olive
{
class Encoder;
using EncoderPtr = std::shared_ptr<Encoder>;
class EncodingParams
{
class EncodingParams {
public:
enum VideoScalingMethod {
kFit,
kStretch,
kCrop
};
enum VideoScalingMethod { kFit, kStretch, kCrop };
EncodingParams();
EncodingParams();
static QDir GetPresetPath();
static QStringList GetListOfPresets();
static QDir GetPresetPath();
static QStringList GetListOfPresets();
bool IsValid() const
{
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
}
bool IsValid() const
{
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
}
void SetFilename(const QString& filename) { filename_ = filename; }
void SetFilename(const QString &filename)
{
filename_ = filename;
}
void EnableVideo(const VideoParams& video_params, const ExportCodec::Codec& vcodec);
void EnableAudio(const AudioParams& audio_params, const ExportCodec::Codec &acodec);
void EnableSubtitles(const ExportCodec::Codec &scodec);
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt, const ExportCodec::Codec &scodec);
void EnableVideo(const VideoParams &video_params,
const ExportCodec::Codec &vcodec);
void EnableAudio(const AudioParams &audio_params,
const ExportCodec::Codec &acodec);
void EnableSubtitles(const ExportCodec::Codec &scodec);
void EnableSidecarSubtitles(const ExportFormat::Format &sfmt,
const ExportCodec::Codec &scodec);
void DisableVideo();
void DisableAudio();
void DisableSubtitles();
void DisableVideo();
void DisableAudio();
void DisableSubtitles();
const ExportFormat::Format &format() const { return format_; }
void set_format(const ExportFormat::Format &format) { format_ = format; }
const ExportFormat::Format &format() const
{
return format_;
}
void set_format(const ExportFormat::Format &format)
{
format_ = format;
}
void set_video_option(const QString& key, const QString& value) { video_opts_.insert(key, value); }
void set_video_bit_rate(const int64_t& rate) { video_bit_rate_ = rate; }
void set_video_min_bit_rate(const int64_t& rate) { video_min_bit_rate_ = rate; }
void set_video_max_bit_rate(const int64_t& rate) { video_max_bit_rate_ = rate; }
void set_video_buffer_size(const int64_t& sz) { video_buffer_size_ = sz; }
void set_video_threads(const int& threads) { video_threads_ = threads; }
void set_video_pix_fmt(const QString& s) { video_pix_fmt_ = s; }
void set_video_is_image_sequence(bool s) { video_is_image_sequence_ = s; }
void set_color_transform(const ColorTransform& color_transform) { color_transform_ = color_transform; }
void set_video_option(const QString &key, const QString &value)
{
video_opts_.insert(key, value);
}
void set_video_bit_rate(const int64_t &rate)
{
video_bit_rate_ = rate;
}
void set_video_min_bit_rate(const int64_t &rate)
{
video_min_bit_rate_ = rate;
}
void set_video_max_bit_rate(const int64_t &rate)
{
video_max_bit_rate_ = rate;
}
void set_video_buffer_size(const int64_t &sz)
{
video_buffer_size_ = sz;
}
void set_video_threads(const int &threads)
{
video_threads_ = threads;
}
void set_video_pix_fmt(const QString &s)
{
video_pix_fmt_ = s;
}
void set_video_is_image_sequence(bool s)
{
video_is_image_sequence_ = s;
}
void set_color_transform(const ColorTransform &color_transform)
{
color_transform_ = color_transform;
}
const QString& filename() const { return filename_; }
const QString &filename() const
{
return filename_;
}
bool video_enabled() const { return video_enabled_; }
const ExportCodec::Codec& video_codec() const { return video_codec_; }
const VideoParams& video_params() const { return video_params_; }
const QHash<QString, QString>& video_opts() const { return video_opts_; }
QString video_option(const QString &key) const { return video_opts_.value(key); }
bool has_video_opt(const QString &key) const { return video_opts_.contains(key); }
const int64_t& video_bit_rate() const { return video_bit_rate_; }
const int64_t& video_min_bit_rate() const { return video_min_bit_rate_; }
const int64_t& video_max_bit_rate() const { return video_max_bit_rate_; }
const int64_t& video_buffer_size() const { return video_buffer_size_; }
const int& video_threads() const { return video_threads_; }
const QString& video_pix_fmt() const { return video_pix_fmt_; }
bool video_is_image_sequence() const { return video_is_image_sequence_; }
const ColorTransform& color_transform() const { return color_transform_; }
bool video_enabled() const
{
return video_enabled_;
}
const ExportCodec::Codec &video_codec() const
{
return video_codec_;
}
const VideoParams &video_params() const
{
return video_params_;
}
const QHash<QString, QString> &video_opts() const
{
return video_opts_;
}
QString video_option(const QString &key) const
{
return video_opts_.value(key);
}
bool has_video_opt(const QString &key) const
{
return video_opts_.contains(key);
}
const int64_t &video_bit_rate() const
{
return video_bit_rate_;
}
const int64_t &video_min_bit_rate() const
{
return video_min_bit_rate_;
}
const int64_t &video_max_bit_rate() const
{
return video_max_bit_rate_;
}
const int64_t &video_buffer_size() const
{
return video_buffer_size_;
}
const int &video_threads() const
{
return video_threads_;
}
const QString &video_pix_fmt() const
{
return video_pix_fmt_;
}
bool video_is_image_sequence() const
{
return video_is_image_sequence_;
}
const ColorTransform &color_transform() const
{
return color_transform_;
}
bool audio_enabled() const { return audio_enabled_; }
const ExportCodec::Codec &audio_codec() const { return audio_codec_; }
const AudioParams& audio_params() const { return audio_params_; }
const int64_t& audio_bit_rate() const { return audio_bit_rate_; }
bool audio_enabled() const
{
return audio_enabled_;
}
const ExportCodec::Codec &audio_codec() const
{
return audio_codec_;
}
const AudioParams &audio_params() const
{
return audio_params_;
}
const int64_t &audio_bit_rate() const
{
return audio_bit_rate_;
}
void set_audio_bit_rate(const int64_t& b) { audio_bit_rate_ = b; }
void set_audio_bit_rate(const int64_t &b)
{
audio_bit_rate_ = b;
}
bool subtitles_enabled() const { return subtitles_enabled_; }
bool subtitles_are_sidecar() const { return subtitles_are_sidecar_; }
ExportFormat::Format subtitle_sidecar_fmt() const { return subtitle_sidecar_fmt_; }
ExportCodec::Codec subtitles_codec() const { return subtitles_codec_; }
bool subtitles_enabled() const
{
return subtitles_enabled_;
}
bool subtitles_are_sidecar() const
{
return subtitles_are_sidecar_;
}
ExportFormat::Format subtitle_sidecar_fmt() const
{
return subtitle_sidecar_fmt_;
}
ExportCodec::Codec subtitles_codec() const
{
return subtitles_codec_;
}
const rational& GetExportLength() const { return export_length_; }
void SetExportLength(const rational& export_length) { export_length_ = export_length; }
const rational &GetExportLength() const
{
return export_length_;
}
void SetExportLength(const rational &export_length)
{
export_length_ = export_length;
}
bool Load(QIODevice *device);
bool Load(QXmlStreamReader *reader);
bool Load(QIODevice *device);
bool Load(QXmlStreamReader *reader);
void Save(QIODevice *device) const;
void Save(QXmlStreamWriter* writer) const;
void Save(QIODevice *device) const;
void Save(QXmlStreamWriter *writer) const;
bool has_custom_range() const { return has_custom_range_; }
const TimeRange& custom_range() const { return custom_range_; }
void set_custom_range(const TimeRange& custom_range)
{
has_custom_range_ = true;
custom_range_ = custom_range;
}
bool has_custom_range() const
{
return has_custom_range_;
}
const TimeRange &custom_range() const
{
return custom_range_;
}
void set_custom_range(const TimeRange &custom_range)
{
has_custom_range_ = true;
custom_range_ = custom_range;
}
const VideoScalingMethod& video_scaling_method() const { return video_scaling_method_; }
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method) { video_scaling_method_ = video_scaling_method; }
const VideoScalingMethod &video_scaling_method() const
{
return video_scaling_method_;
}
void
set_video_scaling_method(const VideoScalingMethod &video_scaling_method)
{
video_scaling_method_ = video_scaling_method;
}
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height);
static QMatrix4x4 GenerateMatrix(VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height);
private:
static const int kEncoderParamsVersion = 1;
static const int kEncoderParamsVersion = 1;
bool LoadV1(QXmlStreamReader *reader);
bool LoadV1(QXmlStreamReader *reader);
QString filename_;
ExportFormat::Format format_;
QString filename_;
ExportFormat::Format format_;
bool video_enabled_;
ExportCodec::Codec video_codec_;
VideoParams video_params_;
QHash<QString, QString> video_opts_;
int64_t video_bit_rate_;
int64_t video_min_bit_rate_;
int64_t video_max_bit_rate_;
int64_t video_buffer_size_;
int video_threads_;
QString video_pix_fmt_;
bool video_is_image_sequence_;
ColorTransform color_transform_;
bool video_enabled_;
ExportCodec::Codec video_codec_;
VideoParams video_params_;
QHash<QString, QString> video_opts_;
int64_t video_bit_rate_;
int64_t video_min_bit_rate_;
int64_t video_max_bit_rate_;
int64_t video_buffer_size_;
int video_threads_;
QString video_pix_fmt_;
bool video_is_image_sequence_;
ColorTransform color_transform_;
bool audio_enabled_;
ExportCodec::Codec audio_codec_;
AudioParams audio_params_;
int64_t audio_bit_rate_;
bool audio_enabled_;
ExportCodec::Codec audio_codec_;
AudioParams audio_params_;
int64_t audio_bit_rate_;
bool subtitles_enabled_;
bool subtitles_are_sidecar_;
ExportFormat::Format subtitle_sidecar_fmt_;
ExportCodec::Codec subtitles_codec_;
bool subtitles_enabled_;
bool subtitles_are_sidecar_;
ExportFormat::Format subtitle_sidecar_fmt_;
ExportCodec::Codec subtitles_codec_;
rational export_length_;
VideoScalingMethod video_scaling_method_;
bool has_custom_range_;
TimeRange custom_range_;
rational export_length_;
VideoScalingMethod video_scaling_method_;
bool has_custom_range_;
TimeRange custom_range_;
};
class Encoder : public QObject
{
Q_OBJECT
class Encoder : public QObject {
Q_OBJECT
public:
Encoder(const EncodingParams& params);
Encoder(const EncodingParams &params);
enum Type {
kEncoderTypeNone = -1,
kEncoderTypeFFmpeg,
kEncoderTypeOIIO
};
enum Type { kEncoderTypeNone = -1, kEncoderTypeFFmpeg, kEncoderTypeOIIO };
/**
/**
* @brief Create a Encoder instance using a Encoder ID
*
* @return
*
* A Encoder instance or nullptr if a Decoder with this ID does not exist
*/
static Encoder *CreateFromID(Type id, const EncodingParams &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 std::vector<SampleFormat> GetSampleFormatsForCodec(ExportCodec::Codec c) const;
virtual QStringList GetPixelFormatsForCodec(ExportCodec::Codec c) const;
virtual std::vector<SampleFormat>
GetSampleFormatsForCodec(ExportCodec::Codec c) const;
const EncodingParams& params() const;
const EncodingParams &params() const;
virtual PixelFormat GetDesiredPixelFormat() const
{
return PixelFormat::INVALID;
}
virtual PixelFormat GetDesiredPixelFormat() const
{
return PixelFormat::INVALID;
}
const QString& GetError() const
{
return error_;
}
const QString &GetError() const
{
return error_;
}
QString GetFilenameForFrame(const rational& frame);
QString GetFilenameForFrame(const rational &frame);
static int GetImageSequencePlaceholderDigitCount(const QString& filename);
static int GetImageSequencePlaceholderDigitCount(const QString &filename);
static bool FilenameContainsDigitPlaceholder(const QString &filename);
static QString FilenameRemoveDigitPlaceholder(QString filename);
static bool FilenameContainsDigitPlaceholder(const QString &filename);
static QString FilenameRemoveDigitPlaceholder(QString filename);
static const QRegularExpression kImageSequenceContainsDigits;
static const QRegularExpression kImageSequenceRemoveDigits;
static const QRegularExpression kImageSequenceContainsDigits;
static const QRegularExpression kImageSequenceRemoveDigits;
public slots:
virtual bool Open() = 0;
virtual bool Open() = 0;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) = 0;
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) = 0;
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
virtual void Close() = 0;
virtual void Close() = 0;
protected:
void SetError(const QString& err)
{
error_ = err;
}
void SetError(const QString &err)
{
error_ = err;
}
private:
EncodingParams params_;
QString error_;
EncodingParams params_;
QString error_;
};
}
+96 -95
View File
@@ -25,115 +25,116 @@ extern "C" {
#include <libavutil/pixdesc.h>
}
namespace olive {
namespace olive
{
QString ExportCodec::GetCodecName(ExportCodec::Codec c)
{
switch (c) {
case kCodecDNxHD:
return tr("DNxHD");
case kCodecH264:
return tr("H.264");
case kCodecH264rgb:
return tr("H.264 RGB");
case kCodecH265:
return tr("H.265");
case kCodecOpenEXR:
return tr("OpenEXR");
case kCodecPNG:
return tr("PNG");
case kCodecProRes:
return tr("ProRes");
case kCodecCineform:
return tr("Cineform");
case kCodecTIFF:
return tr("TIFF");
case kCodecMP2:
return tr("MP2");
case kCodecMP3:
return tr("MP3");
case kCodecAAC:
return tr("AAC");
case kCodecPCM:
return tr("PCM (Uncompressed)");
case kCodecFLAC:
return tr("FLAC");
case kCodecOpus:
return tr("Opus");
case kCodecVorbis:
return tr("Vorbis");
case kCodecVP9:
return tr("VP9");
case kCodecAV1:
return tr("AV1");
case kCodecSRT:
return tr("SubRip SRT");
case kCodecCount:
break;
}
switch (c) {
case kCodecDNxHD:
return tr("DNxHD");
case kCodecH264:
return tr("H.264");
case kCodecH264rgb:
return tr("H.264 RGB");
case kCodecH265:
return tr("H.265");
case kCodecOpenEXR:
return tr("OpenEXR");
case kCodecPNG:
return tr("PNG");
case kCodecProRes:
return tr("ProRes");
case kCodecCineform:
return tr("Cineform");
case kCodecTIFF:
return tr("TIFF");
case kCodecMP2:
return tr("MP2");
case kCodecMP3:
return tr("MP3");
case kCodecAAC:
return tr("AAC");
case kCodecPCM:
return tr("PCM (Uncompressed)");
case kCodecFLAC:
return tr("FLAC");
case kCodecOpus:
return tr("Opus");
case kCodecVorbis:
return tr("Vorbis");
case kCodecVP9:
return tr("VP9");
case kCodecAV1:
return tr("AV1");
case kCodecSRT:
return tr("SubRip SRT");
case kCodecCount:
break;
}
return tr("Unknown");
return tr("Unknown");
}
bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
{
switch (c) {
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecPCM:
case kCodecVorbis:
case kCodecOpus:
case kCodecFLAC:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
return false;
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
return true;
case kCodecCount:
break;
}
switch (c) {
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecPCM:
case kCodecVorbis:
case kCodecOpus:
case kCodecFLAC:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
return false;
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
return true;
case kCodecCount:
break;
}
return false;
return false;
}
bool ExportCodec::IsCodecLossless(Codec c)
{
switch (c) {
case kCodecPCM:
case kCodecFLAC:
return true;
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecVorbis:
case kCodecOpus:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
case kCodecCount:
break;
}
switch (c) {
case kCodecPCM:
case kCodecFLAC:
return true;
case kCodecDNxHD:
case kCodecH264:
case kCodecH264rgb:
case kCodecH265:
case kCodecProRes:
case kCodecCineform:
case kCodecMP2:
case kCodecMP3:
case kCodecAAC:
case kCodecVorbis:
case kCodecOpus:
case kCodecVP9:
case kCodecAV1:
case kCodecSRT:
case kCodecOpenEXR:
case kCodecPNG:
case kCodecTIFF:
case kCodecCount:
break;
}
return false;
return false;
}
}
+30 -31
View File
@@ -27,43 +27,42 @@
#include "common/define.h"
#include "render/subtitleparams.h"
namespace olive {
class ExportCodec : public QObject
namespace olive
{
Q_OBJECT
class ExportCodec : public QObject {
Q_OBJECT
public:
// Only append to this list (never insert) because indexes are used in serialized files
enum Codec {
kCodecDNxHD,
kCodecH264,
kCodecH264rgb,
kCodecH265,
kCodecOpenEXR,
kCodecPNG,
kCodecProRes,
kCodecCineform,
kCodecTIFF,
kCodecVP9,
kCodecMP2,
kCodecMP3,
kCodecAAC,
kCodecPCM,
kCodecOpus,
kCodecVorbis,
kCodecFLAC,
kCodecSRT,
kCodecAV1,
// Only append to this list (never insert) because indexes are used in serialized files
enum Codec {
kCodecDNxHD,
kCodecH264,
kCodecH264rgb,
kCodecH265,
kCodecOpenEXR,
kCodecPNG,
kCodecProRes,
kCodecCineform,
kCodecTIFF,
kCodecVP9,
kCodecMP2,
kCodecMP3,
kCodecAAC,
kCodecPCM,
kCodecOpus,
kCodecVorbis,
kCodecFLAC,
kCodecSRT,
kCodecAV1,
kCodecCount
};
kCodecCount
};
static QString GetCodecName(Codec c);
static QString GetCodecName(Codec c);
static bool IsCodecAStillImage(Codec c);
static bool IsCodecLossless(Codec c);
static bool IsCodecAStillImage(Codec c);
static bool IsCodecLossless(Codec c);
};
}
+184 -171
View File
@@ -22,214 +22,227 @@
#include "encoder.h"
namespace olive {
namespace olive
{
QString ExportFormat::GetName(olive::ExportFormat::Format f)
{
switch (f) {
case kFormatDNxHD:
return tr("DNxHD");
case kFormatMatroska:
return tr("Matroska Video");
case kFormatMPEG4Video:
return tr("MPEG-4 Video");
case kFormatMPEG4Audio:
return tr("MPEG-4 Audio");
case kFormatOpenEXR:
return tr("OpenEXR");
case kFormatPNG:
return tr("PNG");
case kFormatTIFF:
return tr("TIFF");
case kFormatQuickTime:
return tr("QuickTime");
case kFormatWAV:
return tr("Wave Audio");
case kFormatAIFF:
return tr("AIFF");
case kFormatMP3:
return tr("MP3");
case kFormatFLAC:
return tr("FLAC");
case kFormatOgg:
return tr("Ogg");
case kFormatWebM:
return tr("WebM");
case kFormatSRT:
return tr("SubRip SRT");
switch (f) {
case kFormatDNxHD:
return tr("DNxHD");
case kFormatMatroska:
return tr("Matroska Video");
case kFormatMPEG4Video:
return tr("MPEG-4 Video");
case kFormatMPEG4Audio:
return tr("MPEG-4 Audio");
case kFormatOpenEXR:
return tr("OpenEXR");
case kFormatPNG:
return tr("PNG");
case kFormatTIFF:
return tr("TIFF");
case kFormatQuickTime:
return tr("QuickTime");
case kFormatWAV:
return tr("Wave Audio");
case kFormatAIFF:
return tr("AIFF");
case kFormatMP3:
return tr("MP3");
case kFormatFLAC:
return tr("FLAC");
case kFormatOgg:
return tr("Ogg");
case kFormatWebM:
return tr("WebM");
case kFormatSRT:
return tr("SubRip SRT");
case kFormatCount:
break;
}
case kFormatCount:
break;
}
return tr("Unknown");
return tr("Unknown");
}
QString ExportFormat::GetExtension(ExportFormat::Format f)
{
switch (f) {
case kFormatDNxHD:
return QStringLiteral("mxf");
case kFormatMatroska:
return QStringLiteral("mkv");
case kFormatMPEG4Video:
return QStringLiteral("mp4");
case kFormatMPEG4Audio:
return QStringLiteral("m4a");
case kFormatOpenEXR:
return QStringLiteral("exr");
case kFormatPNG:
return QStringLiteral("png");
case kFormatTIFF:
return QStringLiteral("tiff");
case kFormatQuickTime:
return QStringLiteral("mov");
case kFormatWAV:
return QStringLiteral("wav");
case kFormatAIFF:
return QStringLiteral("aiff");
case kFormatMP3:
return QStringLiteral("mp3");
case kFormatFLAC:
return QStringLiteral("flac");
case kFormatOgg:
return QStringLiteral("ogg");
case kFormatWebM:
return QStringLiteral("webm");
case kFormatSRT:
return QStringLiteral("srt");
case kFormatCount:
break;
}
switch (f) {
case kFormatDNxHD:
return QStringLiteral("mxf");
case kFormatMatroska:
return QStringLiteral("mkv");
case kFormatMPEG4Video:
return QStringLiteral("mp4");
case kFormatMPEG4Audio:
return QStringLiteral("m4a");
case kFormatOpenEXR:
return QStringLiteral("exr");
case kFormatPNG:
return QStringLiteral("png");
case kFormatTIFF:
return QStringLiteral("tiff");
case kFormatQuickTime:
return QStringLiteral("mov");
case kFormatWAV:
return QStringLiteral("wav");
case kFormatAIFF:
return QStringLiteral("aiff");
case kFormatMP3:
return QStringLiteral("mp3");
case kFormatFLAC:
return QStringLiteral("flac");
case kFormatOgg:
return QStringLiteral("ogg");
case kFormatWebM:
return QStringLiteral("webm");
case kFormatSRT:
return QStringLiteral("srt");
case kFormatCount:
break;
}
return QString();
return QString();
}
QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
{
switch (f) {
case kFormatDNxHD:
return {ExportCodec::kCodecDNxHD};
case kFormatMatroska:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecVP9};
case kFormatMPEG4Video:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265};
case kFormatOpenEXR:
return {ExportCodec::kCodecOpenEXR};
case kFormatPNG:
return {ExportCodec::kCodecPNG};
case kFormatTIFF:
return {ExportCodec::kCodecTIFF};
case kFormatQuickTime:
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform};
case kFormatWebM:
return {ExportCodec::kCodecAV1, ExportCodec::kCodecVP9};
case kFormatOgg:
case kFormatWAV:
case kFormatMPEG4Audio:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatSRT:
case kFormatCount:
break;
}
switch (f) {
case kFormatDNxHD:
return { ExportCodec::kCodecDNxHD };
case kFormatMatroska:
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265, ExportCodec::kCodecVP9 };
case kFormatMPEG4Video:
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265 };
case kFormatOpenEXR:
return { ExportCodec::kCodecOpenEXR };
case kFormatPNG:
return { ExportCodec::kCodecPNG };
case kFormatTIFF:
return { ExportCodec::kCodecTIFF };
case kFormatQuickTime:
return { ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb,
ExportCodec::kCodecH265, ExportCodec::kCodecProRes,
ExportCodec::kCodecCineform };
case kFormatWebM:
return { ExportCodec::kCodecAV1, ExportCodec::kCodecVP9 };
case kFormatOgg:
case kFormatWAV:
case kFormatMPEG4Audio:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatSRT:
case kFormatCount:
break;
}
return {};
return {};
}
QList<ExportCodec::Codec> ExportFormat::GetAudioCodecs(ExportFormat::Format f)
{
switch (f) {
// Video/audio formats
case kFormatDNxHD:
return {ExportCodec::kCodecPCM};
case kFormatMatroska:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus, ExportCodec::kCodecFLAC};
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3};
case kFormatQuickTime:
return {ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM};
case kFormatWebM:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecAAC, ExportCodec::kCodecMP2, ExportCodec::kCodecMP3, ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis};
switch (f) {
// Video/audio formats
case kFormatDNxHD:
return { ExportCodec::kCodecPCM };
case kFormatMatroska:
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM,
ExportCodec::kCodecVorbis, ExportCodec::kCodecOpus,
ExportCodec::kCodecFLAC };
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3 };
case kFormatQuickTime:
return { ExportCodec::kCodecAAC, ExportCodec::kCodecMP2,
ExportCodec::kCodecMP3, ExportCodec::kCodecPCM };
case kFormatWebM:
return { ExportCodec::kCodecOpus, ExportCodec::kCodecAAC,
ExportCodec::kCodecMP2, ExportCodec::kCodecMP3,
ExportCodec::kCodecPCM, ExportCodec::kCodecVorbis };
// Audio only formats
case kFormatWAV:
return {ExportCodec::kCodecPCM};
case kFormatAIFF:
return {ExportCodec::kCodecPCM};
case kFormatMP3:
return {ExportCodec::kCodecMP3};
case kFormatFLAC:
return {ExportCodec::kCodecFLAC};
case kFormatOgg:
return {ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis, ExportCodec::kCodecPCM};
// Audio only formats
case kFormatWAV:
return { ExportCodec::kCodecPCM };
case kFormatAIFF:
return { ExportCodec::kCodecPCM };
case kFormatMP3:
return { ExportCodec::kCodecMP3 };
case kFormatFLAC:
return { ExportCodec::kCodecFLAC };
case kFormatOgg:
return { ExportCodec::kCodecOpus, ExportCodec::kCodecVorbis,
ExportCodec::kCodecPCM };
// Video only formats
case kFormatOpenEXR:
case kFormatPNG:
case kFormatTIFF:
case kFormatSRT:
case kFormatCount:
break;
// Video only formats
case kFormatOpenEXR:
case kFormatPNG:
case kFormatTIFF:
case kFormatSRT:
case kFormatCount:
break;
}
}
return {};
return {};
}
QList<ExportCodec::Codec> ExportFormat::GetSubtitleCodecs(Format f)
{
switch (f) {
case kFormatDNxHD:
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
case kFormatOpenEXR:
case kFormatQuickTime:
case kFormatPNG:
case kFormatTIFF:
case kFormatWAV:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatOgg:
case kFormatWebM:
case kFormatCount:
break;
case kFormatMatroska:
case kFormatSRT:
return {ExportCodec::kCodecSRT};
}
switch (f) {
case kFormatDNxHD:
case kFormatMPEG4Video:
case kFormatMPEG4Audio:
case kFormatOpenEXR:
case kFormatQuickTime:
case kFormatPNG:
case kFormatTIFF:
case kFormatWAV:
case kFormatAIFF:
case kFormatMP3:
case kFormatFLAC:
case kFormatOgg:
case kFormatWebM:
case kFormatCount:
break;
case kFormatMatroska:
case kFormatSRT:
return { ExportCodec::kCodecSRT };
}
return {};
return {};
}
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f, ExportCodec::Codec c)
QStringList ExportFormat::GetPixelFormatsForCodec(ExportFormat::Format f,
ExportCodec::Codec c)
{
Encoder* e = Encoder::CreateFromFormat(f, EncodingParams());
QStringList list;
Encoder *e = Encoder::CreateFromFormat(f, EncodingParams());
QStringList list;
if (e) {
list = e->GetPixelFormatsForCodec(c);
delete e;
}
if (e) {
list = e->GetPixelFormatsForCodec(c);
delete e;
}
return list;
return list;
}
std::vector<SampleFormat> ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
std::vector<SampleFormat>
ExportFormat::GetSampleFormatsForCodec(Format format, ExportCodec::Codec c)
{
std::vector<SampleFormat> f;
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
std::vector<SampleFormat> f;
Encoder *e = Encoder::CreateFromFormat(format, EncodingParams());
if (e) {
f = e->GetSampleFormatsForCodec(c);
delete e;
}
if (e) {
f = e->GetSampleFormatsForCodec(c);
delete e;
}
return f;
return f;
}
}
+31 -31
View File
@@ -27,42 +27,42 @@
#include "common/define.h"
#include "exportcodec.h"
namespace olive {
class ExportFormat : public QObject
namespace olive
{
Q_OBJECT
class ExportFormat : public QObject {
Q_OBJECT
public:
// Only append to this list (never insert) because indexes are used in serialized files
enum Format {
kFormatDNxHD,
kFormatMatroska,
kFormatMPEG4Video,
kFormatOpenEXR,
kFormatQuickTime,
kFormatPNG,
kFormatTIFF,
kFormatWAV,
kFormatAIFF,
kFormatMP3,
kFormatFLAC,
kFormatOgg,
kFormatWebM,
kFormatSRT,
kFormatMPEG4Audio,
// Only append to this list (never insert) because indexes are used in serialized files
enum Format {
kFormatDNxHD,
kFormatMatroska,
kFormatMPEG4Video,
kFormatOpenEXR,
kFormatQuickTime,
kFormatPNG,
kFormatTIFF,
kFormatWAV,
kFormatAIFF,
kFormatMP3,
kFormatFLAC,
kFormatOgg,
kFormatWebM,
kFormatSRT,
kFormatMPEG4Audio,
kFormatCount
};
kFormatCount
};
static QString GetName(Format f);
static QString GetExtension(Format f);
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
static std::vector<SampleFormat> GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
static QString GetName(Format f);
static QString GetExtension(Format f);
static QList<ExportCodec::Codec> GetVideoCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetAudioCodecs(ExportFormat::Format f);
static QList<ExportCodec::Codec> GetSubtitleCodecs(ExportFormat::Format f);
static QStringList GetPixelFormatsForCodec(Format f, ExportCodec::Codec c);
static std::vector<SampleFormat>
GetSampleFormatsForCodec(Format f, ExportCodec::Codec c);
};
}
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 "common/ffmpegutils.h"
namespace olive {
namespace olive
{
/**
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
*/
class FFmpegDecoder : public Decoder
{
Q_OBJECT
class FFmpegDecoder : public Decoder {
Q_OBJECT
public:
// Constructor
FFmpegDecoder();
// Constructor
FFmpegDecoder();
// Destructor
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
// Destructor
DECODER_DEFAULT_DESTRUCTOR(FFmpegDecoder)
virtual QString id() const override;
virtual QString id() const override;
virtual bool SupportsVideo() override{return true;}
virtual bool SupportsAudio() override{return true;}
virtual bool SupportsVideo() override
{
return true;
}
virtual bool SupportsAudio() override
{
return true;
}
virtual FootageDescription Probe(const QString &filename, CancelAtom *cancelled) const override;
virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected:
virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled) override;
virtual void CloseInternal() override;
virtual bool OpenInternal() override;
virtual TexturePtr
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled) override;
virtual void CloseInternal() override;
virtual rational GetAudioStartOffset() const override;
virtual rational GetAudioStartOffset() const override;
private:
class Instance
{
public:
Instance();
class Instance {
public:
Instance();
~Instance()
{
Close();
}
~Instance()
{
Close();
}
bool Open(const char* filename, int stream_index);
bool Open(const char *filename, int stream_index);
bool IsOpen() const
{
return fmt_ctx_;
}
bool IsOpen() const
{
return fmt_ctx_;
}
void Close();
void Close();
/**
/**
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
*
* @return
*
* An FFmpeg error code, or >= 0 on success
*/
int GetFrame(AVPacket* pkt, AVFrame* frame);
int GetFrame(AVPacket *pkt, AVFrame *frame);
const char *GetSubtitleHeader() const;
const char *GetSubtitleHeader() const;
int GetSubtitle(AVPacket* pkt, AVSubtitle* sub);
int GetSubtitle(AVPacket *pkt, AVSubtitle *sub);
int GetPacket(AVPacket *pkt);
int GetPacket(AVPacket *pkt);
void Seek(int64_t timestamp);
void Seek(int64_t timestamp);
AVFormatContext* fmt_ctx() const
{
return fmt_ctx_;
}
AVFormatContext *fmt_ctx() const
{
return fmt_ctx_;
}
AVStream* avstream() const
{
return avstream_;
}
AVStream *avstream() const
{
return avstream_;
}
private:
AVFormatContext* fmt_ctx_;
AVCodecContext* codec_ctx_;
AVStream* avstream_;
AVDictionary* opts_;
private:
AVFormatContext *fmt_ctx_;
AVCodecContext *codec_ctx_;
AVStream *avstream_;
AVDictionary *opts_;
};
};
/**
/**
* @brief Handle an FFmpeg error code
*
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
@@ -132,54 +140,56 @@ private:
*
* @param error_code
*/
static QString FFmpegError(int error_code);
static QString FFmpegError(int error_code);
void FreeScaler();
void FreeScaler();
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
static PixelFormat GetNativePixelFormat(AVPixelFormat pix_fmt);
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
static AVChannelLayout ValidateChannelLayout(AVStream *stream);
static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
static const char *
GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing);
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
static bool IsPixelFormatGLSLCompatible(AVPixelFormat f);
AVFramePtr GetFrameFromCache(const int64_t &t) const;
AVFramePtr GetFrameFromCache(const int64_t &t) const;
void ClearFrameCache();
void ClearFrameCache();
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
AVFramePtr PreProcessFrame(AVFramePtr f, const RetrieveVideoParams &p);
TexturePtr ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVideoParams &p, const AVFramePtr original);
TexturePtr ProcessFrameIntoTexture(AVFramePtr f,
const RetrieveVideoParams &p,
const AVFramePtr original);
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
void RemoveFirstFrame();
void RemoveFirstFrame();
static int MaximumQueueSize();
static int MaximumQueueSize();
SwsContext *sws_ctx_;
int sws_src_width_;
int sws_src_height_;
AVPixelFormat sws_src_format_;
int sws_dst_width_;
int sws_dst_height_;
AVPixelFormat sws_dst_format_;
AVColorRange sws_colrange_;
AVColorSpace sws_colspace_;
SwsContext *sws_ctx_;
int sws_src_width_;
int sws_src_height_;
AVPixelFormat sws_src_format_;
int sws_dst_width_;
int sws_dst_height_;
AVPixelFormat sws_dst_format_;
AVColorRange sws_colrange_;
AVColorSpace sws_colspace_;
AVPacket *working_packet_;
AVPacket *working_packet_;
int64_t second_ts_;
int64_t second_ts_;
std::list<AVFramePtr> cached_frames_;
std::list<AVFramePtr> cached_frames_;
bool cache_at_zero_;
bool cache_at_eof_;
Instance instance_;
bool cache_at_zero_;
bool cache_at_eof_;
Instance instance_;
};
}
File diff suppressed because it is too large Load Diff
+54 -45
View File
@@ -31,37 +31,41 @@ extern "C" {
#include "codec/encoder.h"
namespace olive {
class FFmpegEncoder : public Encoder
namespace olive
{
Q_OBJECT
class FFmpegEncoder : public Encoder {
Q_OBJECT
public:
FFmpegEncoder(const EncodingParams &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
{
return video_conversion_fmt_;
}
virtual PixelFormat GetDesiredPixelFormat() const override
{
return video_conversion_fmt_;
}
private:
/**
/**
* @brief Handle an FFmpeg error code
*
* Uses the FFmpeg API to retrieve a descriptive string for this error code and sends it to Error(). As such, this
@@ -69,43 +73,48 @@ private:
*
* @param error_code
*/
void FFmpegError(const QString &context, int error_code);
void FFmpegError(const QString &context, int error_code);
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream);
bool WriteAVFrame(AVFrame *frame, AVCodecContext *codec_ctx,
AVStream *stream);
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec);
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, const AVCodec* codec);
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, const AVCodec *codec);
bool InitializeStream(enum AVMediaType type, AVStream **stream,
AVCodecContext **codec_ctx,
const ExportCodec::Codec &codec);
bool InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx,
const AVCodec *codec);
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx,
const AVCodec *codec);
void FlushEncoders();
void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream);
void FlushEncoders();
void FlushCodecCtx(AVCodecContext *codec_ctx, AVStream *stream);
bool InitializeResampleContext(const AudioParams &audio);
bool InitializeResampleContext(const AudioParams &audio);
static const AVCodec *GetEncoder(ExportCodec::Codec c, SampleFormat aformat);
static const AVCodec *GetEncoder(ExportCodec::Codec c,
SampleFormat aformat);
AVFormatContext* fmt_ctx_;
AVFormatContext *fmt_ctx_;
AVStream* video_stream_;
AVCodecContext* video_codec_ctx_;
AVFilterGraph *video_scale_ctx_;
AVFilterContext *video_buffersrc_ctx_;
AVFilterContext *video_buffersink_ctx_;
PixelFormat video_conversion_fmt_;
AVStream *video_stream_;
AVCodecContext *video_codec_ctx_;
AVFilterGraph *video_scale_ctx_;
AVFilterContext *video_buffersrc_ctx_;
AVFilterContext *video_buffersink_ctx_;
PixelFormat video_conversion_fmt_;
AVStream* audio_stream_;
AVCodecContext* audio_codec_ctx_;
SwrContext* audio_resample_ctx_;
AVFrame* audio_frame_;
int audio_max_samples_;
int audio_frame_offset_;
int audio_write_count_;
AVStream *audio_stream_;
AVCodecContext *audio_codec_ctx_;
SwrContext *audio_resample_ctx_;
AVFrame *audio_frame_;
int audio_max_samples_;
int audio_frame_offset_;
int audio_write_count_;
AVStream* subtitle_stream_;
AVCodecContext* subtitle_codec_ctx_;
bool open_;
AVStream *subtitle_stream_;
AVCodecContext *subtitle_codec_ctx_;
bool open_;
};
}
+87 -79
View File
@@ -28,153 +28,161 @@
#include "common/oiioutils.h"
#include "render/framemanager.h"
namespace olive {
namespace olive
{
Frame::Frame() :
data_(nullptr),
data_size_(0),
timestamp_(0)
Frame::Frame()
: data_(nullptr)
, data_size_(0)
, timestamp_(0)
{
}
Frame::~Frame()
{
destroy();
destroy();
}
FramePtr Frame::Create()
{
return std::make_shared<Frame>();
return std::make_shared<Frame>();
}
const VideoParams &Frame::video_params() const
{
return params_;
return params_;
}
void Frame::set_video_params(const VideoParams &params)
{
params_ = params;
params_ = params;
linesize_ = generate_linesize_bytes(width(), params_.format(), params_.channel_count());
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
linesize_ = generate_linesize_bytes(width(), params_.format(),
params_.channel_count());
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
}
FramePtr Frame::Interlace(FramePtr top, FramePtr bottom)
{
if (top->video_params() != bottom->video_params()) {
qCritical() << "Tried to interlace two frames that had incompatible parameters";
return nullptr;
}
if (top->video_params() != bottom->video_params()) {
qCritical()
<< "Tried to interlace two frames that had incompatible parameters";
return nullptr;
}
FramePtr interlaced = Frame::Create();
interlaced->set_video_params(top->video_params());
interlaced->allocate();
FramePtr interlaced = Frame::Create();
interlaced->set_video_params(top->video_params());
interlaced->allocate();
int linesize = interlaced->linesize_bytes();
int linesize = interlaced->linesize_bytes();
for (int i=0; i<interlaced->height(); i++) {
FramePtr which = (i%2 == 0) ? top : bottom;
for (int i = 0; i < interlaced->height(); i++) {
FramePtr which = (i % 2 == 0) ? top : bottom;
memcpy(interlaced->data() + i*linesize,
which->const_data() + i*linesize,
linesize);
}
memcpy(interlaced->data() + i * linesize,
which->const_data() + i * linesize, linesize);
}
return interlaced;
return interlaced;
}
int Frame::generate_linesize_bytes(int width, PixelFormat format, int channel_count)
int Frame::generate_linesize_bytes(int width, PixelFormat format,
int channel_count)
{
// Align to 32 bytes (not sure if this is necessary?)
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31);
// Align to 32 bytes (not sure if this is necessary?)
return VideoParams::GetBytesPerPixel(format, channel_count) *
((width + 31) & ~31);
}
Color Frame::get_pixel(int x, int y) const
{
if (!contains_pixel(x, y)) {
return Color();
}
if (!contains_pixel(x, y)) {
return Color();
}
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
return Color(reinterpret_cast<const char*>(data_ + byte_offset), video_params().format(), video_params().channel_count());
return Color(reinterpret_cast<const char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
}
bool Frame::contains_pixel(int x, int y) const
{
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
}
void Frame::set_pixel(int x, int y, const Color &c)
{
if (!contains_pixel(x, y)) {
return;
}
if (!contains_pixel(x, y)) {
return;
}
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
int byte_offset =
y * linesize_bytes() + x * video_params().GetBytesPerPixel();
c.toData(reinterpret_cast<char*>(data_ + byte_offset), video_params().format(), video_params().channel_count());
c.toData(reinterpret_cast<char *>(data_ + byte_offset),
video_params().format(), video_params().channel_count());
}
bool Frame::allocate()
{
// Assume this frame is intended to be a video frame
if (!params_.is_valid()) {
qWarning() << "Tried to allocate a frame with invalid parameters";
return false;
}
// Assume this frame is intended to be a video frame
if (!params_.is_valid()) {
qWarning() << "Tried to allocate a frame with invalid parameters";
return false;
}
if (is_allocated()) {
// Already allocated
return true;
}
if (is_allocated()) {
// Already allocated
return true;
}
data_size_ = linesize_ * height();
data_ = FrameManager::Allocate(data_size_);
data_size_ = linesize_ * height();
data_ = FrameManager::Allocate(data_size_);
return true;
return true;
}
void Frame::destroy()
{
if (is_allocated()) {
FrameManager::Deallocate(data_size_, data_);
if (is_allocated()) {
FrameManager::Deallocate(data_size_, data_);
data_size_ = 0;
data_ = nullptr;
}
data_size_ = 0;
data_ = nullptr;
}
}
FramePtr Frame::convert(PixelFormat format) const
{
// Create new params with destination format
VideoParams params = params_;
params.set_format(format);
// Create new params with destination format
VideoParams params = params_;
params.set_format(format);
// Create new frame
FramePtr converted = Frame::Create();
converted->set_video_params(params);
converted->set_timestamp(timestamp_);
converted->allocate();
// Create new frame
FramePtr converted = Frame::Create();
converted->set_video_params(params);
converted->set_timestamp(timestamp_);
converted->allocate();
// Do the conversion through OIIO for convenience
OIIO::ImageBuf src(OIIO::ImageSpec(width(), height(),
channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
// Do the conversion through OIIO for convenience
OIIO::ImageBuf src(
OIIO::ImageSpec(width(), height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
OIIOUtils::FrameToBuffer(this, &src);
OIIOUtils::FrameToBuffer(this, &src);
OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(),
channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
OIIO::ImageBuf dst(OIIO::ImageSpec(
converted->width(), converted->height(), channel_count(),
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
if (dst.copy_pixels(src)) {
OIIOUtils::BufferToFrame(&dst, converted.get());
return converted;
} else {
return nullptr;
}
if (dst.copy_pixels(src)) {
OIIOUtils::BufferToFrame(&dst, converted.get());
return converted;
} else {
return nullptr;
}
}
}
+79 -79
View File
@@ -28,7 +28,8 @@
#include "common/define.h"
#include "render/videoparams.h"
namespace olive {
namespace olive
{
class Frame;
using FramePtr = std::shared_ptr<Frame>;
@@ -36,135 +37,134 @@ using FramePtr = std::shared_ptr<Frame>;
/**
* @brief Video frame data or audio sample data from a Decoder
*/
class Frame
{
class Frame {
public:
Frame();
Frame();
~Frame();
~Frame();
DISABLE_COPY_MOVE(Frame)
DISABLE_COPY_MOVE(Frame)
static FramePtr Create();
static FramePtr Create();
const VideoParams& video_params() const;
void set_video_params(const VideoParams& params);
const VideoParams &video_params() const;
void set_video_params(const VideoParams &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
{
return linesize_pixels_;
}
int linesize_pixels() const
{
return linesize_pixels_;
}
int linesize_bytes() const
{
return linesize_;
}
int linesize_bytes() const
{
return linesize_;
}
int width() const
{
return params_.effective_width();
}
int width() const
{
return params_.effective_width();
}
int height() const
{
return params_.effective_height();
}
int height() const
{
return params_.effective_height();
}
PixelFormat format() const
{
return params_.format();
}
PixelFormat format() const
{
return params_.format();
}
int channel_count() const
{
return params_.channel_count();
}
int channel_count() const
{
return params_.channel_count();
}
Color get_pixel(int x, int y) const;
bool contains_pixel(int x, int y) const;
void set_pixel(int x, int y, const Color& c);
Color get_pixel(int x, int y) const;
bool contains_pixel(int x, int y) const;
void set_pixel(int x, int y, const Color &c);
/**
/**
* @brief Get frame's timestamp.
*
* This timestamp is always a rational that will equate to the time in seconds.
*/
const rational& timestamp() const
{
return timestamp_;
}
const rational &timestamp() const
{
return timestamp_;
}
void set_timestamp(const rational& timestamp)
{
timestamp_ = timestamp;
}
void set_timestamp(const rational &timestamp)
{
timestamp_ = timestamp;
}
/**
/**
* @brief Get the data buffer of this frame
*/
char* data()
{
return data_;
}
char *data()
{
return data_;
}
/**
/**
* @brief Get the const data buffer of this frame
*/
const char* const_data() const
{
return data_;
}
const char *const_data() const
{
return data_;
}
/**
/**
* @brief Allocate memory buffer to store data based on parameters
*
* For video frames, the width(), height(), and format() must be set for this function to work.
*
* If a memory buffer has been previously allocated without destroying, this function will destroy it.
*/
bool allocate();
bool allocate();
/**
/**
* @brief Return whether the frame is allocated or not
*/
bool is_allocated() const
{
return data_;
}
bool is_allocated() const
{
return data_;
}
/**
/**
* @brief Destroy a memory buffer allocated with allocate()
*/
void destroy();
void destroy();
/**
/**
* @brief Returns the size of the array returned in data() in bytes
*
* Returns 0 if nothing is allocated.
*/
int allocated_size() const
{
return data_size_;
}
int allocated_size() const
{
return data_size_;
}
FramePtr convert(PixelFormat format) const;
FramePtr convert(PixelFormat format) const;
private:
VideoParams params_;
VideoParams params_;
char* data_;
int data_size_;
char *data_;
int data_size_;
rational timestamp_;
rational timestamp_;
int linesize_;
int linesize_pixels_;
int linesize_;
int linesize_pixels_;
};
}
+150 -133
View File
@@ -31,217 +31,234 @@
#include "core.h"
#include "render/renderer.h"
namespace olive {
namespace olive
{
QStringList OIIODecoder::supported_formats_;
OIIODecoder::OIIODecoder() :
image_(nullptr)
OIIODecoder::OIIODecoder()
: image_(nullptr)
{
}
QString OIIODecoder::id() const
{
return QStringLiteral("oiio");
return QStringLiteral("oiio");
}
FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cancelled) const
FootageDescription OIIODecoder::Probe(const QString &filename,
CancelAtom *cancelled) const
{
Q_UNUSED(cancelled)
Q_UNUSED(cancelled)
FootageDescription desc(id());
FootageDescription desc(id());
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
// to open a file that it can't if it's given one
if (!FileTypeIsSupported(filename)) {
return desc;
}
// Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying
// to open a file that it can't if it's given one
if (!FileTypeIsSupported(filename)) {
return desc;
}
std::string std_filename = filename.toStdString();
std::string std_filename = filename.toStdString();
auto in = OIIO::ImageInput::open(std_filename);
auto in = OIIO::ImageInput::open(std_filename);
if (!in) {
return desc;
}
if (!in) {
return desc;
}
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
// it better
if (!strcmp(in->format_name(), "FFmpeg movie")) {
return desc;
}
// Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle
// it better
if (!strcmp(in->format_name(), "FFmpeg movie")) {
return desc;
}
bool stream_enabled = true;
bool stream_enabled = true;
int i;
for (i=0; in->seek_subimage(i, 0); i++) {
OIIO::ImageSpec spec = in->spec();
int i;
for (i = 0; in->seek_subimage(i, 0); i++) {
OIIO::ImageSpec spec = in->spec();
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
video_params.set_stream_index(i);
video_params.set_stream_index(i);
if (i > 1) {
// This is a multilayer image and this image might have an offset
OIIO::ImageSpec root_spec = in->spec(0);
if (i > 1) {
// This is a multilayer image and this image might have an offset
OIIO::ImageSpec root_spec = in->spec(0);
float norm_x = spec.x + float(spec.width)*0.5f - float(root_spec.width)*0.5f;
float norm_y = spec.y + float(spec.height)*0.5f - float(root_spec.height)*0.5f;
float norm_x = spec.x + float(spec.width) * 0.5f -
float(root_spec.width) * 0.5f;
float norm_y = spec.y + float(spec.height) * 0.5f -
float(root_spec.height) * 0.5f;
video_params.set_x(norm_x);
video_params.set_y(norm_y);
}
video_params.set_x(norm_x);
video_params.set_y(norm_y);
}
// By default, only enable the first subimage (presumably the combined image). Later we will
// ask the user if they want to enable the layers instead.
video_params.set_enabled(stream_enabled);
stream_enabled = false;
// By default, only enable the first subimage (presumably the combined image). Later we will
// ask the user if they want to enable the layers instead.
video_params.set_enabled(stream_enabled);
stream_enabled = false;
// OIIO automatically premultiplies alpha
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
// likely reduces the fidelity?
video_params.set_premultiplied_alpha(true);
// OIIO automatically premultiplies alpha
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
// likely reduces the fidelity?
video_params.set_premultiplied_alpha(true);
desc.AddVideoStream(video_params);
}
desc.AddVideoStream(video_params);
}
desc.SetStreamCount(i);
desc.SetStreamCount(i);
// If we're here, we have a successful image open
in->close();
// If we're here, we have a successful image open
in->close();
return desc;
return desc;
}
bool OIIODecoder::OpenInternal()
{
// If we can open the filename provided, assume everything is working
return OpenImageHandler(stream().filename(), stream().stream());
// If we can open the filename provided, assume everything is working
return OpenImageHandler(stream().filename(), stream().stream());
}
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(p.divider);
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(p.divider);
if (!buffer_.is_allocated()
|| last_params_.divider != p.divider) {
last_params_ = p;
if (!buffer_.is_allocated() || last_params_.divider != p.divider) {
last_params_ = p;
buffer_.destroy();
buffer_.set_video_params(vp);
buffer_.allocate();
buffer_.destroy();
buffer_.set_video_params(vp);
buffer_.allocate();
if (p.divider == 1) {
// Just upload straight to the buffer
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
} else {
OIIO::ImageBuf buf(image_->spec());
image_->read_image(0,0,0,-1,image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride());
if (p.divider == 1) {
// Just upload straight to the buffer
image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data());
} else {
OIIO::ImageBuf buf(image_->spec());
image_->read_image(0, 0, 0, -1, image_->spec().format,
buf.localpixels(), buf.pixel_stride(),
buf.scanline_stride(), buf.z_stride());
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
int px_sz = vp.GetBytesPerPixel();
for (int dst_y=0; dst_y<buffer_.height(); dst_y++) {
int src_y = dst_y * buf.spec().height / buffer_.height();
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
int px_sz = vp.GetBytesPerPixel();
for (int dst_y = 0; dst_y < buffer_.height(); dst_y++) {
int src_y = dst_y * buf.spec().height / buffer_.height();
for (int dst_x=0; dst_x<buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x,
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x,
px_sz);
}
}
}
}
for (int dst_x = 0; dst_x < buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y +
px_sz * dst_x,
static_cast<uint8_t *>(buf.localpixels()) +
buf.scanline_stride() * src_y + px_sz * src_x,
px_sz);
}
}
}
}
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
return p.renderer->CreateTexture(vp, buffer_.data(),
buffer_.linesize_pixels());
}
void OIIODecoder::CloseInternal()
{
CloseImageHandle();
CloseImageHandle();
}
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
bool OIIODecoder::FileTypeIsSupported(const QString &fn)
{
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
// "extension_list" attribute and match it with the extension of the file.
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
// will segfault entirely if given unexpected data (an MPEG-4 for instance). To workaround this issue, we use OIIO's
// "extension_list" attribute and match it with the extension of the file.
// Check if we've created the supported formats list, create it if not
if (supported_formats_.isEmpty()) {
QStringList extension_list = QString::fromStdString(OIIO::get_string_attribute("extension_list")).split(';');
// Check if we've created the supported formats list, create it if not
if (supported_formats_.isEmpty()) {
QStringList extension_list =
QString::fromStdString(OIIO::get_string_attribute("extension_list"))
.split(';');
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
foreach (const QString& ext, extension_list) {
QStringList format_and_ext = ext.split(':');
// The format of "extension_list" is "format:ext", we want to separate it into a simple list of extensions
foreach (const QString &ext, extension_list) {
QStringList format_and_ext = ext.split(':');
supported_formats_.append(format_and_ext.at(1).split(','));
}
}
supported_formats_.append(format_and_ext.at(1).split(','));
}
}
if (!supported_formats_.contains(QFileInfo(fn).suffix(), Qt::CaseInsensitive)) {
return false;
}
if (!supported_formats_.contains(QFileInfo(fn).suffix(),
Qt::CaseInsensitive)) {
return false;
}
return true;
return true;
}
bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
{
image_ = OIIO::ImageInput::open(fn.toStdString());
image_ = OIIO::ImageInput::open(fn.toStdString());
if (!image_) {
return false;
}
if (!image_) {
return false;
}
if (!image_->seek_subimage(subimage, 0)) {
return false;
}
if (!image_->seek_subimage(subimage, 0)) {
return false;
}
// Check if we can work with this pixel format
const OIIO::ImageSpec& spec = image_->spec();
// Check if we can work with this pixel format
const OIIO::ImageSpec &spec = image_->spec();
// We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
// We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
if (pix_fmt_ == PixelFormat::INVALID) {
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
return false;
}
if (pix_fmt_ == PixelFormat::INVALID) {
qWarning()
<< "Failed to convert OIIO::ImageDesc to native pixel format";
return false;
}
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
qCritical() << "Failed to determine appropriate OIIO basetype from native format";
return false;
}
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
qCritical()
<< "Failed to determine appropriate OIIO basetype from native format";
return false;
}
return true;
return true;
}
void OIIODecoder::CloseImageHandle()
{
if (image_) {
image_->close();
image_ = nullptr;
}
if (image_) {
image_->close();
image_ = nullptr;
}
buffer_.destroy();
buffer_.destroy();
}
VideoParams OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
VideoParams
OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
{
VideoParams video_params;
VideoParams video_params;
video_params.set_width(spec.width);
video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_channel_count(spec.nchannels);
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill);
video_params.set_width(spec.width);
video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(
static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_channel_count(spec.nchannels);
video_params.set_pixel_aspect_ratio(
OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill);
return video_params;
return video_params;
}
}
+27 -23
View File
@@ -26,46 +26,50 @@
#include "codec/decoder.h"
namespace olive {
class OIIODecoder : public Decoder
namespace olive
{
Q_OBJECT
class OIIODecoder : public Decoder {
Q_OBJECT
public:
OIIODecoder();
OIIODecoder();
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
DECODER_DEFAULT_DESTRUCTOR(OIIODecoder)
virtual QString id() const override;
virtual QString id() const override;
virtual bool SupportsVideo() override{return true;}
virtual bool SupportsVideo() override
{
return true;
}
virtual FootageDescription Probe(const QString& filename, CancelAtom *cancelled) const override;
virtual FootageDescription Probe(const QString &filename,
CancelAtom *cancelled) const override;
protected:
virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
virtual void CloseInternal() override;
virtual bool OpenInternal() override;
virtual TexturePtr
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual void CloseInternal() override;
private:
std::unique_ptr<OIIO::ImageInput> image_;
std::unique_ptr<OIIO::ImageInput> image_;
static bool FileTypeIsSupported(const QString& fn);
static bool FileTypeIsSupported(const QString &fn);
bool OpenImageHandler(const QString& fn, int subimage);
bool OpenImageHandler(const QString &fn, int subimage);
void CloseImageHandle();
void CloseImageHandle();
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
PixelFormat pix_fmt_;
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
PixelFormat pix_fmt_;
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
Frame buffer_;
RetrieveVideoParams last_params_;
static QStringList supported_formats_;
Frame buffer_;
RetrieveVideoParams last_params_;
static QStringList supported_formats_;
};
}
+28 -26
View File
@@ -22,60 +22,62 @@
#include "common/oiioutils.h"
namespace olive {
OIIOEncoder::OIIOEncoder(const EncodingParams &params) :
Encoder(params)
namespace olive
{
OIIOEncoder::OIIOEncoder(const EncodingParams &params)
: Encoder(params)
{
}
bool OIIOEncoder::Open()
{
return true;
return true;
}
bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
{
std::string filename = GetFilenameForFrame(time).toStdString();
std::string filename = GetFilenameForFrame(time).toStdString();
auto output = OIIO::ImageOutput::create(filename);
if (!output) {
return false;
}
auto output = OIIO::ImageOutput::create(filename);
if (!output) {
return false;
}
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
OIIO::ImageSpec spec(frame->width(), frame->height(), frame->channel_count(), type);
OIIO::TypeDesc type = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
OIIO::ImageSpec spec(frame->width(), frame->height(),
frame->channel_count(), type);
if (!output->open(filename, spec)) {
return false;
}
if (!output->open(filename, spec)) {
return false;
}
if (!output->write_image(type, frame->data(), OIIO::AutoStride, frame->linesize_bytes())) {
return false;
}
if (!output->write_image(type, frame->data(), OIIO::AutoStride,
frame->linesize_bytes())) {
return false;
}
if (!output->close()) {
return false;
}
if (!output->close()) {
return false;
}
return true;
return true;
}
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
{
// Do nothing
return false;
// Do nothing
return false;
}
bool OIIOEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
{
return false;
return false;
}
void OIIOEncoder::Close()
{
// Do nothing
// Do nothing
}
}
+11 -11
View File
@@ -23,23 +23,23 @@
#include "codec/encoder.h"
namespace olive {
class OIIOEncoder : public Encoder
namespace olive
{
Q_OBJECT
class OIIOEncoder : public Encoder {
Q_OBJECT
public:
OIIOEncoder(const EncodingParams &params);
OIIOEncoder(const EncodingParams &params);
public slots:
virtual bool Open() override;
virtual bool Open() override;
virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override;
virtual bool WriteAudio(const SampleBuffer &audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
virtual bool WriteFrame(olive::FramePtr frame,
olive::core::rational time) override;
virtual bool WriteAudio(const SampleBuffer &audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
};
}
+61 -58
View File
@@ -20,100 +20,103 @@
#include "planarfiledevice.h"
namespace olive {
PlanarFileDevice::PlanarFileDevice(QObject *parent) :
QObject(parent)
namespace olive
{
PlanarFileDevice::PlanarFileDevice(QObject *parent)
: QObject(parent)
{
}
PlanarFileDevice::~PlanarFileDevice()
{
close();
close();
}
bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMode mode)
bool PlanarFileDevice::open(const QVector<QString> &filenames,
QIODevice::OpenMode mode)
{
if (isOpen()) {
// Already open
return false;
}
if (isOpen()) {
// Already open
return false;
}
files_.resize(filenames.size());
files_.fill(nullptr);
files_.resize(filenames.size());
files_.fill(nullptr);
for (int i=0; i<files_.size(); i++) {
files_[i] = new QFile(filenames.at(i));
if (!files_[i]->open(mode)) {
close();
return false;
}
}
for (int i = 0; i < files_.size(); i++) {
files_[i] = new QFile(filenames.at(i));
if (!files_[i]->open(mode)) {
close();
return false;
}
}
return true;
return true;
}
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offset)
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel,
qint64 offset)
{
qint64 ret = -1;
qint64 ret = -1;
if (isOpen()) {
for (int i=0; i<files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
}
}
if (isOpen()) {
for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
}
}
return ret;
return ret;
}
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel, qint64 offset)
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel,
qint64 offset)
{
qint64 ret = -1;
qint64 ret = -1;
if (isOpen()) {
for (int i=0; i<files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
}
}
if (isOpen()) {
for (int i = 0; i < files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
}
}
return ret;
return ret;
}
qint64 PlanarFileDevice::size() const
{
if (isOpen()) {
return files_.first()->size();
} else {
return 0;
}
if (isOpen()) {
return files_.first()->size();
} else {
return 0;
}
}
bool PlanarFileDevice::seek(qint64 pos)
{
bool ret = true;
bool ret = true;
for (int i=0; i<files_.size(); i++) {
ret = files_[i]->seek(pos) & ret;
}
for (int i = 0; i < files_.size(); i++) {
ret = files_[i]->seek(pos) & ret;
}
return ret;
return ret;
}
void PlanarFileDevice::close()
{
for (int i=0; i<files_.size(); i++) {
QFile *f = files_.at(i);
if (f) {
if (f->isOpen()) {
f->close();
}
delete f;
}
}
files_.clear();
for (int i = 0; i < files_.size(); i++) {
QFile *f = files_.at(i);
if (f) {
if (f->isOpen()) {
f->close();
}
delete f;
}
}
files_.clear();
}
}
+18 -18
View File
@@ -25,38 +25,38 @@
#include <QFile>
#include <QObject>
namespace olive {
namespace olive
{
using namespace core;
class PlanarFileDevice : public QObject
{
Q_OBJECT
class PlanarFileDevice : public QObject {
Q_OBJECT
public:
PlanarFileDevice(QObject *parent = nullptr);
PlanarFileDevice(QObject *parent = nullptr);
virtual ~PlanarFileDevice() override;
virtual ~PlanarFileDevice() override;
bool isOpen() const
{
return !files_.isEmpty();
}
bool isOpen() const
{
return !files_.isEmpty();
}
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 write(const char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 write(const char **data, qint64 bytes_per_channel,
qint64 offset = 0);
qint64 size() const;
qint64 size() const;
bool seek(qint64 pos);
bool seek(qint64 pos);
void close();
void close();
private:
QVector<QFile*> files_;
QVector<QFile *> files_;
};
}
+3 -6
View File
@@ -23,15 +23,12 @@
#include "common/define.h"
namespace olive {
namespace olive
{
class AutoScroll {
public:
enum Method {
kNone,
kPage,
kSmooth
};
enum Method { kNone, kPage, kSmooth };
};
}
+22 -20
View File
@@ -24,36 +24,38 @@
#include "common/define.h"
#include "render/cancelatom.h"
namespace olive {
namespace olive
{
class CancelableObject {
public:
CancelableObject()
{
}
CancelableObject()
{
}
void Cancel()
{
cancel_.Cancel();
CancelEvent();
}
void Cancel()
{
cancel_.Cancel();
CancelEvent();
}
CancelAtom *GetCancelAtom()
{
return &cancel_;
}
CancelAtom *GetCancelAtom()
{
return &cancel_;
}
bool IsCancelled()
{
return cancel_.IsCancelled();
}
bool IsCancelled()
{
return cancel_.IsCancelled();
}
protected:
virtual void CancelEvent(){}
virtual void CancelEvent()
{
}
private:
CancelAtom cancel_;
CancelAtom cancel_;
};
}
+106 -98
View File
@@ -25,146 +25,154 @@
CommandLineParser::~CommandLineParser()
{
foreach (const KnownOption& o, options_) {
delete o.option;
}
foreach (const KnownOption &o, options_) {
delete o.option;
}
foreach (const KnownPositionalArgument& a, positional_args_) {
delete a.option;
}
foreach (const KnownPositionalArgument &a, positional_args_) {
delete a.option;
}
}
const CommandLineParser::Option *CommandLineParser::AddOption(const QStringList &strings, const QString &description, bool takes_arg, const QString &arg_placeholder, bool hidden)
const CommandLineParser::Option *
CommandLineParser::AddOption(const QStringList &strings,
const QString &description, bool takes_arg,
const QString &arg_placeholder, bool hidden)
{
Option* o = new Option();
Option *o = new Option();
options_.append({strings, description, o, takes_arg, arg_placeholder, hidden});
options_.append(
{ strings, description, o, takes_arg, arg_placeholder, hidden });
return o;
return o;
}
const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArgument(const QString &name, const QString &description, bool required)
const CommandLineParser::PositionalArgument *
CommandLineParser::AddPositionalArgument(const QString &name,
const QString &description,
bool required)
{
PositionalArgument* a = new PositionalArgument();
PositionalArgument *a = new PositionalArgument();
positional_args_.append({name, description, a, required});
positional_args_.append({ name, description, a, required });
return a;
return a;
}
void CommandLineParser::Process(const QVector<QString> &argv)
{
int positional_index = 0;
int positional_index = 0;
for (int i=1; i<argv.size(); i++) {
if (argv[i][0] == '-') {
// Must be an option
for (int i = 1; i < argv.size(); i++) {
if (argv[i][0] == '-') {
// Must be an option
// Skip past first dashes
QString arg_basename = argv[i].mid(1);
// Skip past first dashes
QString arg_basename = argv[i].mid(1);
bool matched_known = false;
bool matched_known = false;
for (int j=0; j<options_.size(); j++) {
KnownOption& o = options_[j];
for (int j = 0; j < options_.size(); j++) {
KnownOption &o = options_[j];
foreach (const QString& s, o.args) {
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
// Flag discovered!
o.option->Set();
foreach (const QString &s, o.args) {
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
// Flag discovered!
o.option->Set();
if (o.takes_arg && i+1 < argv.size()) {
o.option->SetSetting(argv[i+1]);
i++;
}
if (o.takes_arg && i + 1 < argv.size()) {
o.option->SetSetting(argv[i + 1]);
i++;
}
matched_known = true;
goto found_flag;
}
}
}
matched_known = true;
goto found_flag;
}
}
}
found_flag:
if (!matched_known) {
qWarning() << "Unknown parameter:" << argv[i];
}
if (!matched_known) {
qWarning() << "Unknown parameter:" << argv[i];
}
} else {
// Must be a positional flag
if (positional_index < positional_args_.size()) {
positional_args_[positional_index].option->SetSetting(argv[i]);
positional_index++;
} else {
qWarning() << "Unknown parameter:" << argv[i];
}
}
}
} else {
// Must be a positional flag
if (positional_index < positional_args_.size()) {
positional_args_[positional_index].option->SetSetting(argv[i]);
positional_index++;
} else {
qWarning() << "Unknown parameter:" << argv[i];
}
}
}
}
void CommandLineParser::PrintHelp(const char* filename)
void CommandLineParser::PrintHelp(const char *filename)
{
printf("%s %s\n",
QCoreApplication::applicationName().toUtf8().constData(),
QCoreApplication::applicationVersion().toUtf8().constData());
printf("%s %s\n", QCoreApplication::applicationName().toUtf8().constData(),
QCoreApplication::applicationVersion().toUtf8().constData());
printf("Copyright (C) 2018-2022 Olive Team\n");
printf("Copyright (C) 2018-2022 Olive Team\n");
QString positional_args;
for (int i=0; i<positional_args_.size(); i++) {
if (i > 0) {
positional_args.append(' ');
}
QString positional_args;
for (int i = 0; i < positional_args_.size(); i++) {
if (i > 0) {
positional_args.append(' ');
}
positional_args.append('[');
positional_args.append(positional_args_.at(i).name);
positional_args.append(']');
}
positional_args.append('[');
positional_args.append(positional_args_.at(i).name);
positional_args.append(']');
}
const char* basename;
const char *basename;
#ifdef Q_OS_WINDOWS
basename = strrchr(filename, '\\');
if (!basename) {
basename = strrchr(filename, '/');
}
basename = strrchr(filename, '\\');
if (!basename) {
basename = strrchr(filename, '/');
}
#else
basename = strrchr(filename, '/');
basename = strrchr(filename, '/');
#endif
if (basename) {
// Slash found, increment pointer to avoid showing the slash itself
basename++;
} else {
// If no slashes are found, assume string is already a basename
basename = filename;
}
if (basename) {
// Slash found, increment pointer to avoid showing the slash itself
basename++;
} else {
// If no slashes are found, assume string is already a basename
basename = filename;
}
printf("Usage: %s [options] %s\n\n", basename, positional_args.toUtf8().constData());
foreach (const KnownOption& o, options_) {
if (o.hidden) {
continue;
}
printf("Usage: %s [options] %s\n\n", basename,
positional_args.toUtf8().constData());
foreach (const KnownOption &o, options_) {
if (o.hidden) {
continue;
}
QString all_args;
QString all_args;
for (int i=0; i<o.args.size(); i++) {
if (i > 0) {
all_args.append(QStringLiteral(", "));
}
for (int i = 0; i < o.args.size(); i++) {
if (i > 0) {
all_args.append(QStringLiteral(", "));
}
const QString& this_arg = o.args.at(i);
const QString &this_arg = o.args.at(i);
all_args.append('-');
all_args.append(this_arg);
}
all_args.append('-');
all_args.append(this_arg);
}
if (o.arg_placeholder.isEmpty()) {
printf(" %s\n", all_args.toUtf8().constData());
} else {
printf(" %s <%s>\n", all_args.toUtf8().constData(), o.arg_placeholder.toUtf8().constData());
}
if (o.arg_placeholder.isEmpty()) {
printf(" %s\n", all_args.toUtf8().constData());
} else {
printf(" %s <%s>\n", all_args.toUtf8().constData(),
o.arg_placeholder.toUtf8().constData());
}
printf(" %s\n\n", o.description.toUtf8().constData());
}
printf(" %s\n\n", o.description.toUtf8().constData());
}
printf("\n");
printf("\n");
}
+60 -60
View File
@@ -36,86 +36,86 @@
* the user as a command line argument. Therefore we needed a custom implementation that could
* parse arguments without the need for a Q(Core)Application to be present already.
*/
class CommandLineParser
{
class CommandLineParser {
public:
~CommandLineParser();
~CommandLineParser();
DISABLE_COPY_MOVE(CommandLineParser)
DISABLE_COPY_MOVE(CommandLineParser)
class PositionalArgument
{
public:
PositionalArgument() = default;
class PositionalArgument {
public:
PositionalArgument() = default;
const QString& GetSetting() const
{
return setting_;
}
const QString &GetSetting() const
{
return setting_;
}
void SetSetting(const QString& s)
{
setting_ = s;
}
void SetSetting(const QString &s)
{
setting_ = s;
}
private:
QString setting_;
private:
QString setting_;
};
};
class Option : public PositionalArgument {
public:
Option()
{
is_set_ = false;
}
class Option : public PositionalArgument {
public:
Option()
{
is_set_ = false;
}
bool IsSet() const
{
return is_set_;
}
bool IsSet() const
{
return is_set_;
}
void Set()
{
is_set_ = true;
}
void Set()
{
is_set_ = true;
}
private:
bool is_set_;
};
private:
bool is_set_;
CommandLineParser() = default;
};
const Option *AddOption(const QStringList &strings,
const QString &description, bool takes_arg = false,
const QString &arg_placeholder = QString(),
bool hidden = false);
CommandLineParser() = default;
const PositionalArgument *AddPositionalArgument(const QString &name,
const QString &description,
bool required = false);
const Option* AddOption(const QStringList& strings, const QString& description, bool takes_arg = false, const QString& arg_placeholder = QString(), bool hidden = false);
void Process(const QVector<QString> &argv);
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false);
void Process(const QVector<QString> &argv);
void PrintHelp(const char* filename);
void PrintHelp(const char *filename);
private:
struct KnownOption {
QStringList args;
QString description;
Option* option;
bool takes_arg;
QString arg_placeholder;
bool hidden;
};
struct KnownOption {
QStringList args;
QString description;
Option *option;
bool takes_arg;
QString arg_placeholder;
bool hidden;
};
struct KnownPositionalArgument {
QString name;
QString description;
PositionalArgument* option;
bool required;
};
struct KnownPositionalArgument {
QString name;
QString description;
PositionalArgument *option;
bool required;
};
QVector<KnownOption> options_;
QVector<KnownPositionalArgument> positional_args_;
QVector<KnownOption> options_;
QVector<KnownPositionalArgument> positional_args_;
};
#endif // COMMANDLINEPARSER_H
+44 -33
View File
@@ -40,52 +40,63 @@ crashpad::CrashpadClient *client;
bool InitializeCrashpad()
{
QString report_path = QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("reports"));
QString report_path = QDir(olive::FileFunctions::GetTempFilePath())
.filePath(QStringLiteral("reports"));
QString handler_fn = olive::FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("crashpad_handler"));
QString handler_fn =
olive::FileFunctions::GetFormattedExecutableForPlatform(
QStringLiteral("crashpad_handler"));
// Generate absolute path
QString handler_abs_path = QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
// Generate absolute path
QString handler_abs_path =
QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
bool status = false;
bool status = false;
if (QFileInfo::exists(handler_abs_path)) {
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
if (QFileInfo::exists(handler_abs_path)) {
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics"))));
base::FilePath metrics_dir(
QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath())
.filePath(QStringLiteral("metrics"))));
// Metadata that will be posted to the server with the crash report map
std::map<std::string, std::string> annotations;
// Metadata that will be posted to the server with the crash report map
std::map<std::string, std::string> annotations;
// Disable crashpad rate limiting so that all crashes have dmp files
std::vector<std::string> arguments;
arguments.push_back("--no-rate-limit");
arguments.push_back("--no-upload-gzip");
// Disable crashpad rate limiting so that all crashes have dmp files
std::vector<std::string> arguments;
arguments.push_back("--no-rate-limit");
arguments.push_back("--no-upload-gzip");
// Initialize Crashpad database
std::unique_ptr<crashpad::CrashReportDatabase> database = crashpad::CrashReportDatabase::Initialize(reports_dir);
if (database == NULL) return false;
// Initialize Crashpad database
std::unique_ptr<crashpad::CrashReportDatabase> database =
crashpad::CrashReportDatabase::Initialize(reports_dir);
if (database == NULL)
return false;
// Disable automated crash uploads
crashpad::Settings *settings = database->GetSettings();
if (settings == NULL) return false;
settings->SetUploadsEnabled(false);
// Disable automated crash uploads
crashpad::Settings *settings = database->GetSettings();
if (settings == NULL)
return false;
settings->SetUploadsEnabled(false);
// Start crash handler
client = new crashpad::CrashpadClient();
status = client->StartHandler(handler, reports_dir, metrics_dir,
"https://olivevideoeditor.org/crashpad/report.php",
annotations, arguments, true, true);
}
// Start crash handler
client = new crashpad::CrashpadClient();
status = client->StartHandler(
handler, reports_dir, metrics_dir,
"https://olivevideoeditor.org/crashpad/report.php", annotations,
arguments, true, true);
}
// Override Crashpad exception filter with our own
if (!status) {
qWarning() << "Failed to start Crashpad, automatic crash reporting will be disabled";
}
// Override Crashpad exception filter with our own
if (!status) {
qWarning()
<< "Failed to start Crashpad, automatic crash reporting will be disabled";
}
return status;
return status;
}
#endif // USE_CRASHPAD
+1 -1
View File
@@ -35,6 +35,6 @@
// arrays encoded in UTF-16.
#define QSTRING_TO_BASE_STRING(x) x.toStdWString()
#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x)
#endif // BUILDFLAG(IS_WIN)
#endif // BUILDFLAG(IS_WIN)
#endif // CRASHPADUTILS_H
+29 -27
View File
@@ -20,38 +20,40 @@
#include "debug.h"
namespace olive {
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
namespace olive
{
QByteArray localMsg = msg.toLocal8Bit();
const char* msg_type = "UNKNOWN";
switch (type) {
case QtDebugMsg:
msg_type = "DEBUG";
break;
case QtInfoMsg:
msg_type = "INFO";
break;
case QtWarningMsg:
msg_type = "WARNING";
break;
case QtCriticalMsg:
msg_type = "ERROR";
break;
case QtFatalMsg:
msg_type = "FATAL";
break;
}
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
const char *msg_type = "UNKNOWN";
switch (type) {
case QtDebugMsg:
msg_type = "DEBUG";
break;
case QtInfoMsg:
msg_type = "INFO";
break;
case QtWarningMsg:
msg_type = "WARNING";
break;
case QtCriticalMsg:
msg_type = "ERROR";
break;
case QtFatalMsg:
msg_type = "FATAL";
break;
}
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
#ifdef Q_OS_WINDOWS
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
// is flushed
fflush(stderr);
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
// is flushed
fflush(stderr);
#endif
}
+4 -2
View File
@@ -25,9 +25,11 @@
#include "common/define.h"
namespace olive {
namespace olive
{
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg);
}
+54 -55
View File
@@ -26,77 +26,76 @@
//#define ALLOW_RETURNING_INFINITY
namespace olive {
class Decibel
namespace olive
{
class Decibel {
public:
// In basically all circumstances, this should calculate to 0.0 linear
static constexpr double MINIMUM = -200.0;
// In basically all circumstances, this should calculate to 0.0 linear
static constexpr double MINIMUM = -200.0;
static double fromLinear(double linear)
{
double v = double(20.0) * std::log10(linear);
static double fromLinear(double linear)
{
double v = double(20.0) * std::log10(linear);
#ifndef ALLOW_RETURNING_INFINITY
if (std::isinf(v)) {
return MINIMUM;
}
if (std::isinf(v)) {
return MINIMUM;
}
#endif
return v;
}
return v;
}
static double toLinear(double decibel)
{
double to_linear = std::pow(double(10.0), decibel / double(20.0));
static double toLinear(double decibel)
{
double to_linear = std::pow(double(10.0), decibel / double(20.0));
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
if (to_linear < 0.000001) {
return 0;
} else {
return to_linear;
}
}
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
if (to_linear < 0.000001) {
return 0;
} else {
return to_linear;
}
}
static double fromLogarithmic(double logarithmic)
{
if (logarithmic < 0.001)
static double fromLogarithmic(double logarithmic)
{
if (logarithmic < 0.001)
#ifdef ALLOW_RETURNING_INFINITY
return std::numeric_limits<double>::infinity();
return std::numeric_limits<double>::infinity();
#else
return MINIMUM;
return MINIMUM;
#endif
else if (logarithmic > 0.99)
return 0;
else
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
}
else if (logarithmic > 0.99)
return 0;
else
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
}
static double toLogarithmic(double decibel)
{
if (qFuzzyIsNull(decibel)) {
return 1;
} else {
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
}
}
static double toLogarithmic(double decibel)
{
if (qFuzzyIsNull(decibel)) {
return 1;
} else {
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
}
}
static double LinearToLogarithmic(double linear)
{
return 1 - std::exp(-linear * LOG100);
}
static double LinearToLogarithmic(double linear)
{
return 1 - std::exp(-linear * LOG100);
}
static double LogarithmicToLinear(double logarithmic)
{
if (logarithmic > 0.99) {
return 1;
} else {
return -std::log(1 - logarithmic) / LOG100;
}
}
static double LogarithmicToLinear(double logarithmic)
{
if (logarithmic > 0.99) {
return 1;
} else {
return -std::log(1 - logarithmic) / LOG100;
}
}
private:
static constexpr double LOG100 = 4.60517018599;
static constexpr double LOG100 = 4.60517018599;
};
}
+16 -12
View File
@@ -21,7 +21,8 @@
#ifndef OLIVECOMMONDEFINE_H
#define OLIVECOMMONDEFINE_H
namespace olive {
namespace olive
{
/// The minimum size an icon in ProjectExplorer can be
const int kProjectIconSizeMinimum = 16;
@@ -39,25 +40,28 @@ const int kBytesInGigabyte = 1073741824;
#define MACRO_NAME_AS_STR(s) #s
#define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s)
#define OLIVE_NS_CONST_ARG(x, y) QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_ARG(x, y) QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_RETURN_ARG(x, y) QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_CONST_ARG(x, y) \
QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_ARG(x, y) \
QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
#define OLIVE_NS_RETURN_ARG(x, y) \
QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
/**
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
* use our own functions for portability.
*/
#define DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
#define DISABLE_COPY(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete;
#define DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define DISABLE_COPY_MOVE(Class) \
DISABLE_COPY(Class) \
DISABLE_MOVE(Class)
DISABLE_COPY(Class) \
DISABLE_MOVE(Class)
#endif // OLIVECOMMONDEFINE_H
+10 -9
View File
@@ -23,21 +23,22 @@
#include <stdint.h>
namespace olive {
namespace olive
{
inline int64_t GetDigitCount(int64_t input)
{
input = std::abs(input);
input = std::abs(input);
int64_t lim = 10;
int64_t digit = 1;
int64_t lim = 10;
int64_t digit = 1;
while (input >= lim) {
lim *= 10;
digit++;
}
while (input >= lim) {
lim *= 10;
digit++;
}
return digit;
return digit;
}
}
+148 -141
View File
@@ -20,180 +20,187 @@
#include "common/ffmpegutils.h"
namespace olive {
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum)
namespace olive
{
AVPixelFormat possible_pix_fmts[3];
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
AVPixelFormat
FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
PixelFormat maximum)
{
AVPixelFormat possible_pix_fmts[3];
if (maximum == PixelFormat::U8) {
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
} else {
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
}
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
pix_fmt,
1,
nullptr);
if (maximum == PixelFormat::U8) {
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
} else {
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
}
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt, 1,
nullptr);
}
SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
{
switch (smp_fmt) {
case AV_SAMPLE_FMT_U8:
return SampleFormat::U8;
case AV_SAMPLE_FMT_S16:
return SampleFormat::S16;
case AV_SAMPLE_FMT_S32:
return SampleFormat::S32;
case AV_SAMPLE_FMT_S64:
return SampleFormat::S64;
case AV_SAMPLE_FMT_FLT:
return SampleFormat::F32;
case AV_SAMPLE_FMT_DBL:
return SampleFormat::F64;
case AV_SAMPLE_FMT_U8P :
return SampleFormat::U8P;
case AV_SAMPLE_FMT_S16P:
return SampleFormat::S16P;
case AV_SAMPLE_FMT_S32P:
return SampleFormat::S32P;
case AV_SAMPLE_FMT_S64P:
return SampleFormat::S64P;
case AV_SAMPLE_FMT_FLTP:
return SampleFormat::F32P;
case AV_SAMPLE_FMT_DBLP:
return SampleFormat::F64P;
case AV_SAMPLE_FMT_NONE:
case AV_SAMPLE_FMT_NB:
break;
}
switch (smp_fmt) {
case AV_SAMPLE_FMT_U8:
return SampleFormat::U8;
case AV_SAMPLE_FMT_S16:
return SampleFormat::S16;
case AV_SAMPLE_FMT_S32:
return SampleFormat::S32;
case AV_SAMPLE_FMT_S64:
return SampleFormat::S64;
case AV_SAMPLE_FMT_FLT:
return SampleFormat::F32;
case AV_SAMPLE_FMT_DBL:
return SampleFormat::F64;
case AV_SAMPLE_FMT_U8P:
return SampleFormat::U8P;
case AV_SAMPLE_FMT_S16P:
return SampleFormat::S16P;
case AV_SAMPLE_FMT_S32P:
return SampleFormat::S32P;
case AV_SAMPLE_FMT_S64P:
return SampleFormat::S64P;
case AV_SAMPLE_FMT_FLTP:
return SampleFormat::F32P;
case AV_SAMPLE_FMT_DBLP:
return SampleFormat::F64P;
case AV_SAMPLE_FMT_NONE:
case AV_SAMPLE_FMT_NB:
break;
}
return SampleFormat::INVALID;
return SampleFormat::INVALID;
}
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const SampleFormat &smp_fmt)
{
switch (smp_fmt) {
case SampleFormat::U8:
return AV_SAMPLE_FMT_U8;
case SampleFormat::S16:
return AV_SAMPLE_FMT_S16;
case SampleFormat::S32:
return AV_SAMPLE_FMT_S32;
case SampleFormat::S64:
return AV_SAMPLE_FMT_S64;
case SampleFormat::F32:
return AV_SAMPLE_FMT_FLT;
case SampleFormat::F64:
return AV_SAMPLE_FMT_DBL;
case SampleFormat::U8P:
return AV_SAMPLE_FMT_U8P;
case SampleFormat::S16P:
return AV_SAMPLE_FMT_S16P;
case SampleFormat::S32P:
return AV_SAMPLE_FMT_S32P;
case SampleFormat::S64P:
return AV_SAMPLE_FMT_S64P;
case SampleFormat::F32P:
return AV_SAMPLE_FMT_FLTP;
case SampleFormat::F64P:
return AV_SAMPLE_FMT_DBLP;
case SampleFormat::INVALID:
case SampleFormat::COUNT:
break;
}
switch (smp_fmt) {
case SampleFormat::U8:
return AV_SAMPLE_FMT_U8;
case SampleFormat::S16:
return AV_SAMPLE_FMT_S16;
case SampleFormat::S32:
return AV_SAMPLE_FMT_S32;
case SampleFormat::S64:
return AV_SAMPLE_FMT_S64;
case SampleFormat::F32:
return AV_SAMPLE_FMT_FLT;
case SampleFormat::F64:
return AV_SAMPLE_FMT_DBL;
case SampleFormat::U8P:
return AV_SAMPLE_FMT_U8P;
case SampleFormat::S16P:
return AV_SAMPLE_FMT_S16P;
case SampleFormat::S32P:
return AV_SAMPLE_FMT_S32P;
case SampleFormat::S64P:
return AV_SAMPLE_FMT_S64P;
case SampleFormat::F32P:
return AV_SAMPLE_FMT_FLTP;
case SampleFormat::F64P:
return AV_SAMPLE_FMT_DBLP;
case SampleFormat::INVALID:
case SampleFormat::COUNT:
break;
}
return AV_SAMPLE_FMT_NONE;
return AV_SAMPLE_FMT_NONE;
}
int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
{
switch (cs) {
case AVCOL_SPC_BT709:
return SWS_CS_ITU709;
case AVCOL_SPC_FCC:
return SWS_CS_FCC;
case AVCOL_SPC_BT470BG:
return SWS_CS_ITU624;
case AVCOL_SPC_SMPTE170M:
return SWS_CS_SMPTE170M;
case AVCOL_SPC_SMPTE240M:
return SWS_CS_SMPTE240M;
case AVCOL_SPC_BT2020_NCL:
return SWS_CS_BT2020;
default:
break;
}
switch (cs) {
case AVCOL_SPC_BT709:
return SWS_CS_ITU709;
case AVCOL_SPC_FCC:
return SWS_CS_FCC;
case AVCOL_SPC_BT470BG:
return SWS_CS_ITU624;
case AVCOL_SPC_SMPTE170M:
return SWS_CS_SMPTE170M;
case AVCOL_SPC_SMPTE240M:
return SWS_CS_SMPTE240M;
case AVCOL_SPC_BT2020_NCL:
return SWS_CS_BT2020;
default:
break;
}
return SWS_CS_DEFAULT;
return SWS_CS_DEFAULT;
}
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
{
switch (f) {
case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P;
case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P;
case AV_PIX_FMT_YUVJ444P: return AV_PIX_FMT_YUV444P;
case AV_PIX_FMT_YUVJ440P: return AV_PIX_FMT_YUV440P;
case AV_PIX_FMT_YUVJ411P: return AV_PIX_FMT_YUV411P;
default:
break;
}
switch (f) {
case AV_PIX_FMT_YUVJ420P:
return AV_PIX_FMT_YUV420P;
case AV_PIX_FMT_YUVJ422P:
return AV_PIX_FMT_YUV422P;
case AV_PIX_FMT_YUVJ444P:
return AV_PIX_FMT_YUV444P;
case AV_PIX_FMT_YUVJ440P:
return AV_PIX_FMT_YUV440P;
case AV_PIX_FMT_YUVJ411P:
return AV_PIX_FMT_YUV411P;
default:
break;
}
return f;
return f;
}
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt, int channel_layout)
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
int channel_layout)
{
if (channel_layout == VideoParams::kRGBChannelCount) {
switch (pix_fmt) {
case PixelFormat::U8:
return AV_PIX_FMT_RGB24;
case PixelFormat::U16:
return AV_PIX_FMT_RGB48;
case PixelFormat::F16:
case PixelFormat::F32:
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
switch (pix_fmt) {
case PixelFormat::U8:
return AV_PIX_FMT_RGBA;
case PixelFormat::U16:
return AV_PIX_FMT_RGBA64;
case PixelFormat::F16:
case PixelFormat::F32:
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
}
if (channel_layout == VideoParams::kRGBChannelCount) {
switch (pix_fmt) {
case PixelFormat::U8:
return AV_PIX_FMT_RGB24;
case PixelFormat::U16:
return AV_PIX_FMT_RGB48;
case PixelFormat::F16:
case PixelFormat::F32:
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
switch (pix_fmt) {
case PixelFormat::U8:
return AV_PIX_FMT_RGBA;
case PixelFormat::U16:
return AV_PIX_FMT_RGBA64;
case PixelFormat::F16:
case PixelFormat::F32:
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
}
return AV_PIX_FMT_NONE;
return AV_PIX_FMT_NONE;
}
PixelFormat FFmpegUtils::GetCompatiblePixelFormat(const PixelFormat &pix_fmt)
{
switch (pix_fmt) {
case PixelFormat::U8:
return PixelFormat::U8;
case PixelFormat::U16:
case PixelFormat::F16:
case PixelFormat::F32:
return PixelFormat::U16;
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
switch (pix_fmt) {
case PixelFormat::U8:
return PixelFormat::U8;
case PixelFormat::U16:
case PixelFormat::F16:
case PixelFormat::F32:
return PixelFormat::U16;
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
return PixelFormat::INVALID;
return PixelFormat::INVALID;
}
}
+21 -17
View File
@@ -31,63 +31,67 @@ extern "C" {
#include "render/videoparams.h"
namespace olive {
namespace olive
{
using namespace core;
class FFmpegUtils {
public:
/**
/**
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
*/
static AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt, PixelFormat maximum = PixelFormat::INVALID);
static AVPixelFormat
GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
PixelFormat maximum = PixelFormat::INVALID);
/**
/**
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
*/
static PixelFormat GetCompatiblePixelFormat(const PixelFormat& pix_fmt);
static PixelFormat GetCompatiblePixelFormat(const PixelFormat &pix_fmt);
/**
/**
* @brief Returns an FFmpeg pixel format for a given native pixel format
*/
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat& pix_fmt, int channel_layout);
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
int channel_layout);
/**
/**
* @brief Returns a native sample format type for a given AVSampleFormat
*/
static SampleFormat GetNativeSampleFormat(const AVSampleFormat& smp_fmt);
static SampleFormat GetNativeSampleFormat(const AVSampleFormat &smp_fmt);
/**
/**
* @brief Returns an FFmpeg sample format type for a given native type
*/
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
/**
/**
* @brief Returns an SWS_CS_* macro from an AVColorSpace enum member
*
* Why aren't these the same thing anyway? And for that matter, why doesn't FFmpeg provide a
* convenience function to do this conversion for us? Who knows, but here we are.
*/
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
/**
/**
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart
*
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
* aware), we use this function.
*/
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
};
using AVFramePtr = std::shared_ptr<AVFrame>;
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
{
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); });
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
}
inline AVFramePtr CreateAVFramePtr()
{
return CreateAVFramePtr(av_frame_alloc());
return CreateAVFramePtr(av_frame_alloc());
}
}
+142 -125
View File
@@ -29,206 +29,223 @@
#include "config/config.h"
namespace olive {
namespace olive
{
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
{
QFileInfo info(filename);
QFileInfo info(filename);
if (!info.exists()) {
return QString();
}
if (!info.exists()) {
return QString();
}
QCryptographicHash hash(QCryptographicHash::Sha1);
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(info.absoluteFilePath().toUtf8());
hash.addData(info.absoluteFilePath().toUtf8());
hash.addData(QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
hash.addData(
QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
QByteArray result = hash.result();
QByteArray result = hash.result();
return QString(result.toHex());
return QString(result.toHex());
}
QString FileFunctions::GetConfigurationLocation()
{
if (IsPortable()) {
return GetApplicationPath();
} else {
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir(s).mkpath(".");
return s;
}
if (IsPortable()) {
return GetApplicationPath();
} else {
QString s =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir(s).mkpath(".");
return s;
}
}
bool FileFunctions::IsPortable()
{
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
}
QString FileFunctions::GetApplicationPath()
{
return QCoreApplication::applicationDirPath();
return QCoreApplication::applicationDirPath();
}
QString FileFunctions::GetTempFilePath()
{
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
.filePath(QCoreApplication::organizationName()))
.filePath(QCoreApplication::applicationName());
QString temp_path =
QDir(
QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
.filePath(QCoreApplication::organizationName()))
.filePath(QCoreApplication::applicationName());
// Ensure it exists
QDir(temp_path).mkpath(".");
// Ensure it exists
QDir(temp_path).mkpath(".");
return temp_path;
return temp_path;
}
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest)
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString &source,
const QString &dest)
{
QFileInfoList info_list = QDir(source).entryInfoList();
QFileInfoList info_list = QDir(source).entryInfoList();
foreach (const QFileInfo& info, info_list) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
continue;
}
foreach (const QFileInfo &info, info_list) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") ||
info.fileName() == QStringLiteral("..")) {
continue;
}
QString dest_equivalent = QDir(dest).filePath(info.fileName());
QString dest_equivalent = QDir(dest).filePath(info.fileName());
if (info.isDir()) {
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(), dest_equivalent)) {
return false;
}
} else if (QFileInfo::exists(dest_equivalent)) {
return false;
}
}
if (info.isDir()) {
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(),
dest_equivalent)) {
return false;
}
} else if (QFileInfo::exists(dest_equivalent)) {
return false;
}
}
return true;
return true;
}
void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bool overwrite)
void FileFunctions::CopyDirectory(const QString &source, const QString &dest,
bool overwrite)
{
QDir d(source);
QDir d(source);
if (!d.exists()) {
qCritical() << "Failed to copy directory, source" << source << "didn't exist";
return;
}
if (!d.exists()) {
qCritical()
<< "Failed to copy directory, source" << source << "didn't exist";
return;
}
QDir dest_dir(dest);
QDir dest_dir(dest);
if (!dest_dir.mkpath(QStringLiteral("."))) {
qCritical() << "Failed to create destination directory" << dest;
return;
}
if (!dest_dir.mkpath(QStringLiteral("."))) {
qCritical() << "Failed to create destination directory" << dest;
return;
}
QFileInfoList l = d.entryInfoList();
QFileInfoList l = d.entryInfoList();
foreach (const QFileInfo& info, l) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
continue;
}
foreach (const QFileInfo &info, l) {
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
if (info.fileName() == QStringLiteral(".") ||
info.fileName() == QStringLiteral("..")) {
continue;
}
QString dest_file_path = dest_dir.filePath(info.fileName());
QString dest_file_path = dest_dir.filePath(info.fileName());
if (info.isDir()) {
// Copy dir
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
} else {
// Copy file
if (overwrite && QFile::exists(dest_file_path)) {
QFile file(dest_file_path);
file.setPermissions(file.permissions() | QFileDevice::WriteOwner | QFileDevice::WriteUser |
QFileDevice::WriteGroup | QFileDevice::WriteOther);
file.remove();
}
if (info.isDir()) {
// Copy dir
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
} else {
// Copy file
if (overwrite && QFile::exists(dest_file_path)) {
QFile file(dest_file_path);
file.setPermissions(
file.permissions() | QFileDevice::WriteOwner |
QFileDevice::WriteUser | QFileDevice::WriteGroup |
QFileDevice::WriteOther);
file.remove();
}
QFile::copy(info.absoluteFilePath(), dest_file_path);
}
}
QFile::copy(info.absoluteFilePath(), dest_file_path);
}
}
}
bool FileFunctions::DirectoryIsValid(const QDir &d, bool try_to_create_if_not_exists)
bool FileFunctions::DirectoryIsValid(const QDir &d,
bool try_to_create_if_not_exists)
{
// Return whether the directory exists, or whether it could be created if it doesn't
return d.exists() || d.mkpath(QStringLiteral("."));
// Return whether the directory exists, or whether it could be created if it doesn't
return d.exists() || d.mkpath(QStringLiteral("."));
}
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension)
QString FileFunctions::EnsureFilenameExtension(QString fn,
const QString &extension)
{
// No-op if either input is empty
if (!fn.isEmpty() && !extension.isEmpty()) {
QString extension_with_dot;
// No-op if either input is empty
if (!fn.isEmpty() && !extension.isEmpty()) {
QString extension_with_dot;
extension_with_dot.append('.');
extension_with_dot.append(extension);
extension_with_dot.append('.');
extension_with_dot.append(extension);
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
fn.append(extension_with_dot);
}
}
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
fn.append(extension_with_dot);
}
}
return fn;
return fn;
}
QString FileFunctions::ReadFileAsString(const QString &filename)
{
QFile f(filename);
QString file_data;
if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream text_stream(&f);
file_data = text_stream.readAll();
f.close();
}
return file_data;
QFile f(filename);
QString file_data;
if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream text_stream(&f);
file_data = text_stream.readAll();
f.close();
}
return file_data;
}
QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
{
int counter = 0;
int counter = 0;
QFileInfo original_info(original);
QString basename = original_info.baseName();
QString complete_suffix = original_info.completeSuffix();
QFileInfo original_info(original);
QString basename = original_info.baseName();
QString complete_suffix = original_info.completeSuffix();
// If we have a complete suffix, make sure there's a period in it
if (!complete_suffix.isEmpty()) {
complete_suffix.prepend('.');
}
// If we have a complete suffix, make sure there's a period in it
if (!complete_suffix.isEmpty()) {
complete_suffix.prepend('.');
}
QString temp_abs_path;
do {
temp_abs_path = original_info.dir().filePath(
QStringLiteral("%1.tmp%2%3").arg(basename,
QString::number(counter),
complete_suffix));
counter++;
} while (QFileInfo::exists(temp_abs_path));
QString temp_abs_path;
do {
temp_abs_path = original_info.dir().filePath(
QStringLiteral("%1.tmp%2%3")
.arg(basename, QString::number(counter), complete_suffix));
counter++;
} while (QFileInfo::exists(temp_abs_path));
return temp_abs_path;
return temp_abs_path;
}
bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString &to)
bool FileFunctions::RenameFileAllowOverwrite(const QString &from,
const QString &to)
{
if (QFileInfo::exists(to) && !QFile::remove(to)) {
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
return false;
}
if (QFileInfo::exists(to) && !QFile::remove(to)) {
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
return false;
}
// By this point, we can assume `to` either never existed or has now been deleted
if (!QFile::rename(from, to)) {
qCritical() << "Failed to rename file" << from << "to" << to;
return false;
}
// By this point, we can assume `to` either never existed or has now been deleted
if (!QFile::rename(from, to)) {
qCritical() << "Failed to rename file" << from << "to" << to;
return false;
}
return true;
return true;
}
QString FileFunctions::GetAutoRecoveryRoot()
{
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery"));
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppLocalDataLocation))
.filePath(QStringLiteral("autorecovery"));
}
}
+29 -26
View File
@@ -26,36 +26,40 @@
#include "common/define.h"
namespace olive {
namespace olive
{
/**
* @brief A collection of static file and directory functions
*/
class FileFunctions {
public:
/**
/**
* @brief Returns true if the application is running in portable mode
*
* In portable mode, any persistent configuration files should be made in a path relative to the application rather
* than in the user's home folder.
*/
static bool IsPortable();
static bool IsPortable();
static QString GetUniqueFileIdentifier(const QString& filename);
static QString GetUniqueFileIdentifier(const QString &filename);
static QString GetConfigurationLocation();
static QString GetConfigurationLocation();
static QString GetApplicationPath();
static QString GetApplicationPath();
static QString GetTempFilePath();
static QString GetTempFilePath();
static bool CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest);
static bool CanCopyDirectoryWithoutOverwriting(const QString &source,
const QString &dest);
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false);
static void CopyDirectory(const QString &source, const QString &dest,
bool overwrite = false);
static bool DirectoryIsValid(const QDir& dir, bool try_to_create_if_not_exists = true);
static bool DirectoryIsValid(const QDir &dir,
bool try_to_create_if_not_exists = true);
/**
/**
* @brief Ensures a given filename has a certain extension
*
* Checks if the filename has the extension provided and appends it if not. The extension is
@@ -64,11 +68,12 @@ public:
* @return The filename provided either untouched or with the extension appended to it.
*/
static QString EnsureFilenameExtension(QString fn, const QString& extension);
static QString EnsureFilenameExtension(QString fn,
const QString &extension);
static QString ReadFileAsString(const QString& filename);
static QString ReadFileAsString(const QString &filename);
/**
/**
* @brief Returns a temporary filename that can be used while writing rather than the original
*
* If overwriting a file, it's safest to write to a new file first and then only replace it at
@@ -78,28 +83,26 @@ public:
* This function returns a slight variant of the filename provided that's guaranteed to not exist
* and therefore won't overwrite anything important.
*/
static QString GetSafeTemporaryFilename(const QString& original);
static QString GetSafeTemporaryFilename(const QString &original);
/**
/**
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
*/
static bool RenameFileAllowOverwrite(const QString& from, const QString& to);
static bool RenameFileAllowOverwrite(const QString &from,
const QString &to);
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
{
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
{
#ifdef Q_OS_WINDOWS
unformatted.append(QStringLiteral(".exe"));
unformatted.append(QStringLiteral(".exe"));
#endif
return unformatted;
}
static QString GetAutoRecoveryRoot();
return unformatted;
}
static QString GetAutoRecoveryRoot();
};
}
#endif // FILEFUNCTIONS_H
+349 -323
View File
@@ -5,446 +5,472 @@
#include "xmlutils.h"
namespace olive {
namespace olive
{
const QVector<QString> Html::kBlockTags = {
QStringLiteral("p"),
QStringLiteral("div")
};
const QVector<QString> Html::kBlockTags = { QStringLiteral("p"),
QStringLiteral("div") };
inline bool StrEquals(const QStringView &a, const QStringView &b)
{
return !a.compare(b, Qt::CaseInsensitive);
return !a.compare(b, Qt::CaseInsensitive);
}
QString Html::DocToHtml(const QTextDocument *doc)
{
QString html;
QXmlStreamWriter writer(&html);
QString html;
QXmlStreamWriter writer(&html);
//writer.setAutoFormatting(true);
//writer.setAutoFormatting(true);
for (auto it=doc->begin(); it!=doc->end(); it=it.next()) {
WriteBlock(&writer, it);
}
for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
WriteBlock(&writer, it);
}
return html;
return html;
}
struct HtmlNode {
QString tag;
QTextCharFormat format;
QString tag;
QTextCharFormat format;
};
QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
{
QTextCharFormat f;
QTextCharFormat f;
for (int i=0; i<stack.size(); i++) {
f.merge(stack.at(i).format);
}
for (int i = 0; i < stack.size(); i++) {
f.merge(stack.at(i).format);
}
return f;
return f;
}
void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
{
// Empty doc
doc->clear();
bool inside_block = true;
// Empty doc
doc->clear();
bool inside_block = true;
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
QTextCursor c(doc);
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
QTextCursor c(doc);
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
QXmlStreamReader reader(wrapped);
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
QXmlStreamReader reader(wrapped);
QVector<HtmlNode> fmt_stack;
QVector<HtmlNode> fmt_stack;
QTextCharFormat default_fmt;
default_fmt.setFontWeight(QFont::Normal);
fmt_stack.append({QStringLiteral("html"), default_fmt});
QTextCharFormat default_fmt;
default_fmt.setFontWeight(QFont::Normal);
fmt_stack.append({ QStringLiteral("html"), default_fmt });
QTextCharFormat current_fmt;
QTextCharFormat current_fmt;
while (!reader.atEnd()) {
reader.readNext();
while (!reader.atEnd()) {
reader.readNext();
if (reader.tokenType() == QXmlStreamReader::StartElement) {
if (reader.tokenType() == QXmlStreamReader::StartElement) {
QString tag = reader.name().toString().toLower();
QString tag = reader.name().toString().toLower();
fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) });
current_fmt = MergeHtmlFormats(fmt_stack);
fmt_stack.append({tag, ReadCharFormat(reader.attributes())});
current_fmt = MergeHtmlFormats(fmt_stack);
if (kBlockTags.contains(tag)) {
QTextBlockFormat block_fmt =
ReadBlockFormat(reader.attributes());
if (inside_block) {
c.setBlockFormat(block_fmt);
c.setBlockCharFormat(current_fmt);
} else {
c.insertBlock(block_fmt, current_fmt);
inside_block = true;
}
}
if (kBlockTags.contains(tag)) {
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes());
if (inside_block) {
c.setBlockFormat(block_fmt);
c.setBlockCharFormat(current_fmt);
} else {
c.insertBlock(block_fmt, current_fmt);
inside_block = true;
}
}
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
QString characters = reader.text().toString();
c.insertText(characters, current_fmt);
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
QString tag = reader.name().toString().toLower();
QString characters = reader.text().toString();
c.insertText(characters, current_fmt);
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
if (fmt_stack.at(i).tag == tag) {
fmt_stack.removeAt(i);
current_fmt = MergeHtmlFormats(fmt_stack);
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
if (kBlockTags.contains(tag)) {
inside_block = false;
}
break;
}
}
}
}
QString tag = reader.name().toString().toLower();
for (int i=fmt_stack.size()-1; i>=0; i--) {
if (fmt_stack.at(i).tag == tag) {
fmt_stack.removeAt(i);
current_fmt = MergeHtmlFormats(fmt_stack);
if (kBlockTags.contains(tag)) {
inside_block = false;
}
break;
}
}
}
}
if (reader.error()) {
qCritical() << "Failed to parse HTML:" << reader.errorString();
}
if (reader.error()) {
qCritical() << "Failed to parse HTML:" << reader.errorString();
}
}
void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
{
writer->writeStartElement(QStringLiteral("p"));
writer->writeStartElement(QStringLiteral("p"));
const QTextBlockFormat &fmt = block.blockFormat();
const QTextBlockFormat &fmt = block.blockFormat();
// Write block alignment
if (!(fmt.alignment() & Qt::AlignLeft)) {
if (fmt.alignment() & Qt::AlignRight) {
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("right"));
} else if (fmt.alignment() & Qt::AlignHCenter) {
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center"));
} else if (fmt.alignment() & Qt::AlignJustify) {
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify"));
}
}
// Write block alignment
if (!(fmt.alignment() & Qt::AlignLeft)) {
if (fmt.alignment() & Qt::AlignRight) {
writer->writeAttribute(QStringLiteral("align"),
QStringLiteral("right"));
} else if (fmt.alignment() & Qt::AlignHCenter) {
writer->writeAttribute(QStringLiteral("align"),
QStringLiteral("center"));
} else if (fmt.alignment() & Qt::AlignJustify) {
writer->writeAttribute(QStringLiteral("align"),
QStringLiteral("justify"));
}
}
// RTL support
if (block.textDirection() == Qt::RightToLeft) {
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
}
// RTL support
if (block.textDirection() == Qt::RightToLeft) {
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
}
// Write CSS attributes
QString style;
// Write CSS attributes
QString style;
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
WriteCSSProperty(&style, QStringLiteral("line-height"), QStringLiteral("%1%").arg(fmt.lineHeight()));
}
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
WriteCSSProperty(&style, QStringLiteral("line-height"),
QStringLiteral("%1%").arg(fmt.lineHeight()));
}
WriteCharFormat(&style, block.charFormat());
WriteCharFormat(&style, block.charFormat());
if (!style.isEmpty()) {
writer->writeAttribute(QStringLiteral("style"), style);
}
if (!style.isEmpty()) {
writer->writeAttribute(QStringLiteral("style"), style);
}
auto it = block.begin();
auto it = block.begin();
if (it != block.end()) {
for (; it!=block.end(); it++) {
WriteFragment(writer, it.fragment());
}
}
if (it != block.end()) {
for (; it != block.end(); it++) {
WriteFragment(writer, it.fragment());
}
}
writer->writeEndElement(); // p
writer->writeEndElement(); // p
}
void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment)
void Html::WriteFragment(QXmlStreamWriter *writer,
const QTextFragment &fragment)
{
const QTextCharFormat &fmt = fragment.charFormat();
const QTextCharFormat &fmt = fragment.charFormat();
writer->writeStartElement(QStringLiteral("span"));
writer->writeStartElement(QStringLiteral("span"));
// Write CSS attributes
QString style;
// Write CSS attributes
QString style;
WriteCharFormat(&style, fmt);
WriteCharFormat(&style, fmt);
if (!style.isEmpty()) {
writer->writeAttribute(QStringLiteral("style"), style);
}
if (!style.isEmpty()) {
writer->writeAttribute(QStringLiteral("style"), style);
}
QStringList lines = fragment.text().split(QChar::LineSeparator);
bool first_line = true;
foreach (const QString &l, lines) {
if (first_line) {
first_line = false;
} else {
writer->writeEmptyElement(QStringLiteral("br"));
}
writer->writeCharacters(l);
}
QStringList lines = fragment.text().split(QChar::LineSeparator);
bool first_line = true;
foreach (const QString &l, lines) {
if (first_line) {
first_line = false;
} else {
writer->writeEmptyElement(QStringLiteral("br"));
}
writer->writeCharacters(l);
}
writer->writeEndElement(); // span
writer->writeEndElement(); // span
}
void Html::WriteCSSProperty(QString *style, const QString &key, const QStringList &values)
void Html::WriteCSSProperty(QString *style, const QString &key,
const QStringList &values)
{
QString value;
foreach (QString v, values) {
if (v.contains(' ')) {
v = QStringLiteral("'%1'").arg(v);
}
QString value;
foreach (QString v, values) {
if (v.contains(' ')) {
v = QStringLiteral("'%1'").arg(v);
}
AppendStringAutoSpace(&value, v);
}
AppendStringAutoSpace(&value, v);
}
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
}
void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
{
QStringList families = fmt.fontFamilies().toStringList();
if (!families.isEmpty()) {
WriteCSSProperty(style, QStringLiteral("font-family"), families.first());
}
QStringList families = fmt.fontFamilies().toStringList();
if (!families.isEmpty()) {
WriteCSSProperty(style, QStringLiteral("font-family"),
families.first());
}
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
WriteCSSProperty(style, QStringLiteral("font-size"), QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
}
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
WriteCSSProperty(
style, QStringLiteral("font-size"),
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
}
if (fmt.hasProperty(QTextFormat::FontWeight)) {
WriteCSSProperty(style, QStringLiteral("font-weight"), QString::number(fmt.fontWeight() * 8));
}
if (fmt.hasProperty(QTextFormat::FontWeight)) {
WriteCSSProperty(style, QStringLiteral("font-weight"),
QString::number(fmt.fontWeight() * 8));
}
if (fmt.hasProperty(QTextFormat::FontItalic)) {
WriteCSSProperty(style, QStringLiteral("font-style"), fmt.fontItalic() ? QStringLiteral("italic") : QStringLiteral("normal"));
}
if (fmt.hasProperty(QTextFormat::FontItalic)) {
WriteCSSProperty(style, QStringLiteral("font-style"),
fmt.fontItalic() ? QStringLiteral("italic") :
QStringLiteral("normal"));
}
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
WriteCSSProperty(style, QStringLiteral("-ove-font-style"), fmt.fontStyleName().toString());
}
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
WriteCSSProperty(style, QStringLiteral("-ove-font-style"),
fmt.fontStyleName().toString());
}
QStringList deco;
QStringList deco;
if (fmt.fontUnderline()) {
deco.append(QStringLiteral("underline"));
}
if (fmt.fontUnderline()) {
deco.append(QStringLiteral("underline"));
}
if (fmt.fontStrikeOut()) {
deco.append(QStringLiteral("line-through"));
}
if (fmt.fontStrikeOut()) {
deco.append(QStringLiteral("line-through"));
}
if (fmt.fontOverline()) {
deco.append(QStringLiteral("overline"));
}
if (fmt.fontOverline()) {
deco.append(QStringLiteral("overline"));
}
if (!deco.isEmpty()) {
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
}
if (!deco.isEmpty()) {
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
}
if (fmt.foreground().style() != Qt::NoBrush) {
const QColor &color = fmt.foreground().color();
QString cs;
if (fmt.foreground().style() != Qt::NoBrush) {
const QColor &color = fmt.foreground().color();
QString cs;
if (color.alpha() == 255) {
cs = color.name();
} else if (color.alpha()) {
cs = QStringLiteral("rgba(%1, %2, %3, %4)").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()), QString::number(color.alphaF()));
}
if (color.alpha() == 255) {
cs = color.name();
} else if (color.alpha()) {
cs = QStringLiteral("rgba(%1, %2, %3, %4)")
.arg(QString::number(color.red()),
QString::number(color.green()),
QString::number(color.blue()),
QString::number(color.alphaF()));
}
WriteCSSProperty(style, QStringLiteral("color"), cs);
}
WriteCSSProperty(style, QStringLiteral("color"), cs);
}
if (fmt.fontCapitalization() != QFont::MixedCase) {
if (fmt.fontCapitalization() == QFont::SmallCaps) {
WriteCSSProperty(style, QStringLiteral("font-variant"), QStringLiteral("small-caps"));
// TODO: Add others
}
}
if (fmt.fontCapitalization() != QFont::MixedCase) {
if (fmt.fontCapitalization() == QFont::SmallCaps) {
WriteCSSProperty(style, QStringLiteral("font-variant"),
QStringLiteral("small-caps"));
// TODO: Add others
}
}
if (fmt.fontLetterSpacing() != 0.0) {
WriteCSSProperty(style, QStringLiteral("letter-spacing"), QStringLiteral("%1%").arg(QString::number(fmt.fontLetterSpacing())));
}
if (fmt.fontLetterSpacing() != 0.0) {
WriteCSSProperty(style, QStringLiteral("letter-spacing"),
QStringLiteral("%1%").arg(
QString::number(fmt.fontLetterSpacing())));
}
if (fmt.fontStretch() != 0) {
WriteCSSProperty(style, QStringLiteral("font-stretch"), QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
}
if (fmt.fontStretch() != 0) {
WriteCSSProperty(
style, QStringLiteral("font-stretch"),
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
}
}
QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
{
QTextCharFormat fmt;
QTextCharFormat fmt;
foreach (const QXmlStreamAttribute &attr, attributes) {
if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString());
foreach (const QXmlStreamAttribute &attr, attributes) {
if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString());
for (auto it=css.begin(); it!=css.end(); it++) {
const QString &first_val = it.value().first();
for (auto it = css.begin(); it != css.end(); it++) {
const QString &first_val = it.value().first();
if (it.key() == QStringLiteral("font-family")) {
fmt.setFontFamilies({first_val});
} else if (it.key() == QStringLiteral("font-size")) {
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) {
fmt.setFontPointSize(first_val.chopped(2).toDouble());
}
} else if (it.key() == QStringLiteral("font-weight")) {
fmt.setFontWeight(first_val.toInt()/8);
} else if (it.key() == QStringLiteral("font-style")) {
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic")));
} else if (it.key() == QStringLiteral("text-decoration")) {
foreach (const QString &v, it.value()) {
if (StrEquals(v, QStringLiteral("underline"))) {
fmt.setFontUnderline(true);
} else if (StrEquals(v, QStringLiteral("line-through"))) {
fmt.setFontStrikeOut(true);
} else if (StrEquals(v, QStringLiteral("overline"))) {
fmt.setFontOverline(true);
}
}
} else if (it.key() == QStringLiteral("color")) {
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) {
QString vals_only = first_val;
vals_only.remove(QStringLiteral("rgba"));
vals_only.remove(QStringLiteral("("));
vals_only.remove(QStringLiteral(")"));
QStringList rgba = vals_only.split(',');
if (rgba.size() == 4) {
QColor c;
c.setRedF(rgba.at(0).toDouble());
c.setGreenF(rgba.at(1).toDouble());
c.setBlueF(rgba.at(2).toDouble());
c.setAlphaF(rgba.at(3).toDouble());
fmt.setForeground(c);
}
} else {
fmt.setForeground(QColor(first_val));
}
} else if (it.key() == QStringLiteral("font-variant")) {
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
fmt.setFontCapitalization(QFont::SmallCaps);
}
} else if (it.key() == QStringLiteral("letter-spacing")) {
if (first_val.contains(QChar('%'))) {
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble());
}
} else if (it.key() == QStringLiteral("font-stretch")) {
if (first_val.contains(QChar('%'))) {
fmt.setFontStretch(first_val.chopped(1).toInt());
}
} else if (it.key() == QStringLiteral("-ove-font-style")) {
fmt.setFontStyleName(first_val);
}
}
}
}
return fmt;
if (it.key() == QStringLiteral("font-family")) {
fmt.setFontFamilies({ first_val });
} else if (it.key() == QStringLiteral("font-size")) {
if (first_val.endsWith(QStringLiteral("pt"),
Qt::CaseInsensitive)) {
fmt.setFontPointSize(first_val.chopped(2).toDouble());
}
} else if (it.key() == QStringLiteral("font-weight")) {
fmt.setFontWeight(first_val.toInt() / 8);
} else if (it.key() == QStringLiteral("font-style")) {
fmt.setFontItalic(
StrEquals(first_val, QStringLiteral("italic")));
} else if (it.key() == QStringLiteral("text-decoration")) {
foreach (const QString &v, it.value()) {
if (StrEquals(v, QStringLiteral("underline"))) {
fmt.setFontUnderline(true);
} else if (StrEquals(v,
QStringLiteral("line-through"))) {
fmt.setFontStrikeOut(true);
} else if (StrEquals(v, QStringLiteral("overline"))) {
fmt.setFontOverline(true);
}
}
} else if (it.key() == QStringLiteral("color")) {
if (first_val.startsWith(QStringLiteral("rgba"),
Qt::CaseInsensitive)) {
QString vals_only = first_val;
vals_only.remove(QStringLiteral("rgba"));
vals_only.remove(QStringLiteral("("));
vals_only.remove(QStringLiteral(")"));
QStringList rgba = vals_only.split(',');
if (rgba.size() == 4) {
QColor c;
c.setRedF(rgba.at(0).toDouble());
c.setGreenF(rgba.at(1).toDouble());
c.setBlueF(rgba.at(2).toDouble());
c.setAlphaF(rgba.at(3).toDouble());
fmt.setForeground(c);
}
} else {
fmt.setForeground(QColor(first_val));
}
} else if (it.key() == QStringLiteral("font-variant")) {
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
fmt.setFontCapitalization(QFont::SmallCaps);
}
} else if (it.key() == QStringLiteral("letter-spacing")) {
if (first_val.contains(QChar('%'))) {
fmt.setFontLetterSpacing(
first_val.chopped(1).toDouble());
}
} else if (it.key() == QStringLiteral("font-stretch")) {
if (first_val.contains(QChar('%'))) {
fmt.setFontStretch(first_val.chopped(1).toInt());
}
} else if (it.key() == QStringLiteral("-ove-font-style")) {
fmt.setFontStyleName(first_val);
}
}
}
}
return fmt;
}
QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
{
QTextBlockFormat block_fmt;
QTextBlockFormat block_fmt;
foreach (const QXmlStreamAttribute &attr, attributes) {
if (StrEquals(attr.name(), QStringLiteral("align"))) {
if (StrEquals(attr.value(), QStringLiteral("right"))) {
block_fmt.setAlignment(Qt::AlignRight);
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
block_fmt.setAlignment(Qt::AlignHCenter);
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
block_fmt.setAlignment(Qt::AlignJustify);
}
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
block_fmt.setLayoutDirection(Qt::RightToLeft);
}
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString());
foreach (const QXmlStreamAttribute &attr, attributes) {
if (StrEquals(attr.name(), QStringLiteral("align"))) {
if (StrEquals(attr.value(), QStringLiteral("right"))) {
block_fmt.setAlignment(Qt::AlignRight);
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
block_fmt.setAlignment(Qt::AlignHCenter);
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
block_fmt.setAlignment(Qt::AlignJustify);
}
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
block_fmt.setLayoutDirection(Qt::RightToLeft);
}
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
auto css = GetCSSFromStyle(attr.value().toString());
for (auto it=css.begin(); it!=css.end(); it++) {
if (it.key() == QStringLiteral("line-height")) {
const QString &first_val = it.value().constFirst();
if (first_val.contains(QChar('%'))) {
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight);
}
}
}
}
}
for (auto it = css.begin(); it != css.end(); it++) {
if (it.key() == QStringLiteral("line-height")) {
const QString &first_val = it.value().constFirst();
if (first_val.contains(QChar('%'))) {
block_fmt.setLineHeight(
first_val.chopped(1).toDouble(),
QTextBlockFormat::ProportionalHeight);
}
}
}
}
}
return block_fmt;
return block_fmt;
}
void Html::AppendStringAutoSpace(QString *s, const QString &append)
{
if (!s->isEmpty()) {
s->append(QChar(' '));
}
if (!s->isEmpty()) {
s->append(QChar(' '));
}
s->append(append);
s->append(append);
}
QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
{
QMap<QString, QStringList> map;
QMap<QString, QStringList> map;
QStringList list = s.split(QChar(';'));
QStringList list = s.split(QChar(';'));
foreach (const QString &a, list) {
QStringList kv = a.split(QChar(':'));
foreach (const QString &a, list) {
QStringList kv = a.split(QChar(':'));
if (kv.size() != 2) {
continue;
}
if (kv.size() != 2) {
continue;
}
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
// match. Also commas should be filtered out.
QStringList values;
const QString &val = kv.at(1);
QChar in_quote(0);
QString current_str;
for (int i=0; i<val.size(); i++) {
const QChar &current_char = val.at(i);
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
// match. Also commas should be filtered out.
QStringList values;
const QString &val = kv.at(1);
QChar in_quote(0);
QString current_str;
for (int i = 0; i < val.size(); i++) {
const QChar &current_char = val.at(i);
if (!in_quote.isNull()) {
// If inside quotes and character isn't quote, indiscriminately append char
if (current_char == in_quote) {
in_quote = QChar(0);
} else {
current_str.append(current_char);
}
} else if (current_char.isSpace() || current_char == QChar(',')) {
// Dump current
if (!current_str.isEmpty()) {
values.append(current_str);
current_str.clear();
}
} else if (in_quote.isNull() && (current_char == QChar('\'') || current_char == QChar('"'))) {
in_quote = current_char;
} else {
current_str.append(current_char);
}
}
if (!in_quote.isNull()) {
// If inside quotes and character isn't quote, indiscriminately append char
if (current_char == in_quote) {
in_quote = QChar(0);
} else {
current_str.append(current_char);
}
} else if (current_char.isSpace() || current_char == QChar(',')) {
// Dump current
if (!current_str.isEmpty()) {
values.append(current_str);
current_str.clear();
}
} else if (in_quote.isNull() && (current_char == QChar('\'') ||
current_char == QChar('"'))) {
in_quote = current_char;
} else {
current_str.append(current_char);
}
}
if (!current_str.isEmpty()) {
values.append(current_str);
}
if (!current_str.isEmpty()) {
values.append(current_str);
}
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
if (values.isEmpty()) {
values.append(QString());
}
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
if (values.isEmpty()) {
values.append(QString());
}
map[kv.at(0).trimmed().toLower()] = values;
}
map[kv.at(0).trimmed().toLower()] = values;
}
return map;
return map;
}
}
+22 -17
View File
@@ -6,7 +6,8 @@
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
namespace olive {
namespace olive
{
/**
* @brief Functions for converting HTML to QTextDocument and vice versa
@@ -26,33 +27,37 @@ namespace olive {
*/
class Html {
public:
static QString DocToHtml(const QTextDocument *doc);
static QString DocToHtml(const QTextDocument *doc);
static void HtmlToDoc(QTextDocument *doc, const QString &html);
static void HtmlToDoc(QTextDocument *doc, const QString &html);
private:
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
static void WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment);
static void WriteFragment(QXmlStreamWriter *writer,
const QTextFragment &fragment);
static void WriteCSSProperty(QString *style, const QString &key, const QStringList &value);
static void WriteCSSProperty(QString *style, const QString &key, const QString &value)
{
WriteCSSProperty(style, key, QStringList({value}));
}
static void WriteCSSProperty(QString *style, const QString &key,
const QStringList &value);
static void WriteCSSProperty(QString *style, const QString &key,
const QString &value)
{
WriteCSSProperty(style, key, QStringList({ value }));
}
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
static QTextCharFormat ReadCharFormat(const QXmlStreamAttributes &attributes);
static QTextCharFormat
ReadCharFormat(const QXmlStreamAttributes &attributes);
static QTextBlockFormat ReadBlockFormat(const QXmlStreamAttributes &attributes);
static QTextBlockFormat
ReadBlockFormat(const QXmlStreamAttributes &attributes);
static void AppendStringAutoSpace(QString *s, const QString &append);
static void AppendStringAutoSpace(QString *s, const QString &append);
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
static const QVector<QString> kBlockTags;
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
static const QVector<QString> kBlockTags;
};
}
+9 -8
View File
@@ -2,29 +2,30 @@
#include <QMutex>
namespace olive {
namespace olive
{
uint64_t job_time_index = 0;
QMutex job_time_mutex;
JobTime::JobTime()
{
Acquire();
Acquire();
}
void JobTime::Acquire()
{
job_time_mutex.lock();
job_time_mutex.lock();
value_ = job_time_index;
job_time_index++;
value_ = job_time_index;
job_time_index++;
job_time_mutex.unlock();
job_time_mutex.unlock();
}
}
QDebug operator<<(QDebug debug, const olive::JobTime& r)
QDebug operator<<(QDebug debug, const olive::JobTime &r)
{
return debug.space() << r.value();
return debug.space() << r.value();
}
+35 -36
View File
@@ -4,58 +4,57 @@
#include <QDebug>
#include <stdint.h>
namespace olive {
class JobTime
namespace olive
{
class JobTime {
public:
JobTime();
JobTime();
void Acquire();
void Acquire();
uint64_t value() const
{
return value_;
}
uint64_t value() const
{
return value_;
}
bool operator==(const JobTime &rhs) const
{
return value_ == rhs.value_;
}
bool operator==(const JobTime &rhs) const
{
return value_ == rhs.value_;
}
bool operator!=(const JobTime &rhs) const
{
return value_ != rhs.value_;
}
bool operator!=(const JobTime &rhs) const
{
return value_ != rhs.value_;
}
bool operator<(const JobTime &rhs) const
{
return value_ < rhs.value_;
}
bool operator<(const JobTime &rhs) const
{
return value_ < rhs.value_;
}
bool operator>(const JobTime &rhs) const
{
return value_ > rhs.value_;
}
bool operator>(const JobTime &rhs) const
{
return value_ > rhs.value_;
}
bool operator<=(const JobTime &rhs) const
{
return value_ <= rhs.value_;
}
bool operator<=(const JobTime &rhs) const
{
return value_ <= rhs.value_;
}
bool operator>=(const JobTime &rhs) const
{
return value_ >= rhs.value_;
}
bool operator>=(const JobTime &rhs) const
{
return value_ >= rhs.value_;
}
private:
uint64_t value_;
uint64_t value_;
};
}
QDebug operator<<(QDebug debug, const olive::JobTime& r);
QDebug operator<<(QDebug debug, const olive::JobTime &r);
Q_DECLARE_METATYPE(olive::JobTime)
+7 -6
View File
@@ -21,20 +21,21 @@
#ifndef LERP_H
#define LERP_H
template<typename T>
template <typename T>
/**
* @brief Linearly interpolate a value between a and b using t
*
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
* in between a and b at that point linearly.
*/
T lerp(T a, T b, double t) {
return (a * (1.0 - t)) + (b * t);
T lerp(T a, T b, double t)
{
return (a * (1.0 - t)) + (b * t);
}
template<typename T>
T lerp(T a, T b, float t) {
return (a * (1.0f - t)) + (b * t);
template <typename T> T lerp(T a, T b, float t)
{
return (a * (1.0f - t)) + (b * t);
}
#endif // LERP_H
+249 -245
View File
@@ -31,7 +31,8 @@
#include "common/define.h"
namespace olive {
namespace olive
{
/**
* @brief MemoryPool base class
@@ -46,386 +47,389 @@ namespace olive {
* `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
*/
class MemoryPool : public QObject
{
Q_OBJECT
class MemoryPool : public QObject {
Q_OBJECT
public:
/**
/**
* @brief Constructor
* @param element_count
*
* Number of elements per arena
*/
MemoryPool(int element_count)
{
element_count_ = element_count;
MemoryPool(int element_count)
{
element_count_ = element_count;
clear_timer_ = new QTimer();
clear_timer_->setInterval(kMaxEmptyArenaLife);
clear_timer_->moveToThread(qApp->thread());
connect(clear_timer_, &QTimer::timeout, this, &MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
}
clear_timer_ = new QTimer();
clear_timer_->setInterval(kMaxEmptyArenaLife);
clear_timer_->moveToThread(qApp->thread());
connect(clear_timer_, &QTimer::timeout, this,
&MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
}
/**
/**
* @brief Destructor
*
* Deletes all arenas.
*/
virtual ~MemoryPool()
{
Clear();
clear_timer_->deleteLater();
}
virtual ~MemoryPool()
{
Clear();
clear_timer_->deleteLater();
}
DISABLE_COPY_MOVE(MemoryPool)
DISABLE_COPY_MOVE(MemoryPool)
/**
/**
* @brief Clears all arenas, freeing all of their memory
*
* Note that this function is not safe, any elements that are still out there will be invalid
* and accessing them will cause a crash. You'll need to make sure all elements are already
* relinquished before then.
*/
void Clear()
{
qDeleteAll(arenas_);
arenas_.clear();
}
void Clear()
{
qDeleteAll(arenas_);
arenas_.clear();
}
/**
/**
* @brief Returns whether any arenas are successfully allocated
*/
inline bool IsAllocated() const
{
return !arenas_.empty();
}
inline bool IsAllocated() const
{
return !arenas_.empty();
}
/**
/**
* @brief Returns current number of allocated arenas
*/
inline int GetArenaCount() const
{
return arenas_.size();
}
inline int GetArenaCount() const
{
return arenas_.size();
}
class Arena;
class Arena;
/**
/**
* @brief A handle for a chunk of memory in an arena
*
* Calling Get() on the pool or arena will return a shared pointer to an element which will contain a pointer to
* the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the memory
* is released back into the pool so it can be used by another class.
*/
class Element {
public:
/**
class Element {
public:
/**
* @brief Element Constructor
*
* There is no need to use this outside of the memory pool's internal functions.
*/
Element(Arena* parent, uint8_t* data)
{
parent_ = parent;
data_ = data;
accessed_ = QDateTime::currentMSecsSinceEpoch();
}
Element(Arena *parent, uint8_t *data)
{
parent_ = parent;
data_ = data;
accessed_ = QDateTime::currentMSecsSinceEpoch();
}
/**
/**
* @brief Element Destructor
*
* Automatically releases this element's memory back to the arena it was retrieved from.
*/
~Element()
{
release();
}
~Element()
{
release();
}
DISABLE_COPY_MOVE(Element)
DISABLE_COPY_MOVE(Element)
/**
/**
* @brief Access data represented in the pool
*/
inline uint8_t* data() const
{
return data_;
}
inline uint8_t *data() const
{
return data_;
}
inline const int64_t& timestamp() const
{
return timestamp_;
}
inline const int64_t &timestamp() const
{
return timestamp_;
}
inline void set_timestamp(const int64_t& timestamp)
{
timestamp_ = timestamp;
}
inline void set_timestamp(const int64_t &timestamp)
{
timestamp_ = timestamp;
}
/**
/**
* @brief Register that this element has been accessed
*
* \see last_accessed()
*/
inline void access()
{
accessed_ = QDateTime::currentMSecsSinceEpoch();
}
inline void access()
{
accessed_ = QDateTime::currentMSecsSinceEpoch();
}
/**
/**
* @brief Returns the last time `access()` was called on this function
*
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of
* time, it can probably be freed back into the pool). This requires all usages to call `access()`.
*/
inline const int64_t& last_accessed() const
{
return accessed_;
}
inline const int64_t &last_accessed() const
{
return accessed_;
}
void release()
{
if (data_) {
parent_->Release(this);
data_ = nullptr;
}
}
void release()
{
if (data_) {
parent_->Release(this);
data_ = nullptr;
}
}
private:
Arena* parent_;
private:
Arena *parent_;
uint8_t* data_;
uint8_t *data_;
int64_t timestamp_;
int64_t timestamp_;
int64_t accessed_;
int64_t accessed_;
};
};
using ElementPtr = std::shared_ptr<Element>;
using ElementPtr = std::shared_ptr<Element>;
/**
/**
* @brief A memory pool arena - a subsection of memory
*
* The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of
* an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and continue
* providing memory (and freeing arenas when they're no longer in use).
*/
class Arena {
public:
Arena(MemoryPool* parent)
{
parent_ = parent;
data_ = nullptr;
allocated_sz_ = 0;
empty_time_ = QDateTime::currentMSecsSinceEpoch();
}
class Arena {
public:
Arena(MemoryPool *parent)
{
parent_ = parent;
data_ = nullptr;
allocated_sz_ = 0;
empty_time_ = QDateTime::currentMSecsSinceEpoch();
}
~Arena()
{
std::list<Element*> copy = lent_elements_;
foreach (Element* e, copy) {
e->release();
}
~Arena()
{
std::list<Element *> copy = lent_elements_;
foreach (Element *e, copy) {
e->release();
}
delete [] data_;
}
delete[] data_;
}
DISABLE_COPY_MOVE(Arena)
DISABLE_COPY_MOVE(Arena)
/**
/**
* @brief Returns an element if there is free memory to do so
*/
ElementPtr Get()
{
QMutexLocker locker(&lock_);
ElementPtr Get()
{
QMutexLocker locker(&lock_);
for (int i=0;i<available_.size();i++) {
if (available_.at(i)) {
// This buffer is available
available_.replace(i, false);
for (int i = 0; i < available_.size(); i++) {
if (available_.at(i)) {
// This buffer is available
available_.replace(i, false);
ElementPtr e = std::make_shared<Element>(this,
reinterpret_cast<uint8_t*>(data_ + i * element_sz_));
lent_elements_.push_back(e.get());
ElementPtr e = std::make_shared<Element>(
this,
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
lent_elements_.push_back(e.get());
return e;
}
}
return e;
}
}
return nullptr;
}
return nullptr;
}
/**
/**
* @brief Releases an element back into the pool for use elsewhere
*/
void Release(Element* e)
{
QMutexLocker locker(&lock_);
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_);
void Release(Element *e)
{
QMutexLocker locker(&lock_);
quintptr diff = reinterpret_cast<quintptr>(e->data()) -
reinterpret_cast<quintptr>(data_);
int index = diff / element_sz_;
int index = diff / element_sz_;
available_.replace(index, true);
available_.replace(index, true);
lent_elements_.remove(e);
lent_elements_.remove(e);
if (lent_elements_.empty()) {
empty_time_ = QDateTime::currentMSecsSinceEpoch();
}
}
if (lent_elements_.empty()) {
empty_time_ = QDateTime::currentMSecsSinceEpoch();
}
}
int GetUsageCount()
{
QMutexLocker locker(&lock_);
return lent_elements_.size();
}
int GetUsageCount()
{
QMutexLocker locker(&lock_);
return lent_elements_.size();
}
bool Allocate(size_t ele_sz, size_t nb_elements)
{
if (IsAllocated()) {
return true;
}
bool Allocate(size_t ele_sz, size_t nb_elements)
{
if (IsAllocated()) {
return true;
}
element_sz_ = ele_sz;
element_sz_ = ele_sz;
allocated_sz_ = element_sz_ * nb_elements;
allocated_sz_ = element_sz_ * nb_elements;
if ((data_ = new uint8_t[allocated_sz_])) {
available_.resize(nb_elements);
available_.fill(true);
if ((data_ = new uint8_t[allocated_sz_])) {
available_.resize(nb_elements);
available_.fill(true);
return true;
} else {
available_.clear();
return true;
} else {
available_.clear();
return false;
}
}
return false;
}
}
inline int GetElementCount() const
{
return available_.size();
}
inline int GetElementCount() const
{
return available_.size();
}
inline bool IsAllocated() const
{
return data_;
}
inline bool IsAllocated() const
{
return data_;
}
inline qint64 GetTimeArenaWasMadeEmpty()
{
QMutexLocker locker(&lock_);
return empty_time_;
}
inline qint64 GetTimeArenaWasMadeEmpty()
{
QMutexLocker locker(&lock_);
return empty_time_;
}
private:
MemoryPool* parent_;
private:
MemoryPool *parent_;
uint8_t* data_;
uint8_t *data_;
size_t allocated_sz_;
size_t allocated_sz_;
QVector<bool> available_;
QVector<bool> available_;
QMutex lock_;
QMutex lock_;
size_t element_sz_;
size_t element_sz_;
std::list<Element*> lent_elements_;
std::list<Element *> lent_elements_;
qint64 empty_time_;
qint64 empty_time_;
};
};
/**
/**
* @brief Retrieves an element from an available arena
*/
ElementPtr Get()
{
QMutexLocker locker(&lock_);
ElementPtr Get()
{
QMutexLocker locker(&lock_);
// Attempt to get an element from an arena
foreach (Arena* a, arenas_) {
ElementPtr e = a->Get();
// Attempt to get an element from an arena
foreach (Arena *a, arenas_) {
ElementPtr e = a->Get();
if (e) {
return e;
}
}
if (e) {
return e;
}
}
// All arenas were empty, we'll need to create a new one
if (arenas_.empty()) {
qDebug() << "No arenas, creating new...";
} else {
qDebug() << "All arenas are full, creating new...";
}
// All arenas were empty, we'll need to create a new one
if (arenas_.empty()) {
qDebug() << "No arenas, creating new...";
} else {
qDebug() << "All arenas are full, creating new...";
}
size_t ele_sz = GetElementSize();
size_t ele_sz = GetElementSize();
if (!ele_sz) {
qCritical() << "Failed to create arena, element size was 0";
return nullptr;
}
if (!ele_sz) {
qCritical() << "Failed to create arena, element size was 0";
return nullptr;
}
if (element_count_ <= 0) {
qCritical() << "Failed to create arena, element count was invalid:" << element_count_;
return nullptr;
}
if (element_count_ <= 0) {
qCritical() << "Failed to create arena, element count was invalid:"
<< element_count_;
return nullptr;
}
Arena* a = new Arena(this);
if (!a->Allocate(ele_sz, element_count_)) {
qCritical() << "Failed to create arena, allocation failed. Out of memory?";
delete a;
return nullptr;
}
Arena *a = new Arena(this);
if (!a->Allocate(ele_sz, element_count_)) {
qCritical()
<< "Failed to create arena, allocation failed. Out of memory?";
delete a;
return nullptr;
}
arenas_.push_back(a);
return a->Get();
}
arenas_.push_back(a);
return a->Get();
}
protected:
/**
/**
* @brief The size of each element
*
* Override this to use a custom size (e.g. a char array where T = char but the element size is > 1)
*/
virtual size_t GetElementSize()
{
return sizeof(uint8_t);
}
virtual size_t GetElementSize()
{
return sizeof(uint8_t);
}
private:
int element_count_;
int element_count_;
std::list<Arena*> arenas_;
std::list<Arena *> arenas_;
QMutex lock_;
QMutex lock_;
QTimer *clear_timer_;
QTimer *clear_timer_;
static const qint64 kMaxEmptyArenaLife = 5000;
static const qint64 kMaxEmptyArenaLife = 5000;
private slots:
void ClearEmptyArenas()
{
QMutexLocker locker(&lock_);
void ClearEmptyArenas()
{
QMutexLocker locker(&lock_);
const qint64 min_time = QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
const qint64 min_time =
QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
for (auto it=arenas_.begin(); it!=arenas_.end(); ) {
Arena* arena = (*it);
if (arena->GetUsageCount() == 0 && arena->GetTimeArenaWasMadeEmpty() <= min_time) {
qDebug() << "Removing an empty arena";
delete arena;
it = arenas_.erase(it);
} else {
it++;
}
}
}
for (auto it = arenas_.begin(); it != arenas_.end();) {
Arena *arena = (*it);
if (arena->GetUsageCount() == 0 &&
arena->GetTimeArenaWasMadeEmpty() <= min_time) {
qDebug() << "Removing an empty arena";
delete arena;
it = arenas_.erase(it);
} else {
it++;
}
}
}
};
}
+19 -18
View File
@@ -20,28 +20,29 @@
#include "ocioutils.h"
namespace olive {
namespace olive
{
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
{
switch (format) {
case PixelFormat::U8:
return OCIO::BIT_DEPTH_UINT8;
case PixelFormat::U16:
return OCIO::BIT_DEPTH_UINT16;
break;
case PixelFormat::F16:
return OCIO::BIT_DEPTH_F16;
break;
case PixelFormat::F32:
return OCIO::BIT_DEPTH_F32;
break;
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
switch (format) {
case PixelFormat::U8:
return OCIO::BIT_DEPTH_UINT8;
case PixelFormat::U16:
return OCIO::BIT_DEPTH_UINT16;
break;
case PixelFormat::F16:
return OCIO::BIT_DEPTH_F16;
break;
case PixelFormat::F32:
return OCIO::BIT_DEPTH_F32;
break;
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
return OCIO::BIT_DEPTH_UNKNOWN;
return OCIO::BIT_DEPTH_UNKNOWN;
}
}
+4 -4
View File
@@ -26,12 +26,12 @@ namespace OCIO = OCIO_NAMESPACE;
#include "render/videoparams.h"
namespace olive {
class OCIOUtils
namespace olive
{
class OCIOUtils {
public:
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
};
}
+37 -41
View File
@@ -22,62 +22,58 @@
#include <QDebug>
namespace olive {
void OIIOUtils::FrameToBuffer(const Frame* frame, OIIO::ImageBuf *buf)
namespace olive
{
buf->set_pixels(OIIO::ROI(),
buf->spec().format,
frame->const_data(),
OIIO::AutoStride,
frame->linesize_bytes());
void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
{
buf->set_pixels(OIIO::ROI(), buf->spec().format, frame->const_data(),
OIIO::AutoStride, frame->linesize_bytes());
}
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame* frame)
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame *frame)
{
buf->get_pixels(OIIO::ROI(),
buf->spec().format,
frame->data(),
OIIO::AutoStride,
frame->linesize_bytes());
buf->get_pixels(OIIO::ROI(), buf->spec().format, frame->data(),
OIIO::AutoStride, frame->linesize_bytes());
}
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
{
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1));
return rational::fromDouble(
spec.get_float_attribute("PixelAspectRatio", 1));
}
PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
{
switch (type) {
case OIIO::TypeDesc::UNKNOWN:
case OIIO::TypeDesc::NONE:
break;
switch (type) {
case OIIO::TypeDesc::UNKNOWN:
case OIIO::TypeDesc::NONE:
break;
case OIIO::TypeDesc::INT8:
case OIIO::TypeDesc::INT16:
case OIIO::TypeDesc::INT32:
case OIIO::TypeDesc::UINT32:
case OIIO::TypeDesc::INT64:
case OIIO::TypeDesc::UINT64:
case OIIO::TypeDesc::STRING:
case OIIO::TypeDesc::PTR:
case OIIO::TypeDesc::LASTBASE:
case OIIO::TypeDesc::DOUBLE:
qDebug() << "Tried to use unknown OIIO base type";
break;
case OIIO::TypeDesc::INT8:
case OIIO::TypeDesc::INT16:
case OIIO::TypeDesc::INT32:
case OIIO::TypeDesc::UINT32:
case OIIO::TypeDesc::INT64:
case OIIO::TypeDesc::UINT64:
case OIIO::TypeDesc::STRING:
case OIIO::TypeDesc::PTR:
case OIIO::TypeDesc::LASTBASE:
case OIIO::TypeDesc::DOUBLE:
qDebug() << "Tried to use unknown OIIO base type";
break;
case OIIO::TypeDesc::UINT8:
return PixelFormat::U8;
case OIIO::TypeDesc::UINT16:
return PixelFormat::U16;
case OIIO::TypeDesc::HALF:
return PixelFormat::F16;
case OIIO::TypeDesc::FLOAT:
return PixelFormat::F32;
}
case OIIO::TypeDesc::UINT8:
return PixelFormat::U8;
case OIIO::TypeDesc::UINT16:
return PixelFormat::U16;
case OIIO::TypeDesc::HALF:
return PixelFormat::F16;
case OIIO::TypeDesc::FLOAT:
return PixelFormat::F32;
}
return PixelFormat::INVALID;
return PixelFormat::INVALID;
}
}
+24 -23
View File
@@ -27,37 +27,38 @@
#include "codec/frame.h"
#include "render/videoparams.h"
namespace olive {
namespace olive
{
class OIIOUtils {
public:
static OIIO::TypeDesc::BASETYPE GetOIIOBaseTypeFromFormat(PixelFormat format)
{
switch (format) {
case PixelFormat::U8:
return OIIO::TypeDesc::UINT8;
case PixelFormat::U16:
return OIIO::TypeDesc::UINT16;
case PixelFormat::F16:
return OIIO::TypeDesc::HALF;
case PixelFormat::F32:
return OIIO::TypeDesc::FLOAT;
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
static OIIO::TypeDesc::BASETYPE
GetOIIOBaseTypeFromFormat(PixelFormat format)
{
switch (format) {
case PixelFormat::U8:
return OIIO::TypeDesc::UINT8;
case PixelFormat::U16:
return OIIO::TypeDesc::UINT16;
case PixelFormat::F16:
return OIIO::TypeDesc::HALF;
case PixelFormat::F32:
return OIIO::TypeDesc::FLOAT;
case PixelFormat::INVALID:
case PixelFormat::COUNT:
break;
}
return OIIO::TypeDesc::UNKNOWN;
}
return OIIO::TypeDesc::UNKNOWN;
}
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf* buf);
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf);
static void BufferToFrame(OIIO::ImageBuf* buf, Frame* frame);
static void BufferToFrame(OIIO::ImageBuf *buf, Frame *frame);
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec);
};
}
+16 -15
View File
@@ -25,30 +25,31 @@
#include "common/define.h"
namespace olive {
namespace olive
{
uint32_t ceil_to_power_of_2(uint32_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
return v;
}
uint32_t floor_to_power_of_2(uint32_t x)
{
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
return x - (x >> 1);
return x - (x >> 1);
}
}
+125 -114
View File
@@ -22,192 +22,203 @@
#include <QDebug>
namespace olive {
namespace olive
{
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString& s) {
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString &s)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
return fm.width(s);
return fm.width(s);
#else
return fm.horizontalAdvance(s);
return fm.horizontalAdvance(s);
#endif
}
QFrame *QtUtils::CreateHorizontalLine()
{
QFrame* horizontal_line = new QFrame();
horizontal_line->setFrameShape(QFrame::HLine);
horizontal_line->setFrameShadow(QFrame::Sunken);
return horizontal_line;
QFrame *horizontal_line = new QFrame();
horizontal_line->setFrameShape(QFrame::HLine);
horizontal_line->setFrameShadow(QFrame::Sunken);
return horizontal_line;
}
QFrame *QtUtils::CreateVerticalLine()
{
QFrame *l = CreateHorizontalLine();
l->setFrameShape(QFrame::VLine);
return l;
QFrame *l = CreateHorizontalLine();
l->setFrameShape(QFrame::VLine);
return l;
}
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons)
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon,
const QString &title, const QString &message,
QMessageBox::StandardButtons buttons)
{
QMessageBox b(parent);
b.setIcon(icon);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(title);
b.setText(message);
QMessageBox b(parent);
b.setIcon(icon);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(title);
b.setText(message);
uint mask = QMessageBox::FirstButton;
while (mask <= QMessageBox::LastButton) {
uint sb = buttons & mask;
if (sb) {
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
}
mask <<= 1;
}
uint mask = QMessageBox::FirstButton;
while (mask <= QMessageBox::LastButton) {
uint sb = buttons & mask;
if (sb) {
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
}
mask <<= 1;
}
return b.exec();
return b.exec();
}
QDateTime QtUtils::GetCreationDate(const QFileInfo &info)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
return info.created();
return info.created();
#else
QDateTime t = info.birthTime();
if (!t.isValid()) {
t = info.metadataChangeTime();
}
return t;
QDateTime t = info.birthTime();
if (!t.isValid()) {
t = info.metadataChangeTime();
}
return t;
#endif
}
QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
{
return dt.toString(Qt::TextDate);
return dt.toString(Qt::TextDate);
}
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width)
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width)
{
QStringList list;
QStringList list;
QStringList lines = s.split('\n');
QStringList lines = s.split('\n');
// Iterate every line
for (int i=0; i<lines.size(); i++) {
QString this_line = lines.at(i);
// Iterate every line
for (int i = 0; i < lines.size(); i++) {
QString this_line = lines.at(i);
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) {
int old_size = this_line.size();
int hard_break = -1;
while (this_line.size() > 1 &&
QFontMetricsWidth(fm, this_line) >= bounding_width) {
int old_size = this_line.size();
int hard_break = -1;
for (int j=this_line.size()-1; j>=0; j--) {
const QChar &char_test = this_line.at(j);
for (int j = this_line.size() - 1; j >= 0; j--) {
const QChar &char_test = this_line.at(j);
if (char_test.isSpace()
|| char_test == '-') {
if (QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
if (!char_test.isSpace()) {
j++;
}
if (char_test.isSpace() || char_test == '-') {
if (QFontMetricsWidth(fm, this_line.left(j)) <
bounding_width) {
if (!char_test.isSpace()) {
j++;
}
QString chopped = this_line.left(j);
QString chopped = this_line.left(j);
list.append(chopped);
list.append(chopped);
while (j < this_line.size() && this_line.at(j).isSpace()) {
j++;
}
this_line.remove(0, j);
break;
}
} else if (hard_break == -1 && QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
// In case we can't find a better place to split, split at the earliest time the line
// goes under the width limit
hard_break = j;
}
}
while (j < this_line.size() &&
this_line.at(j).isSpace()) {
j++;
}
this_line.remove(0, j);
break;
}
} else if (hard_break == -1 &&
QFontMetricsWidth(fm, this_line.left(j)) <
bounding_width) {
// In case we can't find a better place to split, split at the earliest time the line
// goes under the width limit
hard_break = j;
}
}
if (old_size == this_line.size()) {
if (hard_break != -1) {
list.append(this_line.left(hard_break));
this_line.remove(0, hard_break);
} else {
qWarning() << "Failed to find anywhere to wrap. Returning full line.";
break;
}
}
}
if (old_size == this_line.size()) {
if (hard_break != -1) {
list.append(this_line.left(hard_break));
this_line.remove(0, hard_break);
} else {
qWarning()
<< "Failed to find anywhere to wrap. Returning full line.";
break;
}
}
}
if (!this_line.isEmpty()) {
list.append(this_line);
}
}
if (!this_line.isEmpty()) {
list.append(this_line);
}
}
return list;
return list;
}
Qt::KeyboardModifiers QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
Qt::KeyboardModifiers
QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
{
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
return e;
}
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
return e;
}
if (e & Qt::ShiftModifier) {
e |= Qt::ControlModifier;
e &= ~Qt::ShiftModifier;
} else if (e & Qt::ControlModifier) {
e |= Qt::ShiftModifier;
e &= ~Qt::ControlModifier;
}
if (e & Qt::ShiftModifier) {
e |= Qt::ControlModifier;
e &= ~Qt::ShiftModifier;
} else if (e & Qt::ControlModifier) {
e |= Qt::ShiftModifier;
e &= ~Qt::ControlModifier;
}
return e;
return e;
}
void QtUtils::SetComboBoxData(QComboBox *cb, int data)
{
for (int i=0; i<cb->count(); i++) {
if (cb->itemData(i).toInt() == data) {
cb->setCurrentIndex(i);
break;
}
}
for (int i = 0; i < cb->count(); i++) {
if (cb->itemData(i).toInt() == data) {
cb->setCurrentIndex(i);
break;
}
}
}
void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data)
{
for (int i=0; i<cb->count(); i++) {
if (cb->itemData(i).toString() == data) {
cb->setCurrentIndex(i);
break;
}
}
for (int i = 0; i < cb->count(); i++) {
if (cb->itemData(i).toString() == data) {
cb->setCurrentIndex(i);
break;
}
}
}
QColor QtUtils::toQColor(const core::Color &i)
{
QColor c;
QColor c;
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
return c;
return c;
}
namespace core {
namespace core
{
uint qHash(const core::rational &r, uint seed)
{
return ::qHash(r.toDouble(), seed);
return ::qHash(r.toDouble(), seed);
}
uint qHash(const core::TimeRange &r, uint seed)
{
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
}
}
}
+43 -40
View File
@@ -29,76 +29,79 @@
#include <QFrame>
#include <QMessageBox>
namespace olive {
namespace olive
{
class QtUtils {
public:
/**
/**
* @brief Retrieves the width of a string according to certain QFontMetrics
*
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
* earlier.
*/
static int QFontMetricsWidth(QFontMetrics fm, const QString& s);
static int QFontMetricsWidth(QFontMetrics fm, const QString &s);
static QFrame* CreateHorizontalLine();
static QFrame *CreateHorizontalLine();
static QFrame* CreateVerticalLine();
static QFrame *CreateVerticalLine();
static int MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
static int MsgBox(QWidget *parent, QMessageBox::Icon icon,
const QString &title, const QString &message,
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
static QDateTime GetCreationDate(const QFileInfo &info);
static QDateTime GetCreationDate(const QFileInfo &info);
static QString GetFormattedDateTime(const QDateTime &dt);
static QString GetFormattedDateTime(const QDateTime &dt);
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width);
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm,
int bounding_width);
static Qt::KeyboardModifiers FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
static Qt::KeyboardModifiers
FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
static void SetComboBoxData(QComboBox *cb, int data);
static void SetComboBoxData(QComboBox *cb, const QString &data);
static void SetComboBoxData(QComboBox *cb, int data);
static void SetComboBoxData(QComboBox *cb, const QString &data);
template <typename T>
static T *GetParentOfType(const QObject *child)
{
QObject *t = child->parent();
template <typename T> static T *GetParentOfType(const QObject *child)
{
QObject *t = child->parent();
while (t) {
if (T *p = dynamic_cast<T*>(t)) {
return p;
}
t = t->parent();
}
while (t) {
if (T *p = dynamic_cast<T *>(t)) {
return p;
}
t = t->parent();
}
return nullptr;
}
return nullptr;
}
static QColor toQColor(const core::Color &c);
static QColor toQColor(const core::Color &c);
/**
/**
* @brief Convert a pointer to a value that can be sent between NodeParams
*/
static QVariant PtrToValue(void* ptr)
{
return reinterpret_cast<quintptr>(ptr);
}
static QVariant PtrToValue(void *ptr)
{
return reinterpret_cast<quintptr>(ptr);
}
/**
/**
* @brief Convert a NodeParam value to a pointer of any kind
*/
template<class T>
static T* ValueToPtr(const QVariant &ptr)
{
return reinterpret_cast<T*>(ptr.value<quintptr>());
}
template <class T> static T *ValueToPtr(const QVariant &ptr)
{
return reinterpret_cast<T *>(ptr.value<quintptr>());
}
};
namespace core {
namespace core
{
uint qHash(const core::rational& r, uint seed = 0);
uint qHash(const core::TimeRange& r, uint seed = 0);
uint qHash(const core::rational &r, uint seed = 0);
uint qHash(const core::TimeRange &r, uint seed = 0);
}
+2 -3
View File
@@ -21,10 +21,9 @@
#ifndef RANGE_H
#define RANGE_H
template <typename T>
bool InRange(T a, T b, T range)
template <typename T> bool InRange(T a, T b, T range)
{
return (a >= b - range && a <= b + range);
return (a >= b - range && a <= b + range);
}
#endif // RANGE_H
+54 -52
View File
@@ -24,69 +24,71 @@
#include <QMessageBox>
#include <QRegularExpression>
namespace olive {
double GetFloatRatioFromUser(QWidget* parent,
const QString& title,
bool* ok_in)
namespace olive
{
QString s;
forever {
bool ok;
double GetFloatRatioFromUser(QWidget *parent, const QString &title, bool *ok_in)
{
QString s;
s = QInputDialog::getText(parent,
title,
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
QLineEdit::Normal,
s,
&ok);
forever
{
bool ok;
if (!ok) {
// User cancelled dialog, do nothing
if (ok_in) {
*ok_in = false;
}
return qSNaN();
}
s = QInputDialog::getText(
parent, title,
QCoreApplication::translate(
"RatioDialog",
"Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
QLineEdit::Normal, s, &ok);
QStringList ratio_components = s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
if (!ok) {
// User cancelled dialog, do nothing
if (ok_in) {
*ok_in = false;
}
return qSNaN();
}
if (ratio_components.size() == 1) {
bool float_ok;
QStringList ratio_components =
s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
double flt = ratio_components.at(0).toDouble(&float_ok);
if (ratio_components.size() == 1) {
bool float_ok;
if (float_ok && flt > 0) {
if (ok_in) {
*ok_in = true;
}
return flt;
}
} else if (ratio_components.size() == 2) {
bool numer_ok, denom_ok;
double flt = ratio_components.at(0).toDouble(&float_ok);
double num = ratio_components.at(0).toDouble(&numer_ok);
double den = ratio_components.at(1).toDouble(&denom_ok);
if (float_ok && flt > 0) {
if (ok_in) {
*ok_in = true;
}
return flt;
}
} else if (ratio_components.size() == 2) {
bool numer_ok, denom_ok;
if (numer_ok
&& denom_ok
&& num > 0
&& den > 0) {
// Exit loop and set this ratio
if (ok_in) {
*ok_in = true;
}
return num / den;
}
}
double num = ratio_components.at(0).toDouble(&numer_ok);
double den = ratio_components.at(1).toDouble(&denom_ok);
QMessageBox::warning(parent,
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a "
"rational fraction with a ':' or a '/' separator.").arg(s),
QMessageBox::Ok);
}
if (numer_ok && denom_ok && num > 0 && den > 0) {
// Exit loop and set this ratio
if (ok_in) {
*ok_in = true;
}
return num / den;
}
}
QMessageBox::warning(
parent,
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
QCoreApplication::translate(
"RatioDialog",
"Failed to parse \"%1\" into an aspect ratio. Please format a "
"rational fraction with a ':' or a '/' separator.")
.arg(s),
QMessageBox::Ok);
}
}
}
+4 -4
View File
@@ -23,11 +23,11 @@
#include <QInputDialog>
namespace olive {
namespace olive
{
double GetFloatRatioFromUser(QWidget* parent,
const QString& title,
bool* ok_in);
double GetFloatRatioFromUser(QWidget *parent, const QString &title,
bool *ok_in);
}
+10 -13
View File
@@ -4,24 +4,21 @@
#include <QMap>
#include <QMutex>
template <typename K, typename V>
class ThreadSafeMap
{
template <typename K, typename V> class ThreadSafeMap {
public:
ThreadSafeMap() = default;
ThreadSafeMap() = default;
void insert(K key, V value)
{
mutex_.lock();
map_.insert(key, value);
mutex_.unlock();
}
void insert(K key, V value)
{
mutex_.lock();
map_.insert(key, value);
mutex_.unlock();
}
private:
QMutex mutex_;
QMap<K, V> map_;
QMutex mutex_;
QMap<K, V> map_;
};
#endif // THREADSAFEMAP_H
+5 -3
View File
@@ -6,10 +6,12 @@
#include "common/define.h"
namespace olive {
namespace olive
{
inline QString ToHex(quint64 t) {
return QStringLiteral("%1").arg(t, 0, 16);
inline QString ToHex(quint64 t)
{
return QStringLiteral("%1").arg(t, 0, 16);
}
}
+2 -3
View File
@@ -21,10 +21,9 @@
#ifndef UTIL_H
#define UTIL_H
template <typename T>
inline T mid(T a, T b)
template <typename T> inline T mid(T a, T b)
{
return (a + b) * 0.5;
return (a + b) * 0.5;
}
#endif // UTIL_H
+13 -12
View File
@@ -23,23 +23,24 @@
#include "node/block/block.h"
#include "node/factory.h"
namespace olive {
namespace olive
{
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
{
QXmlStreamReader::TokenType token;
QXmlStreamReader::TokenType token;
while ((token = reader->readNext()) != QXmlStreamReader::Invalid
&& token != QXmlStreamReader::EndDocument
&& (!cancel_atom || !cancel_atom->IsCancelled())) {
if (reader->isEndElement()) {
return false;
} else if (reader->isStartElement()) {
return true;
}
}
while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
token != QXmlStreamReader::EndDocument &&
(!cancel_atom || !cancel_atom->IsCancelled())) {
if (reader->isEndElement()) {
return false;
} else if (reader->isStartElement()) {
return true;
}
}
return false;
return false;
}
}
+5 -3
View File
@@ -27,7 +27,8 @@
#include "render/cancelatom.h"
#include "undo/undocommand.h"
namespace olive {
namespace olive
{
class Block;
class Node;
@@ -35,7 +36,7 @@ class NodeInput;
class NodeGroup;
#define XMLAttributeLoop(reader, item) \
foreach (const QXmlStreamAttribute& item, reader->attributes())
foreach (const QXmlStreamAttribute &item, reader->attributes())
/**
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
@@ -46,7 +47,8 @@ class NodeGroup;
*
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
*/
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr);
bool XMLReadNextStartElement(QXmlStreamReader *reader,
CancelAtom *cancel_atom = nullptr);
}
+297 -189
View File
@@ -37,278 +37,386 @@
#include "ui/style/style.h"
#include "window/mainwindow/mainwindow.h"
namespace olive {
namespace olive
{
Config Config::current_config_;
Config::Config()
{
SetDefaults();
SetDefaults();
}
void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data)
void Config::SetEntryInternal(const QString &key, NodeValue::Type type,
const QVariant &data)
{
config_map_[key] = {type, data};
config_map_[key] = { type, data };
}
QString Config::GetConfigFilePath()
{
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("config.xml"));
return QDir(FileFunctions::GetConfigurationLocation())
.filePath(QStringLiteral("config.xml"));
}
Config &Config::Current()
{
return current_config_;
return current_config_;
}
void Config::SetDefaults()
{
config_map_.clear();
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle);
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame);
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2)));
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1);
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt, 20);
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000);
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage);
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk);
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
config_map_.clear();
SetEntryInternal(QStringLiteral("Style"), NodeValue::kText,
StyleManager::kDefaultStyle);
SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt,
Timecode::kTimecodeDropFrame);
SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational,
QVariant::fromValue(rational(2)));
SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt,
1);
SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt,
20);
SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt,
10000);
SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("EditToolSelectsLinks"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt,
AutoScroll::kPage);
SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"),
NodeValue::kInt, ImportTool::kDWSAsk);
SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"),
NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean,
false);
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailInOut);
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled);
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt,
Timeline::kThumbnailInOut);
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt,
Timeline::kWaveformsEnabled);
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(
QStringLiteral("DefaultVideoTransition"), NodeValue::kText,
QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(
QStringLiteral("DefaultAudioTransition"), NodeValue::kText,
QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultTransitionLength"),
NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt, 48);
SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt, QFont::Bold);
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt,
48);
SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText,
QString());
SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt,
QFont::Bold);
SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean,
true);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt, ColorCoding::kRed);
SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt, ColorCoding::kMaroon);
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt, ColorCoding::kOrange);
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt, ColorCoding::kBrown);
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt, ColorCoding::kYellow);
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt, ColorCoding::kOlive);
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt, ColorCoding::kLime);
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt, ColorCoding::kGreen);
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt, ColorCoding::kCyan);
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt, ColorCoding::kTeal);
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt, ColorCoding::kBlue);
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt, ColorCoding::kNavy);
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt,
ColorCoding::kRed);
SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt,
ColorCoding::kMaroon);
SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt,
ColorCoding::kOrange);
SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt,
ColorCoding::kBrown);
SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt,
ColorCoding::kYellow);
SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt,
ColorCoding::kOlive);
SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt,
ColorCoding::kLime);
SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt,
ColorCoding::kGreen);
SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt,
ColorCoding::kCyan);
SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt,
ColorCoding::kTeal);
SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt,
ColorCoding::kBlue);
SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt,
ColorCoding::kNavy);
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText,
QString());
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt,
48000);
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"),
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(
QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText,
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV);
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM);
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320);
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt,
ExportFormat::kFormatWAV);
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt,
ExportCodec::kCodecPCM);
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"),
NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"),
NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(
QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText,
QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()));
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt,
320);
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0)));
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60)));
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational,
QVariant::fromValue(rational(0)));
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational,
QVariant::fromValue(rational(60)));
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920);
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080);
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeValue::kRational, QVariant::fromValue(rational(1001, 30000)));
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeValue::kInt, VideoParams::kInterlaceNone);
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt, QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt,
1920);
SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt,
1080);
SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"),
NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"),
NodeValue::kRational,
QVariant::fromValue(rational(1001, 30000)));
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"),
NodeValue::kInt, VideoParams::kInterlaceNone);
SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"),
NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"),
NodeValue::kInt, 48000);
SetEntryInternal(
QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt,
QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
// Online/offline settings
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, PixelFormat::F32);
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, PixelFormat::F16);
// Online/offline settings
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt,
PixelFormat::F32);
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt,
PixelFormat::F16);
SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt, ColorCoding::kLime);
SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt,
ColorCoding::kLime);
}
void Config::Load()
{
QFile config_file(GetConfigFilePath());
QFile config_file(GetConfigFilePath());
if (!config_file.exists()) {
return;
}
if (!config_file.exists()) {
return;
}
if (!config_file.open(QFile::ReadOnly)) {
qWarning() << "Failed to load application settings. This session will use defaults.";
return;
}
if (!config_file.open(QFile::ReadOnly)) {
qWarning()
<< "Failed to load application settings. This session will use defaults.";
return;
}
// Reset to defaults
current_config_.SetDefaults();
// Reset to defaults
current_config_.SetDefaults();
QXmlStreamReader reader(&config_file);
QXmlStreamReader reader(&config_file);
QString config_version;
QString config_version;
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("Configuration")) {
while (XMLReadNextStartElement(&reader)) {
QString key = reader.name().toString();
QString value = reader.readElementText();
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("Configuration")) {
while (XMLReadNextStartElement(&reader)) {
QString key = reader.name().toString();
QString value = reader.readElementText();
if (key == QStringLiteral("Version")) {
config_version = value;
if (key == QStringLiteral("Version")) {
config_version = value;
if (!value.contains(".")) {
qDebug() << "CONFIG: This is a 0.1.x config file, upconvert";
}
} else if (key == QStringLiteral("DefaultSequenceFrameRate") && !config_version.contains('.')) {
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
// supported rational
qDebug() << " CONFIG: Finding closest match to" << value;
if (!value.contains(".")) {
qDebug()
<< "CONFIG: This is a 0.1.x config file, upconvert";
}
} else if (key == QStringLiteral("DefaultSequenceFrameRate") &&
!config_version.contains('.')) {
// 0.1.x stored this value as a float while we now use rationals, we'll use a heuristic to find the closest
// supported rational
qDebug() << " CONFIG: Finding closest match to" << value;
double config_fr = value.toDouble();
double config_fr = value.toDouble();
const QVector<rational>& supported_frame_rates = VideoParams::kSupportedFrameRates;
const QVector<rational> &supported_frame_rates =
VideoParams::kSupportedFrameRates;
rational match = supported_frame_rates.first();
double match_diff = qAbs(match.toDouble() - config_fr);
rational match = supported_frame_rates.first();
double match_diff = qAbs(match.toDouble() - config_fr);
for (int i=1;i<supported_frame_rates.size();i++) {
double diff = qAbs(supported_frame_rates.at(i).toDouble() - config_fr);
for (int i = 1; i < supported_frame_rates.size(); i++) {
double diff = qAbs(
supported_frame_rates.at(i).toDouble() - config_fr);
if (diff < match_diff) {
match = supported_frame_rates.at(i);
match_diff = diff;
}
}
if (diff < match_diff) {
match = supported_frame_rates.at(i);
match_diff = diff;
}
}
qDebug() << " CONFIG: Closest match was" << match.toDouble();
qDebug()
<< " CONFIG: Closest match was" << match.toDouble();
current_config_[key] = QVariant::fromValue(match.flipped());
} else {
current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false);
}
}
current_config_[key] = QVariant::fromValue(match.flipped());
} else {
current_config_[key] = NodeValue::StringToValue(
current_config_.GetConfigEntryType(key), value, false);
}
}
//reader.skipCurrentElement();
} else {
reader.skipCurrentElement();
}
}
//reader.skipCurrentElement();
} else {
reader.skipCurrentElement();
}
}
if (reader.hasError()) {
QMessageBox::critical(Core::instance()->main_window(),
QCoreApplication::translate("Config", "Error loading settings"),
QCoreApplication::translate("Config", "Failed to load application settings. This session will "
"use defaults.\n\n%1").arg(reader.errorString()),
QMessageBox::Ok);
current_config_.SetDefaults();
}
if (reader.hasError()) {
QMessageBox::critical(
Core::instance()->main_window(),
QCoreApplication::translate("Config", "Error loading settings"),
QCoreApplication::translate(
"Config",
"Failed to load application settings. This session will "
"use defaults.\n\n%1")
.arg(reader.errorString()),
QMessageBox::Ok);
current_config_.SetDefaults();
}
config_file.close();
config_file.close();
}
void Config::Save()
{
QString real_filename = GetConfigFilePath();
QString temp_filename = FileFunctions::GetSafeTemporaryFilename(real_filename);
QString real_filename = GetConfigFilePath();
QString temp_filename =
FileFunctions::GetSafeTemporaryFilename(real_filename);
QFile config_file(temp_filename);
QFile config_file(temp_filename);
if (!config_file.open(QFile::WriteOnly)) {
QMessageBox::critical(Core::instance()->main_window(),
QCoreApplication::translate("Config", "Error saving settings"),
QCoreApplication::translate("Config", "Failed to save application settings. The application "
"may lack write permissions for this location."),
QMessageBox::Ok);
return;
}
if (!config_file.open(QFile::WriteOnly)) {
QMessageBox::critical(
Core::instance()->main_window(),
QCoreApplication::translate("Config", "Error saving settings"),
QCoreApplication::translate(
"Config",
"Failed to save application settings. The application "
"may lack write permissions for this location."),
QMessageBox::Ok);
return;
}
QXmlStreamWriter writer(&config_file);
writer.setAutoFormatting(true);
QXmlStreamWriter writer(&config_file);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartDocument();
writer.writeStartElement("Configuration");
writer.writeStartElement("Configuration");
// Anything after the hyphen is considered "unimportant" information
writer.writeTextElement("Version", QCoreApplication::applicationVersion().split('-').first());
// Anything after the hyphen is considered "unimportant" information
writer.writeTextElement(
"Version", QCoreApplication::applicationVersion().split('-').first());
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
while (iterator.hasNext()) {
iterator.next();
QMapIterator<QString, ConfigEntry> iterator(current_config_.config_map_);
while (iterator.hasNext()) {
iterator.next();
QString value = NodeValue::ValueToString(iterator.value().type, iterator.value().data, false);
QString value = NodeValue::ValueToString(iterator.value().type,
iterator.value().data, false);
if (iterator.value().type == NodeValue::kNone) {
qWarning() << "Config key" << iterator.key() << "had null type and was discarded";
} else {
writer.writeTextElement(iterator.key(), value);
}
}
if (iterator.value().type == NodeValue::kNone) {
qWarning() << "Config key" << iterator.key()
<< "had null type and was discarded";
} else {
writer.writeTextElement(iterator.key(), value);
}
}
writer.writeEndElement(); // Configuration
writer.writeEndElement(); // Configuration
writer.writeEndDocument();
writer.writeEndDocument();
config_file.close();
config_file.close();
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename, real_filename)) {
qWarning() << QStringLiteral("Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
.arg(real_filename, temp_filename);
}
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename,
real_filename)) {
qWarning()
<< QStringLiteral(
"Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
.arg(real_filename, temp_filename);
}
}
QVariant Config::operator[](const QString &key) const
{
return config_map_[key].data;
return config_map_[key].data;
}
QVariant &Config::operator[](const QString &key)
{
return config_map_[key].data;
return config_map_[key].data;
}
NodeValue::Type Config::GetConfigEntryType(const QString &key) const
{
return config_map_[key].type;
return config_map_[key].type;
}
}
+19 -17
View File
@@ -27,42 +27,44 @@
#include "node/value.h"
namespace olive {
namespace olive
{
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
#define OLIVE_CONFIG_STR(x) Config::Current()[x]
class Config {
public:
static Config& Current();
static Config &Current();
void SetDefaults();
void SetDefaults();
static void Load();
static void Load();
static void Save();
static void Save();
QVariant operator[](const QString&) const;
QVariant operator[](const QString &) const;
QVariant& operator[](const QString&);
QVariant &operator[](const QString &);
NodeValue::Type GetConfigEntryType(const QString& key) const;
NodeValue::Type GetConfigEntryType(const QString &key) const;
private:
Config();
Config();
struct ConfigEntry {
NodeValue::Type type;
QVariant data;
};
struct ConfigEntry {
NodeValue::Type type;
QVariant data;
};
void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data);
void SetEntryInternal(const QString &key, NodeValue::Type type,
const QVariant &data);
QMap<QString, ConfigEntry> config_map_;
QMap<QString, ConfigEntry> config_map_;
static Config current_config_;
static Config current_config_;
static QString GetConfigFilePath();
static QString GetConfigFilePath();
};
}
+1042 -956
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 "widget/projectexplorer/projectviewmodel.h"
namespace olive {
namespace olive
{
class MainWindow;
@@ -48,132 +49,125 @@ class MainWindow;
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
* opening the import dialog, etc.)
*/
class Core : public QObject
{
Q_OBJECT
class Core : public QObject {
Q_OBJECT
public:
class CoreParams
{
public:
CoreParams();
class CoreParams {
public:
CoreParams();
enum RunMode {
kRunNormal,
kHeadlessExport,
kHeadlessPreCache
};
enum RunMode { kRunNormal, kHeadlessExport, kHeadlessPreCache };
bool fullscreen() const
{
return run_fullscreen_;
}
bool fullscreen() const
{
return run_fullscreen_;
}
void set_fullscreen(bool e)
{
run_fullscreen_ = e;
}
void set_fullscreen(bool e)
{
run_fullscreen_ = e;
}
RunMode run_mode() const
{
return mode_;
}
RunMode run_mode() const
{
return mode_;
}
void set_run_mode(RunMode m)
{
mode_ = m;
}
void set_run_mode(RunMode m)
{
mode_ = m;
}
const QString startup_project() const
{
return startup_project_;
}
const QString startup_project() const
{
return startup_project_;
}
void set_startup_project(const QString& p)
{
startup_project_ = p;
}
void set_startup_project(const QString &p)
{
startup_project_ = p;
}
const QString& startup_language() const
{
return startup_language_;
}
const QString &startup_language() const
{
return startup_language_;
}
void set_startup_language(const QString& s)
{
startup_language_ = s;
}
void set_startup_language(const QString &s)
{
startup_language_ = s;
}
bool crash_on_startup() const
{
return crash_;
}
bool crash_on_startup() const
{
return crash_;
}
void set_crash_on_startup(bool e)
{
crash_ = true;
}
void set_crash_on_startup(bool e)
{
crash_ = true;
}
private:
RunMode mode_;
private:
RunMode mode_;
QString startup_project_;
QString startup_project_;
QString startup_language_;
QString startup_language_;
bool run_fullscreen_;
bool run_fullscreen_;
bool crash_;
bool crash_;
};
};
/**
/**
* @brief Core Constructor
*
* Currently empty
*/
Core(const CoreParams& params);
Core(const CoreParams &params);
/**
/**
* @brief Core object accessible from anywhere in the code
*
* Use this to access Core functions.
*/
static Core* instance();
static Core *instance();
const CoreParams& core_params() const
{
return core_params_;
}
const CoreParams &core_params() const
{
return core_params_;
}
/**
/**
* @brief Start Olive Core
*
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
*/
void Start();
void Start();
/**
/**
* @brief Stop Olive Core
*
* Ends all threads and frees all memory ready for the application to exit.
*/
void Stop();
void Stop();
/**
/**
* @brief Retrieve main window instance
*
* @return
*
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
*/
MainWindow* main_window();
MainWindow *main_window();
/**
/**
* @brief Retrieve UndoStack object
*/
UndoStack* undo_stack();
UndoStack *undo_stack();
/**
/**
* @brief Import a list of files
*
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
@@ -181,34 +175,34 @@ public:
*
* @param urls
*/
void ImportFiles(const QStringList& urls, Folder *parent);
void ImportFiles(const QStringList &urls, Folder *parent);
/**
/**
* @brief Get the currently active tool
*/
const Tool::Item& tool() const;
const Tool::Item &tool() const;
/**
/**
* @brief Get the currently selected object that the add tool should make (if the add tool is active)
*/
const Tool::AddableObject& GetSelectedAddableObject() const;
const Tool::AddableObject &GetSelectedAddableObject() const;
/**
/**
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
*/
const QString& GetSelectedTransition() const;
const QString &GetSelectedTransition() const;
/**
/**
* @brief Get current snapping value
*/
const bool& snapping() const;
const bool &snapping() const;
/**
/**
* @brief Returns a list of the most recently opened/saved projects
*/
const QStringList& GetRecentProjects() const;
const QStringList &GetRecentProjects() const;
/**
/**
* @brief Get the currently active project
*
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
@@ -218,402 +212,409 @@ public:
*
* The active Project file, or nullptr if the heuristic couldn't find one.
*/
Project* GetActiveProject() const;
Folder* GetSelectedFolderInActiveProject() const;
Project *GetActiveProject() const;
Folder *GetSelectedFolderInActiveProject() const;
/**
/**
* @brief Gets current timecode display mode
*/
Timecode::Display GetTimecodeDisplay() const;
Timecode::Display GetTimecodeDisplay() const;
/**
/**
* @brief Sets current timecode display mode
*/
void SetTimecodeDisplay(Timecode::Display d);
void SetTimecodeDisplay(Timecode::Display d);
/**
/**
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
*/
void SetAutorecoveryInterval(int minutes);
void SetAutorecoveryInterval(int minutes);
static void CopyStringToClipboard(const QString& s);
static void CopyStringToClipboard(const QString &s);
static QString PasteStringFromClipboard();
static QString PasteStringFromClipboard();
/**
/**
* @brief Recursively count files in a file/directory list
*/
static int CountFilesInFileList(const QFileInfoList &filenames);
static int CountFilesInFileList(const QFileInfoList &filenames);
/**
/**
* @brief Show a dialog to the user to rename a set of nodes
*/
bool LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent = nullptr);
bool LabelNodes(const QVector<Node *> &nodes,
MultiUndoCommand *parent = nullptr);
/**
/**
* @brief Create a new sequence named appropriately for the active project
*/
static Sequence* CreateNewSequenceForProject(const QString &format, Project *project);
static Sequence* CreateNewSequenceForProject(Project *project)
{
return CreateNewSequenceForProject(tr("Sequence %1"), project);
}
static Sequence *CreateNewSequenceForProject(const QString &format,
Project *project);
static Sequence *CreateNewSequenceForProject(Project *project)
{
return CreateNewSequenceForProject(tr("Sequence %1"), project);
}
/**
/**
* @brief Opens a project from the recently opened list
*/
void OpenProjectFromRecentList(int index);
void OpenProjectFromRecentList(int index);
/**
/**
* @brief Closes a project
*/
bool CloseProject(bool auto_open_new, bool ignore_modified = false);
bool CloseProject(bool auto_open_new, bool ignore_modified = false);
/**
/**
* @brief Runs a modal cache task on the currently active sequence
*/
void CacheActiveSequence(bool in_out_only);
void CacheActiveSequence(bool in_out_only);
/**
/**
* @brief Check each footage object for whether it still exists or has changed
*/
bool ValidateFootageInLoadedProject(Project* project, const QString &project_saved_url);
bool ValidateFootageInLoadedProject(Project *project,
const QString &project_saved_url);
/**
/**
* @brief Changes the current language
*/
bool SetLanguage(const QString& locale);
bool SetLanguage(const QString &locale);
/**
/**
* @brief Show message in main window's status bar
*
* Shorthand for Core::instance()->main_window()->statusBar()->showMessage();
*/
void ShowStatusBarMessage(const QString& s, int timeout = 0);
void ShowStatusBarMessage(const QString &s, int timeout = 0);
void ClearStatusBarMessage();
void ClearStatusBarMessage();
void OpenRecoveryProject(const QString& filename);
void OpenRecoveryProject(const QString &filename);
void OpenNodeInViewer(ViewerOutput* viewer);
void OpenNodeInViewer(ViewerOutput *viewer);
void OpenExportDialogForViewer(ViewerOutput *viewer, bool start_still_image);
void OpenExportDialogForViewer(ViewerOutput *viewer,
bool start_still_image);
bool IsMagicEnabled() const { return magic_; }
bool IsMagicEnabled() const
{
return magic_;
}
public slots:
/**
/**
* @brief Starts an open file dialog to load a project from file
*/
void OpenProject();
void OpenProject();
/**
/**
* @brief Saves the current project
*/
bool SaveProject();
bool SaveProject();
/**
/**
* @brief Performs a "save as" on the current project
*/
bool SaveProjectAs();
bool SaveProjectAs();
void RevertProject();
void RevertProject();
/**
/**
* @brief Set the current application-wide tool
*
* @param tool
*/
void SetTool(const Tool::Item& tool);
void SetTool(const Tool::Item &tool);
/**
/**
* @brief Set the current snapping setting
*/
void SetSnapping(const bool& b);
void SetSnapping(const bool &b);
/**
/**
* @brief Show an About dialog
*/
void DialogAboutShow();
void DialogAboutShow();
/**
/**
* @brief Open the import footage dialog and import the files selected (runs ImportFiles())
*/
void DialogImportShow();
void DialogImportShow();
/**
/**
* @brief Show Preferences dialog
*/
void DialogPreferencesShow();
void DialogPreferencesShow();
/**
/**
* @brief Show Project Properties dialog
*/
void DialogProjectPropertiesShow();
void DialogProjectPropertiesShow();
/**
/**
* @brief Show Export dialog
*/
void DialogExportShow();
void DialogExportShow();
/**
/**
* @brief Show OTIO import dialog
*/
#ifdef USE_OTIO
bool DialogImportOTIOShow(const QList<Sequence*>& sequences);
bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
#endif
/**
/**
* @brief Create a new folder in the currently active project
*/
void CreateNewFolder();
void CreateNewFolder();
/**
/**
* @brief Create a new sequence in the currently active project
*/
void CreateNewSequence();
void CreateNewSequence();
/**
/**
* @brief Set the currently selected object that the add tool should make
*/
void SetSelectedAddableObject(const Tool::AddableObject& obj);
void SetSelectedAddableObject(const Tool::AddableObject &obj);
/**
/**
* @brief Set the currently selected object that the add tool should make
*/
void SetSelectedTransitionObject(const QString& obj);
void SetSelectedTransitionObject(const QString &obj);
/**
/**
* @brief Clears the list of recently opened/saved projects
*/
void ClearOpenRecentList();
void ClearOpenRecentList();
/**
/**
* @brief Creates a new empty project and opens it
*/
void CreateNewProject();
void CreateNewProject();
void CheckForAutoRecoveries();
void CheckForAutoRecoveries();
void BrowseAutoRecoveries();
void BrowseAutoRecoveries();
void RequestPixelSamplingInViewers(bool e);
void RequestPixelSamplingInViewers(bool e);
void WarnCacheFull();
void WarnCacheFull();
void SetMagic(bool e)
{
magic_ = e;
}
void SetMagic(bool e)
{
magic_ = e;
}
signals:
/**
/**
* @brief Signal emitted when the tool is changed from somewhere
*/
void ToolChanged(const Tool::Item& tool);
void ToolChanged(const Tool::Item &tool);
/**
/**
* @brief Signal emitted when addable object changes through SetSelectedAddableObject
*/
void AddableObjectChanged(Tool::AddableObject o);
void AddableObjectChanged(Tool::AddableObject o);
/**
/**
* @brief Signal emitted when the snapping setting is changed
*/
void SnappingChanged(const bool& b);
void SnappingChanged(const bool &b);
/**
/**
* @brief Signal emitted when the default timecode display mode changed
*/
void TimecodeDisplayChanged(Timecode::Display d);
void TimecodeDisplayChanged(Timecode::Display d);
/**
/**
* @brief Signal emitted when a change is made to the open recent list
*/
void OpenRecentListChanged();
void OpenRecentListChanged();
/**
/**
* @brief Enable mouse color sampling functionality on all viewers
*
* This can be slow, so we only turn it on when we need it.
*/
void ColorPickerEnabled(bool e);
void ColorPickerEnabled(bool e);
/**
/**
* @brief A viewer with color picked enabled has emitted a color
*/
void ColorPickerColorEmitted(const Color &reference, const Color &display);
void ColorPickerColorEmitted(const Color &reference, const Color &display);
private:
/**
/**
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
*/
static QString GetProjectFilter(bool include_any_filter);
static QString GetProjectFilter(bool include_any_filter);
/**
/**
* @brief Returns the filename where the recently opened/saved projects should be stored
*/
static QString GetRecentProjectsFilePath();
static QString GetRecentProjectsFilePath();
/**
/**
* @brief Called only on startup to set the locale
*/
void SetStartupLocale();
void SetStartupLocale();
/**
/**
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
*/
void PushRecentlyOpenedProject(const QString &s);
void PushRecentlyOpenedProject(const QString &s);
/**
/**
* @brief Declare custom types/classes for Qt's signal/slot system
*
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
* them all in a function here.
*/
void DeclareTypesForQt();
void DeclareTypesForQt();
/**
/**
* @brief Start GUI portion of Olive
*
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
* create an application instance that is completely valid minus the UI (e.g. for CLI modes).
*/
void StartGUI(bool full_screen);
void StartGUI(bool full_screen);
/**
/**
* @brief Internal function for saving a project to a file
*/
void SaveProjectInternal(const QString &override_filename = QString());
void SaveProjectInternal(const QString &override_filename = QString());
/**
/**
* @brief Retrieves the currently most active sequence for exporting
*/
ViewerOutput *GetSequenceToExport();
ViewerOutput *GetSequenceToExport();
static QString GetAutoRecoveryIndexFilename();
static QString GetAutoRecoveryIndexFilename();
void SaveUnrecoveredList();
void SaveUnrecoveredList();
bool RevertProjectInternal(bool by_opening_existing);
bool RevertProjectInternal(bool by_opening_existing);
void SaveRecentProjectsList();
void SaveRecentProjectsList();
/**
/**
* @brief Adds a project to the "open projects" list
*/
void AddOpenProject(olive::Project* p, bool add_to_recents = false);
void AddOpenProject(olive::Project *p, bool add_to_recents = false);
bool AddOpenProjectFromTask(Task* task, bool add_to_recents);
bool AddOpenProjectFromTask(Task *task, bool add_to_recents);
void SetActiveProject(Project *p);
void SetActiveProject(Project *p);
/**
/**
* @brief Internal main window object
*/
MainWindow* main_window_;
MainWindow *main_window_;
/**
/**
* @brief List of currently open projects
*/
Project *open_project_;
Project *open_project_;
/**
/**
* @brief Currently active tool
*/
Tool::Item tool_;
Tool::Item tool_;
/**
/**
* @brief Currently active addable object
*/
Tool::AddableObject addable_object_;
Tool::AddableObject addable_object_;
/**
/**
* @brief Currently selected transition
*/
QString selected_transition_;
QString selected_transition_;
/**
/**
* @brief Current snapping setting
*/
bool snapping_;
bool snapping_;
/**
/**
* @brief Internal timer for saving autorecovery files
*/
QTimer autorecovery_timer_;
QTimer autorecovery_timer_;
/**
/**
* @brief Application-wide undo stack instance
*/
UndoStack undo_stack_;
UndoStack undo_stack_;
/**
/**
* @brief List of most recently opened/saved projects
*/
QStringList recent_projects_;
QStringList recent_projects_;
/**
/**
* @brief Parameters set up in main() determining how the program should run
*/
CoreParams core_params_;
CoreParams core_params_;
/**
/**
* @brief Static singleton core instance
*/
static Core* instance_;
static Core *instance_;
/**
/**
* @brief Internal translator
*/
QTranslator* translator_;
QTranslator *translator_;
/**
/**
* @brief List of projects that are unsaved but have autorecovery projects
*/
QVector<QUuid> autorecovered_projects_;
QVector<QUuid> autorecovered_projects_;
/**
/**
* @brief Do something debug related
*/
bool magic_;
bool magic_;
/**
/**
* @brief How many widgets currently need pixel sampling access
*/
int pixel_sampling_users_;
int pixel_sampling_users_;
bool shown_cache_full_warning_;
bool shown_cache_full_warning_;
private slots:
void SaveAutorecovery();
void SaveAutorecovery();
void ProjectSaveSucceeded(Task *task);
void ProjectSaveSucceeded(Task *task);
bool AddOpenProjectFromTaskAndAddToRecents(Task* task)
{
return AddOpenProjectFromTask(task, true);
}
bool AddOpenProjectFromTaskAndAddToRecents(Task *task)
{
return AddOpenProjectFromTask(task, true);
}
void ImportTaskComplete(Task *task);
void ImportTaskComplete(Task *task);
bool ConfirmImageSequence(const QString &filename);
bool ConfirmImageSequence(const QString &filename);
void ProjectWasModified(bool e);
void ProjectWasModified(bool e);
bool StartHeadlessExport();
bool StartHeadlessExport();
void OpenStartupProject();
void OpenStartupProject();
void AddRecoveryProjectFromTask(Task* task);
void AddRecoveryProjectFromTask(Task *task);
/**
/**
* @brief Internal project open
*/
void OpenProjectInternal(const QString& filename, bool recovery_project = false);
void ImportSingleFile(const QString &f);
void OpenProjectInternal(const QString &filename,
bool recovery_project = false);
void ImportSingleFile(const QString &f);
};
}
+259 -224
View File
@@ -40,335 +40,370 @@
#include "common/filefunctions.h"
#include "version.h"
namespace olive {
CrashHandlerDialog::CrashHandlerDialog(const QString& report_path)
namespace olive
{
setWindowTitle(tr("Olive"));
setWindowFlags(Qt::WindowStaysOnTopHint);
report_filename_ = report_path;
waiting_for_upload_ = false;
CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
{
setWindowTitle(tr("Olive"));
setWindowFlags(Qt::WindowStaysOnTopHint);
QVBoxLayout* layout = new QVBoxLayout(this);
report_filename_ = report_path;
waiting_for_upload_ = false;
layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by "
"sending an error report.")));
QVBoxLayout *layout = new QVBoxLayout(this);
QSplitter* splitter = new QSplitter(Qt::Vertical);
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
layout->addWidget(new QLabel(
tr("We're sorry, Olive has crashed. Please help us fix it by "
"sending an error report.")));
summary_edit_ = new QTextEdit();
summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as "
"possible. If you can, provide steps to reproduce this crash."));
QSplitter *splitter = new QSplitter(Qt::Vertical);
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
splitter->addWidget(summary_edit_);
summary_edit_ = new QTextEdit();
summary_edit_->setPlaceholderText(
tr("Describe what you were doing in as much detail as "
"possible. If you can, provide steps to reproduce this crash."));
QWidget* crash_widget = new QWidget();
QVBoxLayout* crash_widget_layout = new QVBoxLayout(crash_widget);
crash_widget_layout->setMargin(0);
splitter->addWidget(summary_edit_);
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
QWidget *crash_widget = new QWidget();
QVBoxLayout *crash_widget_layout = new QVBoxLayout(crash_widget);
crash_widget_layout->setMargin(0);
crash_report_ = new QTextEdit();
crash_report_->setReadOnly(true);
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
crash_widget_layout->addWidget(crash_report_);
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
splitter->addWidget(crash_widget);
crash_report_ = new QTextEdit();
crash_report_->setReadOnly(true);
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
crash_widget_layout->addWidget(crash_report_);
QHBoxLayout* btn_layout = new QHBoxLayout();
btn_layout->setMargin(0);
btn_layout->addStretch();
splitter->addWidget(crash_widget);
send_report_btn_ = new QPushButton(tr("Send Error Report"));
connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport);
btn_layout->addWidget(send_report_btn_);
QHBoxLayout *btn_layout = new QHBoxLayout();
btn_layout->setMargin(0);
btn_layout->addStretch();
dont_send_btn_ = new QPushButton(tr("Don't Send"));
connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject);
btn_layout->addWidget(dont_send_btn_);
send_report_btn_ = new QPushButton(tr("Send Error Report"));
connect(send_report_btn_, &QPushButton::clicked, this,
&CrashHandlerDialog::SendErrorReport);
btn_layout->addWidget(send_report_btn_);
layout->addLayout(btn_layout);
dont_send_btn_ = new QPushButton(tr("Don't Send"));
connect(dont_send_btn_, &QPushButton::clicked, this,
&CrashHandlerDialog::reject);
btn_layout->addWidget(dont_send_btn_);
crash_report_->setEnabled(false);
send_report_btn_->setEnabled(false);
layout->addLayout(btn_layout);
crash_report_->setText(tr("Waiting for crash report to be generated..."));
crash_report_->setEnabled(false);
send_report_btn_->setEnabled(false);
GenerateReport();
crash_report_->setText(tr("Waiting for crash report to be generated..."));
GenerateReport();
}
void CrashHandlerDialog::SetGUIObjectsEnabled(bool e)
{
summary_edit_->setEnabled(e);
crash_report_->setEnabled(e);
send_report_btn_->setEnabled(e);
dont_send_btn_->setEnabled(e);
summary_edit_->setEnabled(e);
crash_report_->setEnabled(e);
send_report_btn_->setEnabled(e);
dont_send_btn_->setEnabled(e);
}
QString CrashHandlerDialog::GetSymbolPath()
{
QDir app_path(qApp->applicationDirPath());
QString symbols_path;
QDir app_path(qApp->applicationDirPath());
QString symbols_path;
#if BUILDFLAG(IS_WIN)
symbols_path = app_path.filePath(QStringLiteral("symbols"));
symbols_path = app_path.filePath(QStringLiteral("symbols"));
#elif BUILDFLAG(IS_LINUX)
app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
app_path.cdUp();
symbols_path =
app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
#elif BUILDFLAG(IS_APPLE)
app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
#endif
return symbols_path;
return symbols_path;
}
void CrashHandlerDialog::GenerateReport()
{
QProcess* p = new QProcess();
QProcess *p = new QProcess();
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &CrashHandlerDialog::ReadProcessFinished);
connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData);
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &CrashHandlerDialog::ReadProcessFinished);
connect(p, &QProcess::readyReadStandardOutput, this,
&CrashHandlerDialog::ReadProcessHasData);
QString stackwalk_filename = FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("minidump_stackwalk"));
QString stackwalk_filename =
FileFunctions::GetFormattedExecutableForPlatform(
QStringLiteral("minidump_stackwalk"));
QString stackwalk_bin = QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
p->start(stackwalk_bin, {report_filename_, GetSymbolPath()});
crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
QString stackwalk_bin =
QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
crash_report_->setText(
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
}
void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
{
waiting_for_upload_ = false;
waiting_for_upload_ = false;
if (reply->error() == QNetworkReply::NoError) {
// Close dialog
QDialog::accept();
} else {
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Upload Failed"));
b.setText(tr("Failed to send error report (%1). Please try again later.").arg(QString::number(reply->error())));
b.addButton(QMessageBox::Ok);
b.exec();
if (reply->error() == QNetworkReply::NoError) {
// Close dialog
QDialog::accept();
} else {
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Upload Failed"));
b.setText(
tr("Failed to send error report (%1). Please try again later.")
.arg(QString::number(reply->error())));
b.addButton(QMessageBox::Ok);
b.exec();
SetGUIObjectsEnabled(true);
}
SetGUIObjectsEnabled(true);
}
}
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &se)
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply,
const QList<QSslError> &se)
{
QStringList errors;
for (const QSslError &err : se) {
errors.append(err.errorString());
}
QStringList errors;
for (const QSslError &err : se) {
errors.append(err.errorString());
}
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("SSL Error"));
b.setText(tr("Encountered the following SSL errors:\n\n%1").arg(errors.join('\n')));
b.addButton(QMessageBox::Ok);
b.exec();
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("SSL Error"));
b.setText(tr("Encountered the following SSL errors:\n\n%1")
.arg(errors.join('\n')));
b.addButton(QMessageBox::Ok);
b.exec();
}
void CrashHandlerDialog::AttemptToFindReport()
{
// If we found it, use it, otherwise wait a second and try again
if (report_filename_.isEmpty()) {
// Couldn't find report, try again in one second
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
} else {
GenerateReport();
}
// If we found it, use it, otherwise wait a second and try again
if (report_filename_.isEmpty()) {
// Couldn't find report, try again in one second
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
} else {
GenerateReport();
}
}
void CrashHandlerDialog::ReadProcessHasData()
{
report_data_.append(static_cast<QProcess*>(sender())->readAllStandardOutput());
report_data_.append(
static_cast<QProcess *>(sender())->readAllStandardOutput());
}
void CrashHandlerDialog::ReadProcessFinished()
{
SetGUIObjectsEnabled(true);
crash_report_->setText(report_data_);
delete sender();
SetGUIObjectsEnabled(true);
crash_report_->setText(report_data_);
delete sender();
}
void CrashHandlerDialog::SendErrorReport()
{
if (summary_edit_->document()->isEmpty()) {
QMessageBox b(this);
b.setIcon(QMessageBox::Question);
b.setWindowModality(Qt::WindowModal);
b.setText(tr("You must write a description to submit this crash report."));
b.addButton(QMessageBox::Ok);
b.exec();
return;
}
if (summary_edit_->document()->isEmpty()) {
QMessageBox b(this);
b.setIcon(QMessageBox::Question);
b.setWindowModality(Qt::WindowModal);
b.setText(
tr("You must write a description to submit this crash report."));
b.addButton(QMessageBox::Ok);
b.exec();
return;
}
QNetworkAccessManager* manager = new QNetworkAccessManager();
connect(manager, &QNetworkAccessManager::finished, this, &CrashHandlerDialog::ReplyFinished);
connect(manager, &QNetworkAccessManager::sslErrors, this, &CrashHandlerDialog::HandleSslErrors);
QNetworkAccessManager *manager = new QNetworkAccessManager();
connect(manager, &QNetworkAccessManager::finished, this,
&CrashHandlerDialog::ReplyFinished);
connect(manager, &QNetworkAccessManager::sslErrors, this,
&CrashHandlerDialog::HandleSslErrors);
QNetworkRequest request;
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
request.setUrl(QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
QNetworkRequest request;
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
request.setUrl(
QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
// Create HTTP form
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
// Create HTTP form
QHttpMultiPart *multipart =
new QHttpMultiPart(QHttpMultiPart::FormDataType);
// Create description section
QHttpPart desc_part;
desc_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"description\""));
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
multipart->append(desc_part);
// Create description section
QHttpPart desc_part;
desc_part.setHeader(QNetworkRequest::ContentTypeHeader,
QStringLiteral("text/plain; charset=UTF-8"));
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"description\""));
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
multipart->append(desc_part);
// Create report section
QHttpPart report_part;
report_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
report_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"report\""));
report_part.setBody(report_data_);
multipart->append(report_part);
// Create report section
QHttpPart report_part;
report_part.setHeader(QNetworkRequest::ContentTypeHeader,
QStringLiteral("text/plain; charset=UTF-8"));
report_part.setHeader(QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"report\""));
report_part.setBody(report_data_);
multipart->append(report_part);
// Create commit section
QHttpPart commit_part;
commit_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain; charset=UTF-8"));
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"commit\""));
commit_part.setBody(kAppVersionLong.toUtf8());
multipart->append(commit_part);
// Create commit section
QHttpPart commit_part;
commit_part.setHeader(QNetworkRequest::ContentTypeHeader,
QStringLiteral("text/plain; charset=UTF-8"));
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"commit\""));
commit_part.setBody(kAppVersionLong.toUtf8());
multipart->append(commit_part);
// Create dump section
QHttpPart dump_part;
dump_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream"));
dump_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
.arg(QFileInfo(report_filename_).fileName()));
QFile* dump_file = new QFile(report_filename_);
dump_file->open(QFile::ReadOnly);
dump_part.setBodyDevice(dump_file);
dump_file->setParent(multipart); // Delete file with multipart
multipart->append(dump_part);
// Create dump section
QHttpPart dump_part;
dump_part.setHeader(QNetworkRequest::ContentTypeHeader,
QStringLiteral("application/octet-stream"));
dump_part.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
.arg(QFileInfo(report_filename_).fileName()));
QFile *dump_file = new QFile(report_filename_);
dump_file->open(QFile::ReadOnly);
dump_part.setBodyDevice(dump_file);
dump_file->setParent(multipart); // Delete file with multipart
multipart->append(dump_part);
// Find symbol file
QDir symbol_dir(GetSymbolPath());
// Find symbol file
QDir symbol_dir(GetSymbolPath());
QString symbol_bin_name;
QString symbol_bin_name;
#if BUILDFLAG(IS_WIN)
symbol_bin_name = QStringLiteral("olive-editor.pdb");
symbol_bin_name = QStringLiteral("olive-editor.pdb");
#elif BUILDFLAG(IS_APPLE)
symbol_bin_name = QStringLiteral("Olive");
symbol_bin_name = QStringLiteral("Olive");
#else
symbol_bin_name = QStringLiteral("olive-editor");
symbol_bin_name = QStringLiteral("olive-editor");
#endif
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
QStringList folders_in_symbol_path = symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QStringList folders_in_symbol_path =
symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
if (folders_in_symbol_path.size() > 0) {
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
} else {
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Failed to send report"));
b.setText(tr("Failed to find symbols necessary to send report. "
"This is a packaging issue. Please notify "
"the maintainers of this package."));
b.addButton(QMessageBox::Ok);
b.exec();
return;
}
if (folders_in_symbol_path.size() > 0) {
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
} else {
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Failed to send report"));
b.setText(tr("Failed to find symbols necessary to send report. "
"This is a packaging issue. Please notify "
"the maintainers of this package."));
b.addButton(QMessageBox::Ok);
b.exec();
return;
}
// Create sym section
QString symbol_filename;
// Create sym section
QString symbol_filename;
#if BUILDFLAG(IS_APPLE)
symbol_filename = QStringLiteral("Olive.sym");
symbol_filename = QStringLiteral("Olive.sym");
#else
symbol_filename = QStringLiteral("olive-editor.sym");
symbol_filename = QStringLiteral("olive-editor.sym");
#endif
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
QHttpPart sym_part;
sym_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream"));
sym_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
.arg(symbol_filename));
QFile sym_file(symbol_full_path);
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
QHttpPart sym_part;
sym_part.setHeader(QNetworkRequest::ContentTypeHeader,
QStringLiteral("application/octet-stream"));
sym_part.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
.arg(symbol_filename));
QFile sym_file(symbol_full_path);
if (!sym_file.open(QFile::ReadOnly)) {
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Failed to send report"));
b.setText(tr("Failed to open symbol file. You may not have "
"permission to access it."));
b.addButton(QMessageBox::Ok);
b.exec();
return;
}
if (!sym_file.open(QFile::ReadOnly)) {
QMessageBox b(this);
b.setIcon(QMessageBox::Critical);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Failed to send report"));
b.setText(tr("Failed to open symbol file. You may not have "
"permission to access it."));
b.addButton(QMessageBox::Ok);
b.exec();
return;
}
QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
sym_file.close();
QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
sym_file.close();
sym_part.setBody(symbol_data);
multipart->append(sym_part);
sym_part.setBody(symbol_data);
multipart->append(sym_part);
manager->post(request, multipart);
manager->post(request, multipart);
SetGUIObjectsEnabled(false);
waiting_for_upload_ = true;
SetGUIObjectsEnabled(false);
waiting_for_upload_ = true;
}
void CrashHandlerDialog::closeEvent(QCloseEvent* e)
void CrashHandlerDialog::closeEvent(QCloseEvent *e)
{
QMessageBox b(this);
b.setIcon(QMessageBox::Warning);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Confirm Close"));
b.setText(tr("Crash report is still uploading. Closing now may result in no "
"report being sent. Are you sure you wish to close?"));
b.addButton(QMessageBox::Ok);
b.addButton(QMessageBox::Cancel);
QMessageBox b(this);
b.setIcon(QMessageBox::Warning);
b.setWindowModality(Qt::WindowModal);
b.setWindowTitle(tr("Confirm Close"));
b.setText(
tr("Crash report is still uploading. Closing now may result in no "
"report being sent. Are you sure you wish to close?"));
b.addButton(QMessageBox::Ok);
b.addButton(QMessageBox::Cancel);
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
e->ignore();
} else {
e->accept();
}
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
e->ignore();
} else {
e->accept();
}
}
}
int main(int argc, char *argv[])
{
QString report;
QString report;
#ifdef Q_OS_WINDOWS
int num_args;
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
if (num_args < 2) {
LocalFree(args);
return 1;
}
int num_args;
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
if (num_args < 2) {
LocalFree(args);
return 1;
}
report = QString::fromWCharArray(args[1]);
LocalFree(args);
report = QString::fromWCharArray(args[1]);
LocalFree(args);
#else
if (argc < 2) {
return 1;
}
if (argc < 2) {
return 1;
}
report = argv[1];
report = argv[1];
#endif
QApplication a(argc, argv);
QApplication a(argc, argv);
olive::CrashHandlerDialog chd(report);
chd.open();
olive::CrashHandlerDialog chd(report);
chd.open();
return a.exec();
return a.exec();
}
+22 -23
View File
@@ -30,51 +30,50 @@
#include "common/define.h"
namespace olive {
class CrashHandlerDialog : public QDialog
namespace olive
{
Q_OBJECT
class CrashHandlerDialog : public QDialog {
Q_OBJECT
public:
CrashHandlerDialog(const QString& report_path);
CrashHandlerDialog(const QString &report_path);
private:
void SetGUIObjectsEnabled(bool e);
void SetGUIObjectsEnabled(bool e);
void GenerateReport();
void GenerateReport();
static QString GetSymbolPath();
static QString GetSymbolPath();
QTextEdit* summary_edit_;
QTextEdit *summary_edit_;
QTextEdit* crash_report_;
QTextEdit *crash_report_;
QPushButton* send_report_btn_;
QPushButton *send_report_btn_;
QPushButton* dont_send_btn_;
QPushButton *dont_send_btn_;
QString report_filename_;
QString report_filename_;
QByteArray report_data_;
QByteArray report_data_;
bool waiting_for_upload_;
bool waiting_for_upload_;
protected:
virtual void closeEvent(QCloseEvent* e) override;
virtual void closeEvent(QCloseEvent *e) override;
private slots:
void ReplyFinished(QNetworkReply *reply);
void ReplyFinished(QNetworkReply *reply);
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
void AttemptToFindReport();
void AttemptToFindReport();
void ReadProcessHasData();
void ReadProcessHasData();
void ReadProcessFinished();
void SendErrorReport();
void ReadProcessFinished();
void SendErrorReport();
};
}
+96 -83
View File
@@ -30,114 +30,127 @@
#include "patreon.h"
#include "scrollinglabel.h"
namespace olive {
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
QDialog(parent)
namespace olive
{
if (welcome_dialog) {
setWindowTitle(tr("Welcome to %1").arg(QApplication::applicationName()));
} else {
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
}
QFontMetrics fm = fontMetrics();
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
: QDialog(parent)
{
if (welcome_dialog) {
setWindowTitle(
tr("Welcome to %1").arg(QApplication::applicationName()));
} else {
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
}
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
QFontMetrics fm = fontMetrics();
QHBoxLayout *horiz_layout = new QHBoxLayout();
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(), fm.height());
horiz_layout->setSpacing(fm.height()*2);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
fm.height());
QLabel* icon = new QLabel(QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
icon->setAlignment(Qt::AlignCenter);
horiz_layout->addWidget(icon);
QHBoxLayout *horiz_layout = new QHBoxLayout();
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
fm.height());
horiz_layout->setSpacing(fm.height() * 2);
// Construct About text
QLabel* label =
new QLabel(QStringLiteral("<html><head/><body>"
"<p><b>%1</b> %2</p>" // AppName (version identifier)
"<p><a href=\"https://www.olivevideoeditor.org/\">"
"https://www.olivevideoeditor.org/"
"</a></p>"
"<p>%3</p>" // First statement
"</body></html>").arg(QApplication::applicationName(),
QApplication::applicationVersion(),
tr("Olive is a free open source non-linear video editor. "
"This software is licensed under the GNU GPL Version 3.")));
QLabel *icon = new QLabel(
QStringLiteral("<html><img src=':/graphics/olive-splash.png'></html>"));
icon->setAlignment(Qt::AlignCenter);
horiz_layout->addWidget(icon);
// Set text formatting
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
label->setWordWrap(true);
label->setOpenExternalLinks(true);
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
label->setCursor(Qt::IBeamCursor);
horiz_layout->addWidget(label);
// Construct About text
QLabel *label = new QLabel(
QStringLiteral("<html><head/><body>"
"<p><b>%1</b> %2</p>" // AppName (version identifier)
"<p><a href=\"https://www.olivevideoeditor.org/\">"
"https://www.olivevideoeditor.org/"
"</a></p>"
"<p>%3</p>" // First statement
"</body></html>")
.arg(QApplication::applicationName(),
QApplication::applicationVersion(),
tr("Olive is a free open source non-linear video editor. "
"This software is licensed under the GNU GPL Version 3.")));
layout->addLayout(horiz_layout);
// Set text formatting
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
label->setWordWrap(true);
label->setOpenExternalLinks(true);
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
label->setTextInteractionFlags(Qt::TextSelectableByMouse |
Qt::LinksAccessibleByMouse);
label->setCursor(Qt::IBeamCursor);
horiz_layout->addWidget(label);
// Patrons where possible
layout->addWidget(new QLabel());
layout->addLayout(horiz_layout);
QString opening_statement;
// Patrons where possible
layout->addWidget(new QLabel());
if (welcome_dialog || patrons.isEmpty()) {
opening_statement = tr("<b>Olive relies on support from the community to continue its development.</b>");
} else {
opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people.");
}
QString opening_statement;
QLabel* support_lbl = new QLabel(tr("<html>%1 "
"If you like this project, please consider making a "
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
"support its development.</html>").arg(opening_statement));
support_lbl->setWordWrap(true);
support_lbl->setAlignment(Qt::AlignCenter);
support_lbl->setOpenExternalLinks(true);
layout->addWidget(support_lbl);
if (welcome_dialog || patrons.isEmpty()) {
opening_statement = tr(
"<b>Olive relies on support from the community to continue its development.</b>");
} else {
opening_statement = tr(
"Olive wouldn't be possible without the support of gracious donations from the following people.");
}
if (!patrons.isEmpty()) {
ScrollingLabel* scroll = new ScrollingLabel(patrons);
scroll->StartAnimating();
layout->addWidget(scroll);
}
QLabel *support_lbl = new QLabel(
tr("<html>%1 "
"If you like this project, please consider making a "
"<a href='https://olivevideoeditor.org/donate.php'>one-time donation</a> or "
"<a href='https://www.patreon.com/olivevideoeditor'>pledging monthly</a> to "
"support its development.</html>")
.arg(opening_statement));
support_lbl->setWordWrap(true);
support_lbl->setAlignment(Qt::AlignCenter);
support_lbl->setOpenExternalLinks(true);
layout->addWidget(support_lbl);
layout->addWidget(new QLabel());
if (!patrons.isEmpty()) {
ScrollingLabel *scroll = new ScrollingLabel(patrons);
scroll->StartAnimating();
layout->addWidget(scroll);
}
QHBoxLayout *btn_layout = new QHBoxLayout();
btn_layout->setContentsMargins(0, 0, 0, 0);
btn_layout->setSpacing(0);
layout->addWidget(new QLabel());
if (welcome_dialog) {
dont_show_again_checkbox_ = new QCheckBox(tr("Don't show this message again"));
btn_layout->addWidget(dont_show_again_checkbox_);
} else {
dont_show_again_checkbox_ = nullptr;
}
QHBoxLayout *btn_layout = new QHBoxLayout();
btn_layout->setContentsMargins(0, 0, 0, 0);
btn_layout->setSpacing(0);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
if (!welcome_dialog) {
buttons->setCenterButtons(true);
}
btn_layout->addWidget(buttons);
if (welcome_dialog) {
dont_show_again_checkbox_ =
new QCheckBox(tr("Don't show this message again"));
btn_layout->addWidget(dont_show_again_checkbox_);
} else {
dont_show_again_checkbox_ = nullptr;
}
layout->addLayout(btn_layout);
QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok, this);
if (!welcome_dialog) {
buttons->setCenterButtons(true);
}
btn_layout->addWidget(buttons);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
layout->addLayout(btn_layout);
setFixedSize(sizeHint());
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
setFixedSize(sizeHint());
}
void AboutDialog::accept()
{
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
OLIVE_CONFIG("ShowWelcomeDialog") = false;
}
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
OLIVE_CONFIG("ShowWelcomeDialog") = false;
}
QDialog::accept();
QDialog::accept();
}
}
+8 -9
View File
@@ -26,7 +26,8 @@
#include "common/define.h"
namespace olive {
namespace olive
{
/**
* @brief The AboutDialog class
@@ -34,11 +35,10 @@ namespace olive {
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
* anywhere
*/
class AboutDialog : public QDialog
{
Q_OBJECT
class AboutDialog : public QDialog {
Q_OBJECT
public:
/**
/**
* @brief AboutDialog Constructor
*
* Creates About dialog.
@@ -47,14 +47,13 @@ public:
*
* QWidget parent object. Usually this will be MainWindow.
*/
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
public slots:
virtual void accept() override;
virtual void accept() override;
private:
QCheckBox *dont_show_again_checkbox_;
QCheckBox *dont_show_again_checkbox_;
};
}
+58 -55
View File
@@ -24,98 +24,101 @@
#include "common/qtutils.h"
namespace olive {
namespace olive
{
const int ScrollingLabel::kMinLineHeight = 10;
ScrollingLabel::ScrollingLabel(QWidget *parent) :
QWidget(parent),
animate_(0)
ScrollingLabel::ScrollingLabel(QWidget *parent)
: QWidget(parent)
, animate_(0)
{
timer_.setInterval(50);
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
timer_.setInterval(50);
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::AnimationUpdate);
}
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent) :
ScrollingLabel(parent)
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent)
: ScrollingLabel(parent)
{
SetText(text);
SetText(text);
}
void ScrollingLabel::SetText(const QStringList &text)
{
text_ = text;
text_ = text;
QFontMetrics fm = fontMetrics();
text_height_ = fm.height();
QFontMetrics fm = fontMetrics();
text_height_ = fm.height();
int width = 0;
foreach (const QString& s, text_) {
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
}
int width = 0;
foreach (const QString &s, text_) {
width = qMax(width, QtUtils::QFontMetricsWidth(fm, s));
}
setMinimumSize(width, text_height_ * kMinLineHeight);
setMinimumSize(width, text_height_ * kMinLineHeight);
}
void ScrollingLabel::paintEvent(QPaintEvent *e)
{
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
map.fill(Qt::transparent);
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
map.fill(Qt::transparent);
{
QPainter p(&map);
p.setPen(palette().text().color());
{
QPainter p(&map);
p.setPen(palette().text().color());
QFontMetrics fm = p.fontMetrics();
int half_width = width();
QFontMetrics fm = p.fontMetrics();
int half_width = width();
for (int i=0; i<text_.size(); i++) {
int text_y = fm.ascent() + height() - animate_ + (fm.height()*i);
int text_bottom = text_y + fm.descent();
int text_top = text_y - fm.ascent();
for (int i = 0; i < text_.size(); i++) {
int text_y = fm.ascent() + height() - animate_ + (fm.height() * i);
int text_bottom = text_y + fm.descent();
int text_top = text_y - fm.ascent();
if (text_bottom < 0 || text_top >= height()) {
continue;
}
if (text_bottom < 0 || text_top >= height()) {
continue;
}
const QString& s = text_.at(i);
const QString &s = text_.at(i);
int width = QtUtils::QFontMetricsWidth(fm, s);
p.drawText(half_width/2 - width/2, text_y, s);
}
int width = QtUtils::QFontMetricsWidth(fm, s);
p.drawText(half_width / 2 - width / 2, text_y, s);
}
for (int y=0; y<text_height_; y++) {
double mul = double(y)/double(text_height_);
for (int y = 0; y < text_height_; y++) {
double mul = double(y) / double(text_height_);
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
SetOpacityOfScanLine(map.scanLine(map.height()-1-y), map.width(), 4, mul);
}
}
SetOpacityOfScanLine(map.scanLine(y), map.width(), 4, mul);
SetOpacityOfScanLine(map.scanLine(map.height() - 1 - y),
map.width(), 4, mul);
}
}
QPainter wp(this);
wp.drawImage(0, 0, map);
QPainter wp(this);
wp.drawImage(0, 0, map);
}
void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width, int channels, double mul)
void ScrollingLabel::SetOpacityOfScanLine(uchar *scan_line, int width,
int channels, double mul)
{
for (int x=0; x<width; x++) {
uchar* pixel = &scan_line[x*4];
for (int x = 0; x < width; x++) {
uchar *pixel = &scan_line[x * 4];
for (int c=0; c<channels; c++) {
pixel[c] *= mul;
}
}
for (int c = 0; c < channels; c++) {
pixel[c] *= mul;
}
}
}
void ScrollingLabel::AnimationUpdate()
{
animate_++;
animate_++;
if (animate_ >= height() + text_.size() * text_height_) {
animate_ = 0;
}
if (animate_ >= height() + text_.size() * text_height_) {
animate_ = 0;
}
update();
update();
}
}
+24 -24
View File
@@ -24,46 +24,46 @@
#include <QTimer>
#include <QWidget>
namespace olive {
class ScrollingLabel : public QWidget
namespace olive
{
Q_OBJECT
class ScrollingLabel : public QWidget {
Q_OBJECT
public:
ScrollingLabel(QWidget* parent = nullptr);
ScrollingLabel(const QStringList& text, QWidget* parent = nullptr);
ScrollingLabel(QWidget *parent = nullptr);
ScrollingLabel(const QStringList &text, QWidget *parent = nullptr);
void SetText(const QStringList& text);
void SetText(const QStringList &text);
void StartAnimating()
{
timer_.start();
}
void StartAnimating()
{
timer_.start();
}
void StopAnimating()
{
timer_.stop();
}
void StopAnimating()
{
timer_.stop();
}
protected:
virtual void paintEvent(QPaintEvent* e) override;
virtual void paintEvent(QPaintEvent *e) override;
private:
static void SetOpacityOfScanLine(uchar* scan_line, int width, int channels, double mul);
static void SetOpacityOfScanLine(uchar *scan_line, int width, int channels,
double mul);
static const int kMinLineHeight;
static const int kMinLineHeight;
QStringList text_;
QStringList text_;
int text_height_;
int text_height_;
QTimer timer_;
QTimer timer_;
int animate_;
int animate_;
private slots:
void AnimationUpdate();
void AnimationUpdate();
};
}
+187 -183
View File
@@ -25,247 +25,251 @@
#include <QMenuBar>
#include <QVBoxLayout>
namespace olive {
ActionSearch::ActionSearch(QWidget *parent) :
QDialog(parent),
menu_bar_(nullptr)
namespace olive
{
// ActionSearch requires a parent widget
Q_ASSERT(parent != nullptr);
// Set styling (object name is required for CSS specific to this object)
setObjectName("ASDiag");
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
ActionSearch::ActionSearch(QWidget *parent)
: QDialog(parent)
, menu_bar_(nullptr)
{
// ActionSearch requires a parent widget
Q_ASSERT(parent != nullptr);
// Size proportionally to the parent (usually MainWindow).
resize(parent->width()/3, parent->height()/3);
// Set styling (object name is required for CSS specific to this object)
setObjectName("ASDiag");
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
setWindowFlags(Qt::Popup);
// Size proportionally to the parent (usually MainWindow).
resize(parent->width() / 3, parent->height() / 3);
QVBoxLayout* layout = new QVBoxLayout(this);
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
setWindowFlags(Qt::Popup);
// Construct the main entry text field.
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
QVBoxLayout *layout = new QVBoxLayout(this);
// Set the main entry field font size to 1.2x its standard font size.
QFont entry_field_font = entry_field->font();
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
entry_field->setFont(entry_field_font);
// Construct the main entry text field.
ActionSearchEntry *entry_field = new ActionSearchEntry(this);
// Set placeholder text for the main entry field
entry_field->setPlaceholderText(tr("Search for action..."));
// Set the main entry field font size to 1.2x its standard font size.
QFont entry_field_font = entry_field->font();
entry_field_font.setPointSize(qRound(entry_field_font.pointSize() * 1.2));
entry_field->setFont(entry_field_font);
// Connect signals/slots
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
// Set placeholder text for the main entry field
entry_field->setPlaceholderText(tr("Search for action..."));
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
// We override it here to select the upper or lower item in the list.
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up()));
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
layout->addWidget(entry_field);
// Connect signals/slots
connect(entry_field, SIGNAL(textChanged(const QString &)), this,
SLOT(search_update(const QString &)));
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
// Construct list of actions
list_widget = new ActionSearchList(this);
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
// We override it here to select the upper or lower item in the list.
connect(entry_field, SIGNAL(moveSelectionUp()), this,
SLOT(move_selection_up()));
connect(entry_field, SIGNAL(moveSelectionDown()), this,
SLOT(move_selection_down()));
layout->addWidget(entry_field);
// Set list's font to 1.2x its standard font size
QFont list_widget_font = list_widget->font();
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
list_widget->setFont(list_widget_font);
// Construct list of actions
list_widget = new ActionSearchList(this);
layout->addWidget(list_widget);
// Set list's font to 1.2x its standard font size
QFont list_widget_font = list_widget->font();
list_widget_font.setPointSize(qRound(list_widget_font.pointSize() * 1.2));
list_widget->setFont(list_widget_font);
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
layout->addWidget(list_widget);
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
// shortcut for example).
entry_field->setFocus();
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
// shortcut for example).
entry_field->setFocus();
}
void ActionSearch::SetMenuBar(QMenuBar *menu_bar)
{
menu_bar_ = menu_bar;
menu_bar_ = menu_bar;
}
void ActionSearch::search_update(const QString &s, const QString &p, QMenu *parent)
void ActionSearch::search_update(const QString &s, const QString &p,
QMenu *parent)
{
// Do nothing if there's no menu bar to work with
if (menu_bar_ == nullptr) {
return;
}
// Do nothing if there's no menu bar to work with
if (menu_bar_ == nullptr) {
return;
}
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
// submenus).
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
// submenus).
if (parent == nullptr) {
if (parent == nullptr) {
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
// (and their submenus).
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
// (and their submenus).
// We'll clear all the current items in the list since if we're here, we're just starting.
list_widget->clear();
// We'll clear all the current items in the list since if we're here, we're just starting.
list_widget->clear();
QList<QAction *> menus = menu_bar_->actions();
QList<QAction*> menus = menu_bar_->actions();
// Loop through all menus from the menubar and run this function on each one.
for (int i = 0; i < menus.size(); i++) {
QMenu *menu = menus.at(i)->menu();
// Loop through all menus from the menubar and run this function on each one.
for (int i=0;i<menus.size();i++) {
QMenu* menu = menus.at(i)->menu();
search_update(s, p, menu);
}
search_update(s, p, menu);
}
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
// keyboard-exclusive functionality.
if (list_widget->count() > 0) {
list_widget->item(0)->setSelected(true);
}
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
// keyboard-exclusive functionality.
if (list_widget->count() > 0) {
list_widget->item(0)->setSelected(true);
}
} else {
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
} else {
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
// adding the current menu's text to the existing hierarchy (passed in `p`).
QString menu_text;
if (!p.isEmpty())
menu_text += p + " > ";
menu_text += parent->title().replace(
"&", ""); // Strip out any &s used in menu action names
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
// Loop over the menu's actions
QList<QAction *> actions = parent->actions();
for (int i = 0; i < actions.size(); i++) {
QAction *a = actions.at(i);
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
// adding the current menu's text to the existing hierarchy (passed in `p`).
QString menu_text;
if (!p.isEmpty()) menu_text += p + " > ";
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names
// Ignore separator actions
if (!a->isSeparator()) {
if (a->menu() != nullptr) {
// If the action is a menu, run this function recursively on it
search_update(s, menu_text, a->menu());
// Loop over the menu's actions
QList<QAction*> actions = parent->actions();
for (int i=0;i<actions.size();i++) {
} else {
// This is a valid non-separator non-menu action, so check it against the currently entered string.
QAction* a = actions.at(i);
// Strip out all &s from the action's name
QString comp = a->text().replace("&", "");
// Ignore separator actions
if (!a->isSeparator()) {
// See if the action's name contains any of the currently entered string
if (comp.contains(s, Qt::CaseInsensitive)) {
// If so, we add it to the list widget.
QListWidgetItem *item = new QListWidgetItem(
QStringLiteral("%1\n(%2)").arg(comp, menu_text),
list_widget);
if (a->menu() != nullptr) {
// Add a pointer to the original QAction in the item's data
item->setData(Qt::UserRole + 1,
reinterpret_cast<quintptr>(a));
// If the action is a menu, run this function recursively on it
search_update(s, menu_text, a->menu());
} else {
// This is a valid non-separator non-menu action, so check it against the currently entered string.
// Strip out all &s from the action's name
QString comp = a->text().replace("&", "");
// See if the action's name contains any of the currently entered string
if (comp.contains(s, Qt::CaseInsensitive)) {
// If so, we add it to the list widget.
QListWidgetItem* item = new QListWidgetItem(QStringLiteral("%1\n(%2)").arg(comp, menu_text), list_widget);
// Add a pointer to the original QAction in the item's data
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
list_widget->addItem(item);
}
}
}
}
}
list_widget->addItem(item);
}
}
}
}
}
}
void ActionSearch::perform_action() {
void ActionSearch::perform_action()
{
// Loop over all the items in the list and if we find one that's selected, we trigger it.
QList<QListWidgetItem *> selected_items = list_widget->selectedItems();
if (list_widget->count() > 0 && selected_items.size() > 0) {
QListWidgetItem *item = selected_items.at(0);
// Loop over all the items in the list and if we find one that's selected, we trigger it.
QList<QListWidgetItem*> selected_items = list_widget->selectedItems();
if (list_widget->count() > 0 && selected_items.size() > 0) {
// Get QAction pointer from item's data
QAction *a = reinterpret_cast<QAction *>(
item->data(Qt::UserRole + 1).value<quintptr>());
QListWidgetItem* item = selected_items.at(0);
// Get QAction pointer from item's data
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
a->trigger();
}
// Close this popup
accept();
a->trigger();
}
// Close this popup
accept();
}
void ActionSearch::move_selection_up() {
void ActionSearch::move_selection_up()
{
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
// bottom item).
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
// bottom item).
int lim = list_widget->count();
for (int i=1;i<lim;i++) {
if (list_widget->item(i)->isSelected()) {
list_widget->item(i-1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i-1));
break;
}
}
int lim = list_widget->count();
for (int i = 1; i < lim; i++) {
if (list_widget->item(i)->isSelected()) {
list_widget->item(i - 1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i - 1));
break;
}
}
}
void ActionSearch::move_selection_down() {
void ActionSearch::move_selection_down()
{
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
// bottom item).
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
// bottom item).
int lim = list_widget->count()-1;
for (int i=0;i<lim;i++) {
if (list_widget->item(i)->isSelected()) {
list_widget->item(i+1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i+1));
break;
}
}
int lim = list_widget->count() - 1;
for (int i = 0; i < lim; i++) {
if (list_widget->item(i)->isSelected()) {
list_widget->item(i + 1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i + 1));
break;
}
}
}
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {}
ActionSearchEntry::ActionSearchEntry(QWidget *parent)
: QLineEdit(parent)
{
}
bool ActionSearchEntry::event(QEvent *e)
{
switch (e->type()) {
case QEvent::ShortcutOverride:
switch (static_cast<QKeyEvent*>(e)->key()) {
case Qt::Key_Up:
case Qt::Key_Down:
e->accept();
return true;
}
break;
case QEvent::KeyPress:
// Listen for up/down, otherwise pass the key event to the base class.
switch (static_cast<QKeyEvent*>(e)->key()) {
case Qt::Key_Up:
e->accept();
emit moveSelectionUp();
return true;
case Qt::Key_Down:
e->accept();
emit moveSelectionDown();
return true;
}
break;
default:
break;
}
switch (e->type()) {
case QEvent::ShortcutOverride:
switch (static_cast<QKeyEvent *>(e)->key()) {
case Qt::Key_Up:
case Qt::Key_Down:
e->accept();
return true;
}
break;
case QEvent::KeyPress:
// Listen for up/down, otherwise pass the key event to the base class.
switch (static_cast<QKeyEvent *>(e)->key()) {
case Qt::Key_Up:
e->accept();
emit moveSelectionUp();
return true;
case Qt::Key_Down:
e->accept();
emit moveSelectionDown();
return true;
}
break;
default:
break;
}
return QLineEdit::event(e);
return QLineEdit::event(e);
}
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {}
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) {
// Indiscriminately emit a signal on any double click
emit dbl_click();
ActionSearchList::ActionSearchList(QWidget *parent)
: QListWidget(parent)
{
}
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *)
{
// Indiscriminately emit a signal on any double click
emit dbl_click();
}
}
+40 -36
View File
@@ -29,7 +29,8 @@
#include "common/define.h"
namespace olive {
namespace olive
{
class ActionSearchList;
@@ -39,11 +40,10 @@ class ActionSearchList;
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
*/
class ActionSearch : public QDialog
{
Q_OBJECT
class ActionSearch : public QDialog {
Q_OBJECT
public:
/**
/**
* @brief ActionSearch Constructor
*
* Create ActionSearch popup.
@@ -52,14 +52,14 @@ public:
*
* QWidget parent. Usually MainWindow.
*/
ActionSearch(QWidget* parent);
ActionSearch(QWidget *parent);
/**
/**
* @brief Set the menu bar to use in this action search
*/
void SetMenuBar(QMenuBar* menu_bar);
void SetMenuBar(QMenuBar *menu_bar);
private slots:
/**
/**
* @brief Update the list of actions according to a search query
*
* This function adds/removes actions in the action list according to a given search query entered by the user.
@@ -82,42 +82,44 @@ private slots:
* The current menu to loop over. In most cases, this should be left as nullptr when called externally.
* search_update() will fill this automatically as it needs while calling itself recursively.
*/
void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr);
void search_update(const QString &s, const QString &p = nullptr,
QMenu *parent = nullptr);
/**
/**
* @brief Perform the currently selected action
*
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
* triggered and the popup closes anyway.
*/
void perform_action();
void perform_action();
/**
/**
* @brief Move selection up
*
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
* selection is already at the top of the list, this is a no-op.
*/
void move_selection_up();
void move_selection_up();
/**
/**
* @brief Move selection down
*
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
* selection is already at the bottom of the list, this is a no-op.
*/
void move_selection_down();
void move_selection_down();
private:
/**
/**
* @brief Main widget that shows the list of commands
*/
ActionSearchList* list_widget;
ActionSearchList *list_widget;
/**
/**
* @brief Attached menu bar object
*/
QMenuBar* menu_bar_;
QMenuBar *menu_bar_;
};
/**
@@ -127,25 +129,26 @@ private:
* to a slot that triggers the currently selected action.
*/
class ActionSearchList : public QListWidget {
Q_OBJECT
Q_OBJECT
public:
/**
/**
* @brief ActionSearchList Constructor
* @param parent
*
* Usually ActionSearch.
*/
ActionSearchList(QWidget* parent);
ActionSearchList(QWidget *parent);
protected:
/**
/**
* @brief Override of QListWidget's double click event that emits a signal.
*/
void mouseDoubleClickEvent(QMouseEvent *);
void mouseDoubleClickEvent(QMouseEvent *);
signals:
/**
/**
* @brief Signal emitted when a QListWidget item is double clicked.
*/
void dbl_click();
void dbl_click();
};
/**
@@ -155,32 +158,33 @@ signals:
* can connect them to moving the current selection up or down.
*/
class ActionSearchEntry : public QLineEdit {
Q_OBJECT
Q_OBJECT
public:
/**
/**
* @brief ActionSearchEntry
* @param parent
*
* Usually ActionSearch.
*/
ActionSearchEntry(QWidget* parent);
ActionSearchEntry(QWidget *parent);
protected:
/**
/**
* @brief Override of QLineEdit's key press event that listens for up/down key presses.
* @param event
*/
virtual bool event(QEvent *e) override;
virtual bool event(QEvent *e) override;
signals:
/**
/**
* @brief Emitted when the user presses the up arrow key.
*/
void moveSelectionUp();
void moveSelectionUp();
/**
/**
* @brief Emitted when the user presses the down arrow key.
*/
void moveSelectionDown();
void moveSelectionDown();
};
}
+90 -71
View File
@@ -29,108 +29,127 @@
#include "core.h"
namespace olive {
namespace olive
{
#define super QDialog
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message, const QStringList &recoveries, bool autocheck_latest, QWidget* parent) :
QDialog(parent)
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message,
const QStringList &recoveries,
bool autocheck_latest, QWidget *parent)
: QDialog(parent)
{
Init(message);
Init(message);
PopulateTree(recoveries, autocheck_latest);
PopulateTree(recoveries, autocheck_latest);
}
void AutoRecoveryDialog::accept()
{
foreach (QTreeWidgetItem* checkable, checkable_items_) {
if (checkable->checkState(0) == Qt::Checked) {
QString filename = checkable->data(0, kFilenameRole).toString();
Core::instance()->OpenRecoveryProject(filename);
}
}
foreach (QTreeWidgetItem *checkable, checkable_items_) {
if (checkable->checkState(0) == Qt::Checked) {
QString filename = checkable->data(0, kFilenameRole).toString();
Core::instance()->OpenRecoveryProject(filename);
}
}
super::accept();
super::accept();
}
void AutoRecoveryDialog::Init(const QString& header_text)
void AutoRecoveryDialog::Init(const QString &header_text)
{
QVBoxLayout* layout = new QVBoxLayout(this);
QVBoxLayout *layout = new QVBoxLayout(this);
setWindowTitle(tr("Auto-Recovery"));
setWindowTitle(tr("Auto-Recovery"));
layout->addWidget(new QLabel(header_text));
layout->addWidget(new QLabel(header_text));
tree_widget_ = new QTreeWidget();
tree_widget_->setHeaderHidden(true);
layout->addWidget(tree_widget_);
tree_widget_ = new QTreeWidget();
tree_widget_->setHeaderHidden(true);
layout->addWidget(tree_widget_);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
connect(buttons, &QDialogButtonBox::accepted, this, &AutoRecoveryDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &AutoRecoveryDialog::reject);
layout->addWidget(buttons);
QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
connect(buttons, &QDialogButtonBox::accepted, this,
&AutoRecoveryDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this,
&AutoRecoveryDialog::reject);
layout->addWidget(buttons);
}
void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autocheck_latest)
void AutoRecoveryDialog::PopulateTree(const QStringList &recoveries,
bool autocheck_latest)
{
// Each entry in `recoveries` is a directory with 1+ recovery projects in it
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
// Each entry in `recoveries` is a directory with 1+ recovery projects in it
QDir autorecovery_root(FileFunctions::GetAutoRecoveryRoot());
foreach (const QString& recovery_folder, recoveries) {
QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
foreach (const QString &recovery_folder, recoveries) {
QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
QString pretty_name;
{
// Retrieve pretty name
QFile pretty_name_file(recovery_dir.filePath(QStringLiteral("realname.txt")));
if (pretty_name_file.open(QFile::ReadOnly)) {
// Read pretty name that we should have written in the autorecovery process
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
pretty_name_file.close();
}
QString pretty_name;
{
// Retrieve pretty name
QFile pretty_name_file(
recovery_dir.filePath(QStringLiteral("realname.txt")));
if (pretty_name_file.open(QFile::ReadOnly)) {
// Read pretty name that we should have written in the autorecovery process
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
pretty_name_file.close();
}
if (pretty_name.isEmpty()) {
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
pretty_name = recovery_dir.dirName();
}
}
if (pretty_name.isEmpty()) {
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
pretty_name = recovery_dir.dirName();
}
}
QTreeWidgetItem* top_level = new QTreeWidgetItem(tree_widget_);
top_level->setText(0, pretty_name);
QTreeWidgetItem *top_level = new QTreeWidgetItem(tree_widget_);
top_level->setText(0, pretty_name);
{
// Populate with recoveries
QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
for (int i=0; i<entries.size(); i++) {
const QString& entry = entries.at(i);
{
// Populate with recoveries
QStringList entries =
recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
QDir::Name | QDir::Reversed);
for (int i = 0; i < entries.size(); i++) {
const QString &entry = entries.at(i);
if (entry.endsWith(QStringLiteral(".ove"), Qt::CaseInsensitive)) {
QTreeWidgetItem* entry_item = new QTreeWidgetItem(top_level);
if (entry.endsWith(QStringLiteral(".ove"),
Qt::CaseInsensitive)) {
QTreeWidgetItem *entry_item =
new QTreeWidgetItem(top_level);
bool ok;
qint64 recovery_time = entry.left(entry.indexOf('.')).toLongLong(&ok);
QString entry_name;
bool ok;
qint64 recovery_time =
entry.left(entry.indexOf('.')).toLongLong(&ok);
QString entry_name;
if (ok) {
// Set as time/date of recovery
entry_name = QDateTime::fromSecsSinceEpoch(recovery_time).toString();
} else {
// Fallback if we couldn't discern a date from this
entry_name = entry;
}
if (ok) {
// Set as time/date of recovery
entry_name =
QDateTime::fromSecsSinceEpoch(recovery_time)
.toString();
} else {
// Fallback if we couldn't discern a date from this
entry_name = entry;
}
entry_item->setText(0, entry_name);
entry_item->setData(0, kFilenameRole, recovery_dir.filePath(entry));
entry_item->setText(0, entry_name);
entry_item->setData(0, kFilenameRole,
recovery_dir.filePath(entry));
// Allow to be checked, auto-checking the first entry
entry_item->setCheckState(0, (autocheck_latest && top_level->childCount() == 1) ? Qt::Checked : Qt::Unchecked);
// Allow to be checked, auto-checking the first entry
entry_item->setCheckState(
0, (autocheck_latest && top_level->childCount() == 1) ?
Qt::Checked :
Qt::Unchecked);
checkable_items_.append(entry_item);
}
}
}
}
checkable_items_.append(entry_item);
}
}
}
}
}
}
+12 -14
View File
@@ -26,30 +26,28 @@
#include "common/define.h"
namespace olive {
class AutoRecoveryDialog : public QDialog
namespace olive
{
Q_OBJECT
class AutoRecoveryDialog : public QDialog {
Q_OBJECT
public:
AutoRecoveryDialog(const QString& message, const QStringList& recoveries, bool autocheck_latest, QWidget* parent);
AutoRecoveryDialog(const QString &message, const QStringList &recoveries,
bool autocheck_latest, QWidget *parent);
public slots:
virtual void accept() override;
virtual void accept() override;
private:
void Init(const QString &header_text);
void Init(const QString &header_text);
void PopulateTree(const QStringList &recoveries, bool autocheck);
void PopulateTree(const QStringList &recoveries, bool autocheck);
QTreeWidget* tree_widget_;
QTreeWidget *tree_widget_;
QVector<QTreeWidgetItem*> checkable_items_;
enum DataRole {
kFilenameRole = Qt::UserRole
};
QVector<QTreeWidgetItem *> checkable_items_;
enum DataRole { kFilenameRole = Qt::UserRole };
};
}
+120 -101
View File
@@ -26,167 +26,186 @@
#include "common/qtutils.h"
namespace olive {
ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start, QWidget *parent) :
QDialog(parent),
color_manager_(color_manager)
namespace olive
{
setWindowTitle(tr("Select Color"));
QVBoxLayout* layout = new QVBoxLayout(this);
ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
QWidget *parent)
: QDialog(parent)
, color_manager_(color_manager)
{
setWindowTitle(tr("Select Color"));
QSplitter* splitter = new QSplitter(Qt::Horizontal);
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
QVBoxLayout *layout = new QVBoxLayout(this);
QWidget* graphics_area = new QWidget();
splitter->addWidget(graphics_area);
QSplitter *splitter = new QSplitter(Qt::Horizontal);
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
QWidget *graphics_area = new QWidget();
splitter->addWidget(graphics_area);
QHBoxLayout* wheel_layout = new QHBoxLayout();
graphics_layout->addLayout(wheel_layout);
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
color_wheel_ = new ColorWheelWidget();
wheel_layout->addWidget(color_wheel_);
QHBoxLayout *wheel_layout = new QHBoxLayout();
graphics_layout->addLayout(wheel_layout);
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
hsv_value_gradient_->setFixedWidth(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
wheel_layout->addWidget(hsv_value_gradient_);
color_wheel_ = new ColorWheelWidget();
wheel_layout->addWidget(color_wheel_);
QHBoxLayout *swatch_layout = new QHBoxLayout();
graphics_layout->addLayout(swatch_layout);
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
hsv_value_gradient_->setFixedWidth(
QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
wheel_layout->addWidget(hsv_value_gradient_);
swatch_layout->addStretch();
QHBoxLayout *swatch_layout = new QHBoxLayout();
graphics_layout->addLayout(swatch_layout);
swatch_ = new ColorSwatchChooser(color_manager_);
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
swatch_layout->addWidget(swatch_);
swatch_layout->addStretch();
swatch_layout->addStretch();
swatch_ = new ColorSwatchChooser(color_manager_);
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
swatch_layout->addWidget(swatch_);
QWidget* value_area = new QWidget();
QVBoxLayout* value_layout = new QVBoxLayout(value_area);
value_layout->setSpacing(0);
splitter->addWidget(value_area);
swatch_layout->addStretch();
color_values_widget_ = new ColorValuesWidget(color_manager_);
color_values_widget_->IgnorePickFrom(this);
value_layout->addWidget(color_values_widget_);
QWidget *value_area = new QWidget();
QVBoxLayout *value_layout = new QVBoxLayout(value_area);
value_layout->setSpacing(0);
splitter->addWidget(value_area);
chooser_ = new ColorSpaceChooser(color_manager_);
color_values_widget_ = new ColorValuesWidget(color_manager_);
color_values_widget_->IgnorePickFrom(this);
value_layout->addWidget(color_values_widget_);
value_layout->addWidget(chooser_);
chooser_ = new ColorSpaceChooser(color_manager_);
// Split window 50/50
splitter->setSizes({INT_MAX, INT_MAX});
value_layout->addWidget(chooser_);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_, &ColorValuesWidget::SetColor);
// Split window 50/50
splitter->setSizes({ INT_MAX, INT_MAX });
connect(color_wheel_, &ColorWheelWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
color_values_widget_, &ColorValuesWidget::SetColor);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged,
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_,
&ColorSwatchChooser::SetCurrentColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
color_values_widget_, &ColorValuesWidget::SetColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged,
swatch_, &ColorSwatchChooser::SetCurrentColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged,
color_wheel_, &ColorWheelWidget::SetSelectedColor);
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_,
&ColorSwatchChooser::SetCurrentColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_,
&ColorGradientWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_,
&ColorWheelWidget::SetSelectedColor);
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_,
&ColorValuesWidget::SetColor);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
layout->addWidget(buttons);
connect(color_wheel_, &ColorWheelWidget::DiameterChanged,
hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
SetColor(start);
QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
layout->addWidget(buttons);
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged);
ColorSpaceChanged(chooser_->input(), chooser_->output());
SetColor(start);
// Set default size ratio to 2:1
resize(sizeHint().height() * 2, sizeHint().height());
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this,
&ColorDialog::ColorSpaceChanged);
ColorSpaceChanged(chooser_->input(), chooser_->output());
// Set default size ratio to 2:1
resize(sizeHint().height() * 2, sizeHint().height());
}
void ColorDialog::SetColor(const ManagedColor &start)
{
chooser_->set_input(start.color_input());
chooser_->set_output(start.color_output());
chooser_->set_input(start.color_input());
chooser_->set_output(start.color_output());
Color managed_start;
Color managed_start;
if (start.color_input().isEmpty()) {
if (start.color_input().isEmpty()) {
managed_start = start;
managed_start = start;
} else {
// Convert reference color to the input space
ColorProcessorPtr linear_to_input = ColorProcessor::Create(
color_manager_, color_manager_->GetReferenceColorSpace(),
start.color_input());
} else {
managed_start = linear_to_input->ConvertColor(start);
}
// Convert reference color to the input space
ColorProcessorPtr linear_to_input = ColorProcessor::Create(color_manager_,
color_manager_->GetReferenceColorSpace(),
start.color_input());
managed_start = linear_to_input->ConvertColor(start);
}
color_wheel_->SetSelectedColor(managed_start);
hsv_value_gradient_->SetSelectedColor(managed_start);
color_values_widget_->SetColor(managed_start);
swatch_->SetCurrentColor(managed_start);
color_wheel_->SetSelectedColor(managed_start);
hsv_value_gradient_->SetSelectedColor(managed_start);
color_values_widget_->SetColor(managed_start);
swatch_->SetCurrentColor(managed_start);
}
ManagedColor ColorDialog::GetSelectedColor() const
{
ManagedColor selected = color_wheel_->GetSelectedColor();
ManagedColor selected = color_wheel_->GetSelectedColor();
// Convert to linear and return a linear color
if (input_to_ref_processor_) {
selected = input_to_ref_processor_->ConvertColor(selected);
}
// Convert to linear and return a linear color
if (input_to_ref_processor_) {
selected = input_to_ref_processor_->ConvertColor(selected);
}
selected.set_color_input(GetColorSpaceInput());
selected.set_color_output(GetColorSpaceOutput());
selected.set_color_input(GetColorSpaceInput());
selected.set_color_output(GetColorSpaceOutput());
return selected;
return selected;
}
QString ColorDialog::GetColorSpaceInput() const
{
return chooser_->input();
return chooser_->input();
}
ColorTransform ColorDialog::GetColorSpaceOutput() const
{
return chooser_->output();
return chooser_->output();
}
void ColorDialog::ColorSpaceChanged(const QString &input, const ColorTransform &output)
void ColorDialog::ColorSpaceChanged(const QString &input,
const ColorTransform &output)
{
input_to_ref_processor_ = ColorProcessor::Create(color_manager_, input, color_manager_->GetReferenceColorSpace());
input_to_ref_processor_ = ColorProcessor::Create(
color_manager_, input, color_manager_->GetReferenceColorSpace());
ColorProcessorPtr ref_to_display = ColorProcessor::Create(color_manager_,
color_manager_->GetReferenceColorSpace(),
output);
ColorProcessorPtr ref_to_display = ColorProcessor::Create(
color_manager_, color_manager_->GetReferenceColorSpace(), output);
ColorProcessorPtr ref_to_input = ColorProcessor::Create(color_manager_, color_manager_->GetReferenceColorSpace(), input);
ColorProcessorPtr ref_to_input = ColorProcessor::Create(
color_manager_, color_manager_->GetReferenceColorSpace(), input);
// FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to
// crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget)
// FIXME: For some reason, using OCIO::TRANSFORM_DIR_INVERSE (wrapped by ColorProcessor::kInverse) causes OCIO to
// crash. We've disabled that functionality for now (also disabling display_tab_ in ColorValuesWidget)
/*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(),
/*ColorProcessorPtr display_to_ref = ColorProcessor::Create(color_manager_->GetConfig(),
color_manager_->GetReferenceColorSpace(),
display,
view,
look,
ColorProcessor::kInverse);*/
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
color_values_widget_->SetColorProcessor(input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
color_wheel_->SetColorProcessor(input_to_ref_processor_, ref_to_display);
hsv_value_gradient_->SetColorProcessor(input_to_ref_processor_,
ref_to_display);
color_values_widget_->SetColorProcessor(
input_to_ref_processor_, ref_to_display, nullptr, ref_to_input);
}
}
+21 -20
View File
@@ -31,13 +31,13 @@
#include "widget/colorwheel/colorvalueswidget.h"
#include "widget/colorwheel/colorwheelwidget.h"
namespace olive {
class ColorDialog : public QDialog
namespace olive
{
Q_OBJECT
class ColorDialog : public QDialog {
Q_OBJECT
public:
/**
/**
* @brief ColorDialog Constructor
*
* @param color_manager
@@ -56,40 +56,41 @@ public:
*
* QWidget parent.
*/
ColorDialog(ColorManager* color_manager, const ManagedColor &start = Color(1.0f, 1.0f, 1.0f), QWidget* parent = nullptr);
ColorDialog(ColorManager *color_manager,
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
QWidget *parent = nullptr);
/**
/**
* @brief Retrieves the color selected by the user
*
* The color is always returned in the ColorManager's reference space (usually scene linear).
*/
ManagedColor GetSelectedColor() const;
ManagedColor GetSelectedColor() const;
QString GetColorSpaceInput() const;
QString GetColorSpaceInput() const;
ColorTransform GetColorSpaceOutput() const;
ColorTransform GetColorSpaceOutput() const;
public slots:
void SetColor(const ManagedColor &c);
void SetColor(const ManagedColor &c);
private:
ColorManager* color_manager_;
ColorManager *color_manager_;
ColorWheelWidget* color_wheel_;
ColorWheelWidget *color_wheel_;
ColorValuesWidget* color_values_widget_;
ColorValuesWidget *color_values_widget_;
ColorGradientWidget* hsv_value_gradient_;
ColorGradientWidget *hsv_value_gradient_;
ColorProcessorPtr input_to_ref_processor_;
ColorProcessorPtr input_to_ref_processor_;
ColorSpaceChooser* chooser_;
ColorSpaceChooser *chooser_;
ColorSwatchChooser *swatch_;
ColorSwatchChooser *swatch_;
private slots:
void ColorSpaceChanged(const QString& input, const ColorTransform &output);
void ColorSpaceChanged(const QString &input, const ColorTransform &output);
};
}
+39 -37
View File
@@ -26,66 +26,68 @@
#include "core.h"
namespace olive {
ConfigDialogBase::ConfigDialogBase(QWidget* parent) :
QDialog(parent)
namespace olive
{
QVBoxLayout* layout = new QVBoxLayout(this);
QSplitter* splitter = new QSplitter();
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
ConfigDialogBase::ConfigDialogBase(QWidget *parent)
: QDialog(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
list_widget_ = new QListWidget();
QSplitter *splitter = new QSplitter();
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
preference_pane_stack_ = new QStackedWidget(this);
list_widget_ = new QListWidget();
splitter->addWidget(list_widget_);
splitter->addWidget(preference_pane_stack_);
preference_pane_stack_ = new QStackedWidget(this);
QDialogButtonBox* button_box = new QDialogButtonBox(this);
button_box->setOrientation(Qt::Horizontal);
button_box->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
splitter->addWidget(list_widget_);
splitter->addWidget(preference_pane_stack_);
layout->addWidget(button_box);
QDialogButtonBox *button_box = new QDialogButtonBox(this);
button_box->setOrientation(Qt::Horizontal);
button_box->setStandardButtons(QDialogButtonBox::Cancel |
QDialogButtonBox::Ok);
connect(button_box, &QDialogButtonBox::accepted, this, &ConfigDialogBase::accept);
connect(button_box, &QDialogButtonBox::rejected, this, &ConfigDialogBase::reject);
layout->addWidget(button_box);
connect(list_widget_,
&QListWidget::currentRowChanged,
preference_pane_stack_,
&QStackedWidget::setCurrentIndex);
connect(button_box, &QDialogButtonBox::accepted, this,
&ConfigDialogBase::accept);
connect(button_box, &QDialogButtonBox::rejected, this,
&ConfigDialogBase::reject);
connect(list_widget_, &QListWidget::currentRowChanged,
preference_pane_stack_, &QStackedWidget::setCurrentIndex);
}
void ConfigDialogBase::accept()
{
foreach (ConfigDialogBaseTab* tab, tabs_) {
if (!tab->Validate()) {
return;
}
}
foreach (ConfigDialogBaseTab *tab, tabs_) {
if (!tab->Validate()) {
return;
}
}
MultiUndoCommand* command = new MultiUndoCommand();
MultiUndoCommand *command = new MultiUndoCommand();
foreach (ConfigDialogBaseTab* tab, tabs_) {
tab->Accept(command);
}
foreach (ConfigDialogBaseTab *tab, tabs_) {
tab->Accept(command);
}
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
AcceptEvent();
AcceptEvent();
QDialog::accept();
QDialog::accept();
}
void ConfigDialogBase::AddTab(ConfigDialogBaseTab *tab, const QString &title)
{
list_widget_->addItem(title);
preference_pane_stack_->addWidget(tab);
list_widget_->addItem(title);
preference_pane_stack_->addWidget(tab);
tabs_.append(tab);
tabs_.append(tab);
}
}
+14 -13
View File
@@ -27,32 +27,33 @@
#include "configdialogbasetab.h"
namespace olive {
class ConfigDialogBase : public QDialog
namespace olive
{
Q_OBJECT
class ConfigDialogBase : public QDialog {
Q_OBJECT
public:
ConfigDialogBase(QWidget* parent = nullptr);
ConfigDialogBase(QWidget *parent = nullptr);
private slots:
/**
/**
* @brief Override of accept to save preferences to Config.
*/
virtual void accept() override;
virtual void accept() override;
protected:
void AddTab(ConfigDialogBaseTab* tab, const QString& title);
void AddTab(ConfigDialogBaseTab *tab, const QString &title);
virtual void AcceptEvent(){}
virtual void AcceptEvent()
{
}
private:
QListWidget* list_widget_;
QListWidget *list_widget_;
QStackedWidget* preference_pane_stack_;
QList<ConfigDialogBaseTab*> tabs_;
QStackedWidget *preference_pane_stack_;
QList<ConfigDialogBaseTab *> tabs_;
};
}
@@ -20,11 +20,12 @@
#include "configdialogbasetab.h"
namespace olive {
namespace olive
{
bool ConfigDialogBaseTab::Validate()
{
return true;
return true;
}
}
+6 -6
View File
@@ -26,16 +26,16 @@
#include "config/config.h"
#include "undo/undocommand.h"
namespace olive {
class ConfigDialogBaseTab : public QWidget
namespace olive
{
class ConfigDialogBaseTab : public QWidget {
public:
ConfigDialogBaseTab() = default;
ConfigDialogBaseTab() = default;
virtual bool Validate();
virtual bool Validate();
virtual void Accept(MultiUndoCommand *parent) = 0;
virtual void Accept(MultiUndoCommand *parent) = 0;
};
}
+69 -54
View File
@@ -28,86 +28,101 @@
#include "config/config.h"
#include "core.h"
namespace olive {
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget* parent) :
QDialog(parent),
folder_(folder)
namespace olive
{
QGridLayout* layout = new QGridLayout(this);
int row = 0;
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
: QDialog(parent)
, folder_(folder)
{
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())), row, 0, 1, 2);
setWindowTitle(tr("Disk Cache Settings"));
int row = 0;
row++;
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->GetPath())),
row, 0, 1, 2);
setWindowTitle(tr("Disk Cache Settings"));
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
row++;
maximum_cache_slider_ = new FloatSlider();
maximum_cache_slider_->SetFormat(tr("%1 GB"));
maximum_cache_slider_->SetMinimum(1.0);
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) / static_cast<double>(kBytesInGigabyte));
layout->addWidget(maximum_cache_slider_, row, 1);
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
row++;
maximum_cache_slider_ = new FloatSlider();
maximum_cache_slider_->SetFormat(tr("%1 GB"));
maximum_cache_slider_->SetMinimum(1.0);
maximum_cache_slider_->SetValue(static_cast<double>(folder->GetLimit()) /
static_cast<double>(kBytesInGigabyte));
layout->addWidget(maximum_cache_slider_, row, 1);
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
connect(clear_cache_btn_, &QPushButton::clicked, this, static_cast<void(DiskCacheDialog::*)()>(&DiskCacheDialog::ClearDiskCache));
layout->addWidget(clear_cache_btn_, row, 1);
row++;
row++;
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
connect(clear_cache_btn_, &QPushButton::clicked, this,
static_cast<void (DiskCacheDialog::*)()>(
&DiskCacheDialog::ClearDiskCache));
layout->addWidget(clear_cache_btn_, row, 1);
clear_disk_cache_ = new QCheckBox(tr("Automatically clear disk cache on close"));
clear_disk_cache_->setChecked(folder->GetClearOnClose());
layout->addWidget(clear_disk_cache_, row, 1);
row++;
row++;
clear_disk_cache_ =
new QCheckBox(tr("Automatically clear disk cache on close"));
clear_disk_cache_->setChecked(folder->GetClearOnClose());
layout->addWidget(clear_disk_cache_, row, 1);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &DiskCacheDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &DiskCacheDialog::reject);
layout->addWidget(buttons, row, 0, 1, 2);
row++;
QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this,
&DiskCacheDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this,
&DiskCacheDialog::reject);
layout->addWidget(buttons, row, 0, 1, 2);
}
void DiskCacheDialog::accept()
{
qint64 new_disk_cache_limit = qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
if (new_disk_cache_limit != folder_->GetLimit()) {
folder_->SetLimit(new_disk_cache_limit);
}
qint64 new_disk_cache_limit =
qRound64(maximum_cache_slider_->GetValue() * kBytesInGigabyte);
if (new_disk_cache_limit != folder_->GetLimit()) {
folder_->SetLimit(new_disk_cache_limit);
}
if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) {
folder_->SetClearOnClose(clear_disk_cache_->isChecked());
}
if (folder_->GetClearOnClose() != clear_disk_cache_->isChecked()) {
folder_->SetClearOnClose(clear_disk_cache_->isChecked());
}
QDialog::accept();
QDialog::accept();
}
void DiskCacheDialog::ClearDiskCache()
{
ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_);
ClearDiskCache(folder_->GetPath(), this, clear_cache_btn_);
}
void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn)
void DiskCacheDialog::ClearDiskCache(const QString &path, QWidget *parent,
QPushButton *clear_btn)
{
if (QMessageBox::question(parent,
tr("Clear Disk Cache"),
tr("Are you sure you want to clear the disk cache in '%1'?").arg(path),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (clear_btn) clear_btn->setEnabled(false);
if (QMessageBox::question(
parent, tr("Clear Disk Cache"),
tr("Are you sure you want to clear the disk cache in '%1'?")
.arg(path),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (clear_btn)
clear_btn->setEnabled(false);
if (DiskManager::instance()->ClearDiskCache(path)) {
if (clear_btn) clear_btn->setText(tr("Disk Cache Cleared"));
} else {
QMessageBox::information(parent,
tr("Clear Disk Cache"),
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
QMessageBox::Ok);
if (clear_btn) clear_btn->setText(tr("Disk Cache Partially Cleared"));
}
}
if (DiskManager::instance()->ClearDiskCache(path)) {
if (clear_btn)
clear_btn->setText(tr("Disk Cache Cleared"));
} else {
QMessageBox::information(
parent, tr("Clear Disk Cache"),
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
QMessageBox::Ok);
if (clear_btn)
clear_btn->setText(tr("Disk Cache Partially Cleared"));
}
}
}
}
+14 -14
View File
@@ -28,31 +28,31 @@
#include "render/diskmanager.h"
#include "widget/slider/floatslider.h"
namespace olive {
class DiskCacheDialog : public QDialog
namespace olive
{
Q_OBJECT
public:
DiskCacheDialog(DiskCacheFolder* folder, QWidget* parent = nullptr);
static void ClearDiskCache(const QString &path, QWidget *parent, QPushButton *clear_btn = nullptr);
class DiskCacheDialog : public QDialog {
Q_OBJECT
public:
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
static void ClearDiskCache(const QString &path, QWidget *parent,
QPushButton *clear_btn = nullptr);
public slots:
virtual void accept() override;
virtual void accept() override;
private:
DiskCacheFolder* folder_;
DiskCacheFolder *folder_;
FloatSlider* maximum_cache_slider_;
FloatSlider *maximum_cache_slider_;
QCheckBox* clear_disk_cache_;
QCheckBox *clear_disk_cache_;
QPushButton* clear_cache_btn_;
QPushButton *clear_cache_btn_;
private slots:
void ClearDiskCache();
void ClearDiskCache();
};
}
+70 -64
View File
@@ -28,105 +28,111 @@
#include "common/qtutils.h"
#include "widget/slider/integerslider.h"
namespace olive {
namespace olive
{
AV1Section::AV1Section(QWidget *parent) :
AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
AV1Section::AV1Section(QWidget *parent)
: AV1Section(AV1CRFSection::kDefaultAV1CRF, parent)
{
}
AV1Section::AV1Section(int default_crf, QWidget *parent) :
CodecSection(parent)
AV1Section::AV1Section(int default_crf, QWidget *parent)
: CodecSection(parent)
{
QGridLayout* layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
int row = 0;
layout->addWidget(new QLabel(tr("Preset:")), row, 0);
int row = 0;
layout->addWidget(new QLabel(tr("Preset:")), row, 0);
preset_combobox_ = new QComboBox();
preset_combobox_->setToolTip(tr("This parameter governs the efficiency/encode-time trade-off.\n"
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
preset_combobox_ = new QComboBox();
preset_combobox_->setToolTip(tr(
"This parameter governs the efficiency/encode-time trade-off.\n"
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
for (int i = 0; i <= 13; i++)
preset_combobox_->addItem(QString::number(i));
for (int i = 0; i <= 13; i++)
preset_combobox_->addItem(QString::number(i));
preset_combobox_->setCurrentIndex(8);
preset_combobox_->setCurrentIndex(8);
layout->addWidget(preset_combobox_, row, 1);
layout->addWidget(preset_combobox_, row, 1);
row++;
row++;
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
QComboBox* compression_box = new QComboBox();
compression_box->setToolTip(tr("This parameter governs the quality/size trade-off.\n"
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
"Lower CRF values retain more detail at the cost of larger file sizes.\n"
"The possible range of CRF in SVT-AV1 is 1-63."));
QComboBox *compression_box = new QComboBox();
compression_box->setToolTip(tr(
"This parameter governs the quality/size trade-off.\n"
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
"Lower CRF values retain more detail at the cost of larger file sizes.\n"
"The possible range of CRF in SVT-AV1 is 1-63."));
// These items must correspond to the CompressionMethod enum
compression_box->addItem(tr("Constant Rate Factor"));
// These items must correspond to the CompressionMethod enum
compression_box->addItem(tr("Constant Rate Factor"));
layout->addWidget(compression_box, row, 1);
layout->addWidget(compression_box, row, 1);
row++;
row++;
compression_method_stack_ = new QStackedWidget();
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
compression_method_stack_ = new QStackedWidget();
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
crf_section_ = new AV1CRFSection(default_crf);
compression_method_stack_->addWidget(crf_section_);
crf_section_ = new AV1CRFSection(default_crf);
compression_method_stack_->addWidget(crf_section_);
connect(compression_box,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
compression_method_stack_,
&QStackedWidget::setCurrentIndex);
connect(
compression_box,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
compression_method_stack_, &QStackedWidget::setCurrentIndex);
}
void AV1Section::AddOpts(EncodingParams *params)
{
CompressionMethod method = static_cast<CompressionMethod>(
compression_method_stack_->currentIndex());
CompressionMethod method = static_cast<CompressionMethod>(compression_method_stack_->currentIndex());
if (method == kConstantRateFactor) {
// Set Quantizer value
params->set_video_option(QStringLiteral("qp"),
QString::number(crf_section_->GetValue()));
}
if (method == kConstantRateFactor) {
// Set Quantizer value
params->set_video_option(QStringLiteral("qp"), QString::number(crf_section_->GetValue()));
}
params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex()));
params->set_video_option(QStringLiteral("preset"),
QString::number(preset_combobox_->currentIndex()));
}
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent) :
QWidget(parent)
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
crf_slider_ = new QSlider(Qt::Horizontal);
crf_slider_->setMinimum(kMinimumCRF);
crf_slider_->setMaximum(kMaximumCRF);
crf_slider_->setValue(default_crf);
layout->addWidget(crf_slider_);
crf_slider_ = new QSlider(Qt::Horizontal);
crf_slider_->setMinimum(kMinimumCRF);
crf_slider_->setMaximum(kMaximumCRF);
crf_slider_->setValue(default_crf);
layout->addWidget(crf_slider_);
IntegerSlider* crf_input = new IntegerSlider();
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(crf_input->fontMetrics(), QStringLiteral("HHHH")));
crf_input->SetMinimum(kMinimumCRF);
crf_input->SetMaximum(kMaximumCRF);
crf_input->SetValue(default_crf);
crf_input->SetDefaultValue(default_crf);
layout->addWidget(crf_input);
IntegerSlider *crf_input = new IntegerSlider();
crf_input->setMaximumWidth(QtUtils::QFontMetricsWidth(
crf_input->fontMetrics(), QStringLiteral("HHHH")));
crf_input->SetMinimum(kMinimumCRF);
crf_input->SetMaximum(kMaximumCRF);
crf_input->SetValue(default_crf);
crf_input->SetDefaultValue(default_crf);
layout->addWidget(crf_input);
connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue);
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_, &QSlider::setValue);
connect(crf_slider_, &QSlider::valueChanged, crf_input,
&IntegerSlider::SetValue);
connect(crf_input, &IntegerSlider::ValueChanged, crf_slider_,
&QSlider::setValue);
}
int AV1CRFSection::GetValue() const
{
return crf_slider_->value();
return crf_slider_->value();
}
}
+21 -23
View File
@@ -28,45 +28,43 @@
#include "codecsection.h"
#include "widget/slider/floatslider.h"
namespace olive {
class AV1CRFSection : public QWidget
namespace olive
{
Q_OBJECT
class AV1CRFSection : public QWidget {
Q_OBJECT
public:
AV1CRFSection(int default_crf, QWidget* parent = nullptr);
AV1CRFSection(int default_crf, QWidget *parent = nullptr);
int GetValue() const;
int GetValue() const;
static const int kDefaultAV1CRF = 30;
static const int kDefaultAV1CRF = 30;
private:
static const int kMinimumCRF = 0;
static const int kMaximumCRF = 63;
QSlider* crf_slider_;
static const int kMinimumCRF = 0;
static const int kMaximumCRF = 63;
QSlider *crf_slider_;
};
class AV1Section : public CodecSection
{
Q_OBJECT
class AV1Section : public CodecSection {
Q_OBJECT
public:
enum CompressionMethod {
kConstantRateFactor,
};
enum CompressionMethod {
kConstantRateFactor,
};
AV1Section(QWidget* parent = nullptr);
AV1Section(int default_crf, QWidget* parent);
AV1Section(QWidget *parent = nullptr);
AV1Section(int default_crf, QWidget *parent);
virtual void AddOpts(EncodingParams* params) override;
virtual void AddOpts(EncodingParams *params) override;
private:
QStackedWidget* compression_method_stack_;
QStackedWidget *compression_method_stack_;
AV1CRFSection* crf_section_;
AV1CRFSection *crf_section_;
QComboBox *preset_combobox_;
QComboBox *preset_combobox_;
};
}
+32 -28
View File
@@ -23,22 +23,23 @@
#include <QGridLayout>
#include <QLabel>
namespace olive {
CineformSection::CineformSection(QWidget *parent) :
CodecSection(parent)
namespace olive
{
QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
CineformSection::CineformSection(QWidget *parent)
: CodecSection(parent)
{
QGridLayout *layout = new QGridLayout(this);
int row = 0;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
int row = 0;
quality_combobox_ = new QComboBox();
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
/* Correspond to the following indexes for FFmpeg
quality_combobox_ = new QComboBox();
/* Correspond to the following indexes for FFmpeg
*
* -quality <int> E..V....... set quality (from 0 to 12) (default film3+)
* film3+ 0 E..V.......
@@ -57,34 +58,37 @@ CineformSection::CineformSection(QWidget *parent) :
*
*/
quality_combobox_->addItem(tr("Film Scan 3+"));
quality_combobox_->addItem(tr("Film Scan 3"));
quality_combobox_->addItem(tr("Film Scan 2+"));
quality_combobox_->addItem(tr("Film Scan 2"));
quality_combobox_->addItem(tr("Film Scan 1.5"));
quality_combobox_->addItem(tr("Film Scan 1+"));
quality_combobox_->addItem(tr("Film Scan 1"));
quality_combobox_->addItem(tr("High+"));
quality_combobox_->addItem(tr("High"));
quality_combobox_->addItem(tr("Medium+"));
quality_combobox_->addItem(tr("Medium"));
quality_combobox_->addItem(tr("Low+"));
quality_combobox_->addItem(tr("Low"));
quality_combobox_->addItem(tr("Film Scan 3+"));
quality_combobox_->addItem(tr("Film Scan 3"));
quality_combobox_->addItem(tr("Film Scan 2+"));
quality_combobox_->addItem(tr("Film Scan 2"));
quality_combobox_->addItem(tr("Film Scan 1.5"));
quality_combobox_->addItem(tr("Film Scan 1+"));
quality_combobox_->addItem(tr("Film Scan 1"));
quality_combobox_->addItem(tr("High+"));
quality_combobox_->addItem(tr("High"));
quality_combobox_->addItem(tr("Medium+"));
quality_combobox_->addItem(tr("Medium"));
quality_combobox_->addItem(tr("Low+"));
quality_combobox_->addItem(tr("Low"));
// Default to "medium"
quality_combobox_->setCurrentIndex(10);
// Default to "medium"
quality_combobox_->setCurrentIndex(10);
layout->addWidget(quality_combobox_, row, 1);
layout->addWidget(quality_combobox_, row, 1);
}
void CineformSection::AddOpts(EncodingParams *params)
{
params->set_video_option(QStringLiteral("quality"), QString::number(quality_combobox_->currentIndex()));
params->set_video_option(
QStringLiteral("quality"),
QString::number(quality_combobox_->currentIndex()));
}
void CineformSection::SetOpts(const EncodingParams *p)
{
quality_combobox_->setCurrentIndex(p->video_option(QStringLiteral("quality")).toInt());
quality_combobox_->setCurrentIndex(
p->video_option(QStringLiteral("quality")).toInt());
}
}
+8 -9
View File
@@ -25,21 +25,20 @@
#include "codecsection.h"
namespace olive {
class CineformSection : public CodecSection
namespace olive
{
Q_OBJECT
class CineformSection : public CodecSection {
Q_OBJECT
public:
CineformSection(QWidget *parent = nullptr);
CineformSection(QWidget *parent = nullptr);
virtual void AddOpts(EncodingParams* params) override;
virtual void AddOpts(EncodingParams *params) override;
virtual void SetOpts(const EncodingParams *p) override;
virtual void SetOpts(const EncodingParams *p) override;
private:
QComboBox *quality_combobox_;
QComboBox *quality_combobox_;
};
}
+4 -3
View File
@@ -20,10 +20,11 @@
#include "codecsection.h"
namespace olive {
namespace olive
{
CodecSection::CodecSection(QWidget *parent) :
QWidget(parent)
CodecSection::CodecSection(QWidget *parent)
: QWidget(parent)
{
}
+13 -8
View File
@@ -25,18 +25,23 @@
#include "codec/encoder.h"
namespace olive {
class CodecSection : public QWidget
namespace olive
{
Q_OBJECT
class CodecSection : public QWidget {
Q_OBJECT
public:
CodecSection(QWidget* parent = nullptr);
CodecSection(QWidget *parent = nullptr);
virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)}
virtual void SetOpts(const EncodingParams *p){Q_UNUSED(p)}
virtual void AddOpts(EncodingParams *params)
{
Q_UNUSED(params)
}
virtual void SetOpts(const EncodingParams *p)
{
Q_UNUSED(p)
}
};
}

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